API Reference

The Ice9 API accepts image uploads and returns structured analysis from a coordinated ensemble of machine learning services. Submit an image, receive an image_id, poll until processing is complete, then retrieve the results.

Access: All endpoints require an API key. To request access, reach out via Discord or GitHub.

Overview

Ice9 routes each image through multiple specialized ML services in parallel — object detection, image captioning, segmentation, color analysis, and more. Because these services run asynchronously, the API follows a submit-then-poll pattern:

  1. SubmitPOST /analyze queues the image and returns an image_id immediately.
  2. PollGET /status/{image_id} returns live progress and results as services complete.
  3. RetrieveGET /results/{image_id} returns the complete payload once all services are done.

Results are available progressively — you can render partial output as each service finishes rather than waiting for everything.

Authentication

Every request must include your API key in the X-API-Key header. Keys are never embedded in query parameters or request bodies.

X-API-Key: ice9_your_key_here

A missing or invalid key returns 401 with the same message in both cases — no information is leaked about whether a key exists. Keys are rate-limited individually; see each endpoint for limits.

Quickstart

A complete end-to-end example using curl:

1. Submit an image

curl -X POST https://app.ice9.ai/analyze \
  -H "X-API-Key: ice9_your_key_here" \
  -F "file=@photo.jpg"
{
  "image_id":           4821,
  "trace_id":           "a3f7c2d1-...",
  "services_submitted": ["blip", "moondream", "yolo_v8", "..."],
  "image_width":        1920,
  "image_height":       1080
}

2. Poll for completion

curl https://app.ice9.ai/status/4821 \
  -H "X-API-Key: ice9_your_key_here"

Repeat until is_complete is true. Results populate progressively as services finish.

3. Get the final results

curl https://app.ice9.ai/results/4821 \
  -H "X-API-Key: ice9_your_key_here"

POST /analyze

POST /analyze 60 requests / minute

Submit an image for analysis. The image is validated, normalized, and dispatched to the configured set of ML services. Returns immediately with an image_id — processing happens asynchronously.

Request

Content-Type must be multipart/form-data.

FieldTypeDescription
file file required Image to analyze. Accepted formats: JPEG, PNG, WebP, HEIC/HEIF. Maximum size 16 MB. Images larger than 2048px on the longest dimension are resized proportionally. EXIF orientation is automatically corrected.
tier string optional Analysis tier controlling which services run and the flat per-image price. Defaults to free. See the tier table below. Unknown tier names return a 400 with the list of valid options.
services string optional Comma-separated list of services to run. For internal keys only — overrides tier entirely. Unknown service names return a 400 with the list of valid options.
image_group string optional A label applied to the image for grouping and filtering in the database. Defaults to "api".
Image handling: Image bytes are processed entirely in memory and are never written to disk. The original bytes are not stored — only metadata (filename, dimensions, perceptual hash) is persisted.

Tiers

TierServicesPrice
free colors, metadata, ocr, nudenet $0.00
basic colors, metadata, ocr, nudenet, yolo_v8, blip, moondream, ollama, qwen + background removal $0.05
premium colors, metadata, ocr, nudenet, yolo_v8, blip, moondream, ollama, qwen, haiku, gemini, gpt_nano + background removal $0.10
cloud colors, metadata, ocr, nudenet, yolo_v8, haiku, gemini, gpt_nano + background removal. Cloud-hosted VLMs only — no local models. $0.05

Response — 202 Accepted

FieldTypeDescription
image_idintegerUnique identifier for this submission. Use this to poll /status and retrieve /results.
trace_idstringUUID assigned to this submission for tracing across the processing pipeline.
tierstringEffective tier applied to this submission.
services_submittedarrayNames of the services the image was dispatched to.
image_widthintegerWidth of the normalized image in pixels.
image_heightintegerHeight of the normalized image in pixels.

GET /status/{image_id}

GET /status/{image_id} 300 requests / minute

Returns current processing state plus all results available so far. Call this repeatedly until is_complete is true. Results are included progressively — you can render useful output before all services have finished.

Path parameter

ParameterTypeDescription
image_idintegerThe image_id returned by /analyze.

Response — 200 OK

Includes all image metadata, per-service completion state, and the full results payload (see Response fields). Status-specific fields:

FieldTypeDescription
image_idintegerEcho of the requested ID.
image_filenamestringOriginal filename from the upload.
image_groupstringGroup label set at submission.
image_createdstringISO 8601 timestamp of when the image was registered.
services_submittedarrayServices the image was sent to.
vlm_servicesarraySubset of submitted services that are vision-language models.
services_completedobjectPer-service status, completion time, and processing duration.
services_pendingarrayServices not yet complete.
services_failedobjectServices that failed or were dead-lettered, keyed by service name with a reason string. Empty object if no failures.
progressstringCompletion fraction, e.g. "3/7".
is_completebooleantrue when all submitted services have finished. Stop polling here.
noun_consensus_completebooleantrue when noun consensus has been computed.
verb_consensus_completebooleantrue when verb consensus has been computed.
caption_summary_completebooleantrue when the caption summary has been generated.
sam3_completebooleantrue when SAM3 segmentation has finished.
content_analysis_completebooleantrue when content analysis has finished.

GET /results/{image_id}

GET /results/{image_id} 300 requests / minute

Returns the complete analysis payload for an image. Functionally equivalent to the final /status response but without the polling state fields. Call this after is_complete is true, or use it for already-processed images.

Path parameter

ParameterTypeDescription
image_idintegerThe image_id returned by /analyze.

Returns the same results payload described in Response fields below, plus image metadata fields.


Response fields

Both /status and /results include a results payload. Fields are null until the corresponding stage has completed — check the *_complete flags on /status to know what is ready.

service_results
object — keyed by service name
Raw output from each ML service, indexed by service name. Each entry includes data (service-specific payload), processing_time (seconds), and result_created (ISO 8601 timestamp). Only services with a "success" status are included.
noun_consensus
object | null
Detected objects and concepts aggregated across all captioning and detection services. nouns contains items with confidence above 0.5 or those explicitly promoted; nouns_all contains the full unfiltered list. category_tally summarizes detections by semantic category. service_count indicates how many services contributed.
verb_consensus
object | null
Actions and verbs extracted from captions and aggregated across services. verbs is a list of detected actions. svo_triples contains subject-verb-object relationships derived from captions. service_count indicates how many services contributed.
caption_summary
object | null
A single synthesized natural-language description of the image, generated by distilling captions from multiple vision-language models. summary_caption is the final text. model identifies which model produced the summary. services_present lists which VLMs contributed input captions.
sam3
object | null
Segmentation results from SAM3 (Segment Anything Model 3). Triggered automatically after noun consensus completes. results is a dictionary keyed by noun, each containing a list of instances with segmentation masks and bounding boxes. nouns_queried lists the nouns that were passed to the segmentation model. instance_count is the total number of segmented instances across all nouns.
content_analysis
object | null
Scene-level classification including scene_type, detected activities, people_count, and related fields. Produced by a dedicated content analysis stage that runs after primary services complete.
rembg
object | null
Background removal result. png_b64 is a base64-encoded RGBA PNG with the subject isolated on a transparent background. shape is [height, width]. Available on basic, premium, and cloud tiers. rembg_complete in /status indicates when it is ready. Included in the per-image tier price — no separate fee.
postprocessing
array
Per-region analysis results (colors, face attributes, pose estimation) applied to bounding box crops identified during detection. Each entry includes service, data, and source_bbox for mapping results back to image coordinates.

Polling pattern

Processing time varies based on image complexity and which services are running. A reasonable polling strategy is to start at 1–2 seconds and back off gradually.

# Python example
import time, requests

BASE = "https://app.ice9.ai"
HEADERS = {"X-API-Key": "ice9_your_key_here"}

# 1. Submit
resp = requests.post(f"{BASE}/analyze", headers=HEADERS,
                     files={"file": open("photo.jpg", "rb")})
image_id = resp.json()["image_id"]

# 2. Poll
interval = 1.5
while True:
    data = requests.get(f"{BASE}/status/{image_id}", headers=HEADERS).json()
    if data["is_complete"]:
        break
    time.sleep(interval)
    interval = min(interval * 1.3, 10)  # cap at 10s

# 3. Use results (or read them from the final status response above)
results = requests.get(f"{BASE}/results/{image_id}", headers=HEADERS).json()
The final /status response already contains the complete results payload — you only need to call /results separately if you want a clean response without the polling fields, or to retrieve results for a previously-processed image.

Error codes

All error responses include a JSON body with an "error" string field.

StatusMeaning
400Bad request — missing file, invalid image, unknown service name, or wrong content type.
401Missing or invalid API key. The same message is returned regardless of which case applies.
404The requested image_id does not exist.
413Payload too large. Maximum upload size is 16 MB.
429Rate limit exceeded. Slow down and retry after a short wait.
500Internal server error — database or processing failure.
502Upstream API unavailable.