Molmo2-4B Image Captioner: Generate detailed, accurate captions for images with customizable detail levels (low, medium, high). Open-source vision-language model with object grounding capabilities. Ready-to-use REST API, no cold starts, affordable pricing.
Integration Steps
Asynchronous Polling1.8s for up to 180s.succeeded / failed / cancelled.output_payload.assets[] URLs.Step 1 · Create Request
Submit generation input. Choose a backend language example below.
curl -X POST https://api.openoctopus.com/v1/images/recognitions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer ooq_your_api_key" \\
-d '{
"model": "openoctopus/image-captioner-molmo2",
"prompt": "a premium octopus mascot, orange and black, clean background"
}'Poll Task Status
Use the task ID from create response to poll execution status.
curl https://api.openoctopus.com/v1/tasks/task_id_from_previous_response \
-H "Authorization: Bearer ooq_your_api_key"const timeoutMs = 180000;
const intervalMs = 1800;
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
const r = await fetch("https://api.openoctopus.com/v1/tasks/task_id_from_previous_response", {
headers: { Authorization: "Bearer " + process.env.OPENOCTOPUS_API_KEY },
});
const task = await r.json();
if (!r.ok) throw new Error(task?.error?.message ?? "task query failed");
if (task.status === "queued" || task.status === "processing") {
await new Promise((resolve) => setTimeout(resolve, intervalMs));
continue;
}
if (task.status === "succeeded") {
console.log(task.output_payload);
break;
}
throw new Error(task?.error_message ?? "task failed");
}import time, requests
timeout_s = 180
interval_s = 1.8
started = time.time()
while time.time() - started < timeout_s:
resp = requests.get(
"https://api.openoctopus.com/v1/tasks/task_id_from_previous_response",
headers={"Authorization": f"Bearer {OPENOCTOPUS_API_KEY}"},
timeout=30,
)
task = resp.json()
if resp.status_code >= 400:
raise RuntimeError(task.get("error", {}).get("message", "task query failed"))
status = task.get("status")
if status in ("queued", "processing"):
time.sleep(interval_s)
continue
if status == "succeeded":
print(task.get("output_payload"))
break
raise RuntimeError(task.get("error_message", "task failed"))Request Example (From Internal)
{
"model": "openoctopus/your-model",
"input": {
"image": "example",
"face_image": "example",
"target_index": "0",
"output_format": "jpeg",
"enable_base64_output": false,
"enable_sync_mode": "false"
}
}Submit Response Example
{
"id": "00000000-0000-0000-0000-000000000000",
"status": "queued",
"model": "openoctopus/your-model"
}Input Schema (Standard + Provider Extension)
Standard request contract merged with this model's upstream input parameters.
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| image | string (uri) | Yes | Input image URL for captioning. Supports common image formats (JPEG, PNG, WebP). | - |
| detail_level | string | No | Level of detail in the generated caption. Low: brief summary. Medium: balanced description. High: comprehensive, detailed analysis. | medium |
| enable_sync_mode | boolean | No | If set to true, the function will wait for the result to be generated and uploaded before returning the response. It allows you to get the result directly in the response. This property is only available through the API. | false |
{
"standard": {
"model": "public model slug (required)",
"prompt": "user prompt (required)",
"input": "provider-specific options (optional object)"
},
"providerExtension": {
"params": [
{
"name": "image",
"type": "string (uri)",
"required": true,
"description": "Input image URL for captioning. Supports common image formats (JPEG, PNG, WebP).",
"exposedToCustomer": true
},
{
"enum": [
"low",
"medium",
"high"
],
"name": "detail_level",
"type": "string",
"example": "medium",
"required": false,
"description": "Level of detail in the generated caption. Low: brief summary. Medium: balanced description. High: comprehensive, detailed analysis.",
"exposedToCustomer": true
},
{
"name": "enable_sync_mode",
"type": "boolean",
"example": "false",
"required": false,
"description": "If set to true, the function will wait for the result to be generated and uploaded before returning the response. It allows you to get the result directly in the response. This property is only available through the API.",
"exposedToCustomer": false
}
]
}
}Output Schema (Standard + Provider Extension)
Standard output contract merged with provider extension fields.
Note: integrate against output_payload.assets[]. raw is optional and may be omitted by endpoint policy.
| Field | Type | Exposed | Description | Example |
|---|---|---|---|---|
| created_at | string (date-time) | Yes | ISO timestamp of when the request was created (e.g., '2023-04-01T12:34:56.789Z'). | - |
| has_nsfw_contents | array of boolean | Yes | Array of boolean values indicating NSFW detection for each output. | - |
| id | string | Yes | Unique identifier for the prediction, the ID of the prediction to get. | - |
| model | string | Yes | Model ID used for the prediction. | - |
| outputs | array of string | Yes | Array of URLs to the generated content (empty when status is not completed). | - |
| status | string | Yes | Status of the task: created, processing, completed, or failed. | - |
| urls | object | Yes | Object containing related API endpoints. | [object Object] |
{
"standard": {
"id": "task id",
"status": "queued | processing | succeeded | failed | cancelled",
"queue": {
"enabled": "whether OpenOctopus local queue is enabled for this model",
"position": "1-based queue position while queued, 0 while processing, null when unavailable",
"size": "current queued request count for this model",
"concurrency": "local concurrent upstream submissions allowed for this model",
"upstreamQueueSupported": "whether the upstream provider has a reliable queue",
"upstreamCancelSupported": "whether the upstream provider supports cancellation"
},
"asset_storage": {
"provider": "supabase | aliyun-oss | tencent-cos",
"inputBucket": "bucket used for uploaded reference media",
"outputBucket": "bucket used for generated media",
"custom": "true when this model overrides default system asset storage"
},
"capability": "image_generation | video_generation",
"output_payload": {
"format": "openoctopus.image.output.v1 | openoctopus.video.output.v1",
"assets": "normalized output assets",
"raw": "optional sanitized debug payload (may be omitted depending on endpoint policy)"
}
},
"providerExtension": {
"fields": [
{
"name": "created_at",
"type": "string (date-time)",
"description": "ISO timestamp of when the request was created (e.g., '2023-04-01T12:34:56.789Z').",
"exposedToCustomer": true
},
{
"name": "has_nsfw_contents",
"type": "array of boolean",
"description": "Array of boolean values indicating NSFW detection for each output.",
"exposedToCustomer": true
},
{
"name": "id",
"type": "string",
"description": "Unique identifier for the prediction, the ID of the prediction to get.",
"exposedToCustomer": true
},
{
"name": "model",
"type": "string",
"description": "Model ID used for the prediction.",
"exposedToCustomer": true
},
{
"name": "outputs",
"type": "array of string",
"description": "Array of URLs to the generated content (empty when status is not completed).",
"exposedToCustomer": true
},
{
"name": "status",
"type": "string",
"description": "Status of the task: created, processing, completed, or failed.",
"exposedToCustomer": true
},
{
"name": "urls",
"type": "object",
"example": "[object Object]",
"description": "Object containing related API endpoints.",
"exposedToCustomer": true
}
]
}
}Final Output Example (Normalized)
{
"capability": "image_generation",
"status": "succeeded",
"output_payload": {
"format": "openoctopus.image.output.v1",
"assets": [
{
"type": "image",
"url": "https://example.com/result.png",
"mimeType": "image/png"
}
],
"raw": {
"model": "example",
"outputs": "example",
"status": "example",
"urls": "example",
"created_at": "example",
"has_nsfw_contents": "example",
"id": "example"
}
}
}Step 4 · Error Handling Guide
Error codes below are loaded from internal gateway error definitions and should be treated as source of truth.
| Code | HTTP | Retryable | Category | Message |
|---|---|---|---|---|
| invalid_request | 400 | No | validation | The request payload is invalid. Check the required fields and try again. |
| unauthorized | 401 | No | auth | Authentication is required for this request. |
| invalid_api_key | 401 | No | auth | The API key is invalid or inactive. |
| insufficient_balance | 402 | No | billing | Your wallet balance is insufficient. Please top up and try again. |
| model_not_available | 404 | No | routing | The requested model is currently unavailable. |
| task_not_found | 404 | No | task | The requested task could not be found. |
| file_not_found | 404 | No | asset | The requested generated file is not available. |
| provider_offline | 503 | Yes | upstream | The selected model is temporarily unavailable. Please retry later. |
| provider_model_inactive | 503 | Yes | routing | The selected model is temporarily unavailable. Please retry later. |
| provider_credential_missing | 503 | Yes | system | The service is temporarily unavailable for this model. Please retry later. |
| provider_credential_incomplete | 503 | Yes | system | The service is temporarily unavailable for this model. Please retry later. |
| provider_credential_unusable | 503 | Yes | system | The service is temporarily unavailable for this model. Please retry later. |
| provider_credential_legacy | 503 | Yes | system | The service is temporarily unavailable for this model. Please retry later. |
| provider_credential_unavailable | 503 | Yes | system | The service is temporarily unavailable for this model. Please retry later. |
| provider_credential_decrypt_failed | 503 | Yes | system | The service is temporarily unavailable for this model. Please retry later. |
| model_billing_not_configured | 503 | No | system | The selected model is temporarily unavailable. Please retry later. |
| provider_pricing_not_configured | 503 | No | system | The selected model is temporarily unavailable. Please retry later. |
| database_operation_failed | 503 | Yes | system | The service could not access required internal records. Please retry later. |
| billing_resolution_failed | 503 | Yes | system | The selected model pricing could not be evaluated. Please retry later. |
| request_record_write_failed | 503 | Yes | system | The request could not be recorded internally. Please retry later. |
| api_key_touch_failed | 503 | Yes | system | The request was accepted but internal key tracking failed. Please retry later. |
| queue_unavailable | 503 | Yes | system | The internal job queue is temporarily unavailable. Please retry later. |
| provider_submit_failed | 502 | Yes | upstream | The generation provider could not accept the request. Please retry shortly. |
| provider_poll_failed | 502 | Yes | upstream | The generation provider could not complete the request. Please retry shortly. |
| upstream_failed | 502 | Yes | upstream | The generation provider failed to complete the request. Please retry shortly. |
| content_policy_violation | 400 | No | safety | The prompt or image was rejected by the provider safety policy. Please adjust the content and try again. |
| upstream_timeout | 504 | Yes | upstream | The generation request timed out. Please retry shortly. |
| upstream_result_missing | 502 | Yes | upstream | The generation provider returned an incomplete result. Please retry shortly. |
| video_output_missing | 502 | Yes | upstream | The generation provider returned an incomplete result. Please retry shortly. |
| service_unavailable | 503 | Yes | system | The service is temporarily unavailable. Please retry later. |
| internal_error | 500 | Yes | system | The service encountered an unexpected error. Please retry later. |
task.status=failed, always read error.code and match the table above; retry only when retryable=true.