Job polling
Any call made with ?async=true — to
/v1/optimize,
/v1/optimize/multi,
or /v1/smartcrop — returns a job_id. Poll it
here until status stops being "processing".
GET /v1/jobs/{job_id}
curl https://api.helloimg.io/v1/jobs/job_5f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c \
-H "Authorization: Bearer <your API key>"
Unauthorized (401) — like every other /v1/* route, a missing or
malformed Authorization header answers:
{ "error": "invalid api key" }
Single-format job
Processing:
{
"job_id": "job_5f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c",
"status": "processing"
}
Completed:
{
"job_id": "job_5f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c",
"status": "completed",
"progress": 100,
"result": {
"image_url": "https://cdn.helloimg.io/helloimg-optimized/opt_job_5f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c.webp",
"original_size": 855000,
"new_size": 111000,
"percent": 87.03,
"processing_time_ms": 26
}
}
Failed:
{
"job_id": "job_5f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c",
"status": "failed",
"error": "..."
}
Not found (404) — the job ID doesn't exist, or it's more than an hour
old:
{
"error": "Job not found",
"message": "Job may have expired (TTL: 1 hour) or invalid job_id"
}
Multi-format job
A job ID from /v1/optimize/multi
aggregates every requested format into one response. status is
"processing" while any format is still running, "completed" once at
least one format finished and none are pending, or "failed" if every
format failed:
{
"job_id": "multi_5f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c",
"status": "completed",
"progress": 100,
"results": [
{
"format": "jpeg",
"status": "completed",
"result": {
"image_url": "https://cdn.helloimg.io/helloimg-optimized/opt_multi_..._jpeg.jpg",
"original_size": 855000,
"new_size": 214000,
"percent": 74.97
},
"error": null
},
{
"format": "webp",
"status": "completed",
"result": {
"image_url": "https://cdn.helloimg.io/helloimg-optimized/opt_multi_..._webp.webp",
"original_size": 855000,
"new_size": 111000,
"percent": 87.03
},
"error": null
}
]
}
progress is the percentage of formats completed while any are still
processing, and 100 once the job is terminal — even if some formats
failed, so a client watching progress isn't left at a frozen value.
Notes
- Job results expire after 1 hour — fetch and store the
image_urlbefore then. - A completed result can also come back immediately, with
200instead of202, straight from the original optimize/smartcrop call — that happens when the exact same image and options were already optimized recently. There's nothing to poll in that case.