Errors
problem+json and every status code.
Problem responses
Every JSON failure is an RFC 9457 application/problem+json document. The type URI points at the matching section of this page, so an agent can link a human straight to the fix. Log the request_id; never log credentials or delivery tokens.
{
"type": "https://carimage.dev/docs/errors#402",
"title": "Insufficient credits",
"status": 402,
"detail": "This request needs 1 credit; balance is 0. Add credits at https://carimage.dev/dashboard?ref=api#billing",
"request_id": "req_01j9x…",
"balance": 0,
"required_credits": 1,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=100&ref=agent-api",
"pricing": {
"credits_per_dollar": 100,
"packs": [
{
"id": "credits-100",
"credits": 100,
"price": 1,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=100&ref=agent-api"
},
{
"id": "credits-500",
"credits": 500,
"price": 5,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=500&ref=agent-api"
},
{
"id": "credits-1000",
"credits": 1000,
"price": 10,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=1000&ref=agent-api"
},
{
"id": "credits-2500",
"credits": 2500,
"price": 25,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=2500&ref=agent-api"
},
{
"id": "credits-10000",
"credits": 10000,
"price": 100,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=10000&ref=agent-api"
}
],
"plans": [
{
"id": "pro",
"price_monthly": 29,
"credits_per_month": 25000,
"distinct_vehicles_per_month": 2500,
"checkout_url": "https://carimage.dev/dashboard/checkout?plan=pro&interval=month&ref=agent-api"
},
{
"id": "business",
"price_monthly": 99,
"credits_per_month": 150000,
"distinct_vehicles_per_month": 15000,
"checkout_url": "https://carimage.dev/dashboard/checkout?plan=business&interval=month&ref=agent-api"
}
],
"pricing_url": "https://carimage.dev/api/v1/pricing"
}
}At a glance
| Status | Meaning | Retry? |
|---|---|---|
| 400 | Invalid parameters or JSON. | Fix, then retry |
| 401 | Bearer key missing, invalid or revoked. | Fix, then retry |
| 402 | The balance is empty, or the plan's monthly vehicle cap is reached. | Do not retry |
| 403 | Missing scope, an invalid/expired delivery token, a suspended account or a disallowed origin. | Fix, then retry |
| 404 | The make/model/year is not in the catalog. | Fix, then retry |
| 409 | A request with this Idempotency-Key is still running. | Retry later |
| 410 | A capped delivery URL was fully redeemed. | Mint a new URL |
| 413 | JSON body larger than 64 KiB. | Fix, then retry |
| 422 | The Idempotency-Key was already used with a different body. | Fix, then retry |
| 429 | Rate limited: this key, the whole account, or the account's daily render share. | Retry with backoff |
| 500 | Unexpected failure. | Retry later |
| 502 | The render failed. | Mint a new URL |
| 503 | A dependency is down or the service is overloaded. | Retry later |
400Bad Request
A parameter is missing, out of range or not one of the allowed values, or the JSON body does not parse. Image errors include parameter and retryable: false; detail explains the correction. Correct the request; do not retry it unchanged.
A query parameter the image endpoints do not read is one of these, and free: the problem names it in parameter and lists every name the endpoint reads in allowed, so a request is never served, and billed, as a different image than the one it asked for. Only tracking tags and cache busters (utm_source, gclid, ref, v, t…) are ignored instead, and named in X-Ignored-Parameters. The other names each parameter is read under (angle for view, paint for color, bg for background) are on Images.
{
"type": "https://carimage.dev/docs/errors#400",
"title": "Invalid image parameters",
"status": 400,
"detail": "Unknown parameter: wiggle. Nothing was charged. This endpoint reads make, model, year, vehicle, view, color, size, w, h, fit, background, framing, trim, padding, shadow, format and response (also under the spellings at /docs/images#spellings), and ignores only tracking tags and cache busters such as utm_source, gclid, v and t.",
"request_id": "req_01j9x…",
"code": "invalid_params",
"parameter": "wiggle",
"allowed": [
"make",
"model",
"year",
"vehicle",
"view",
"color",
"size",
"w",
"h",
"fit",
"background",
"framing",
"trim",
"padding",
"shadow",
"format",
"response"
],
"retryable": false
}401Unauthorized
Send the key as Authorization: Bearer cimg_…. Keys in the query string are never accepted. Authentication required means no key arrived: none, a blank one, or a placeholder such as an unexpanded ${CAR_IMAGE_API_KEY}. Invalid credential means the key is malformed, unknown, expired or revoked: create a new one in the dashboard or run car-image login. The detail says which, and how to fix it; on /api/mcp it is written for Claude Code, which shows it when a server will not connect.
402Payment Required
Two cases share this status; the code extension member tells them apart. Without one (title Insufficient credits), the balance cannot pay for the request: the problem carries balance, the credits left, and required_credits, what the request needed, as in the example at the top of this page. Its detail ends on the dashboard's billing section, which the response also sends as a Link: <https://carimage.dev/dashboard?ref=api#billing>; rel="payment" header. Nothing was charged. Cold renders and CDN hits cost the same 1 credit, so buy a pack in the dashboard, turn on auto-reload, or move to a plan with a larger allowance. A renewing signed URL that meets an empty balance answers the same problem with renewal_window, the window it could not pay for, and resumes on its next load once credits land.
With "code": "plan_vehicle_limit" (title Plan limit reached) the request names more new distinct vehicles than the plan allows this month. The problem carries "scope": "month", plan, vehicles_this_month, vehicles_per_month and requested; its detail and Link header name the dashboard's plan section (#plan) instead, because credits do not lift the cap. A vehicle the account already served this month is always allowed, so a live product is never cut off; the cap resets with the calendar month (UTC) and a higher plan raises it. Nothing was charged. An account on a written Enterprise agreement may be capped over the agreement's whole term instead: the same problem with "scope": "term", vehicles_this_term and vehicles_per_term, and the fix is adding vehicles to the agreement (email support@carimage.dev), not a plan.
{
"type": "https://carimage.dev/docs/errors#402",
"title": "Plan limit reached",
"status": 402,
"detail": "The Free plan covers 100 distinct vehicles a month and this account has used 100; this request names 3 it has not served yet. No credits were charged. A higher plan covers more vehicles: https://carimage.dev/dashboard?ref=api#plan",
"request_id": "req_01j9x…",
"code": "plan_vehicle_limit",
"scope": "month",
"plan": "free",
"vehicles_this_month": 100,
"vehicles_per_month": 100,
"requested": 3,
"checkout_url": "https://carimage.dev/dashboard/checkout?plan=pro&interval=month&ref=agent-api",
"next_plan": {
"id": "pro",
"name": "Pro",
"price": {
"monthly": 29,
"yearly": 290
},
"adds": [
"25,000 credits a month included",
"Commercial license that delivered renders keep after the plan ends",
"Up to 2,500 distinct vehicles a month"
],
"checkout_url": "https://carimage.dev/dashboard/checkout?plan=pro&interval=month&ref=agent-api"
},
"pricing": {
"credits_per_dollar": 100,
"packs": [
{
"id": "credits-100",
"credits": 100,
"price": 1,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=100&ref=agent-api"
},
{
"id": "credits-500",
"credits": 500,
"price": 5,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=500&ref=agent-api"
},
{
"id": "credits-1000",
"credits": 1000,
"price": 10,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=1000&ref=agent-api"
},
{
"id": "credits-2500",
"credits": 2500,
"price": 25,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=2500&ref=agent-api"
},
{
"id": "credits-10000",
"credits": 10000,
"price": 100,
"checkout_url": "https://carimage.dev/dashboard/checkout?credits=10000&ref=agent-api"
}
],
"plans": [
{
"id": "pro",
"price_monthly": 29,
"credits_per_month": 25000,
"distinct_vehicles_per_month": 2500,
"checkout_url": "https://carimage.dev/dashboard/checkout?plan=pro&interval=month&ref=agent-api"
},
{
"id": "business",
"price_monthly": 99,
"credits_per_month": 150000,
"distinct_vehicles_per_month": 15000,
"checkout_url": "https://carimage.dev/dashboard/checkout?plan=business&interval=month&ref=agent-api"
}
],
"pricing_url": "https://carimage.dev/api/v1/pricing"
}
}Agents: never retry a 402 automatically and never open or complete a purchase on your own. Surface the problem to the human with the dashboard link it carries. A key with the billing:write scope, which car-image login saves, can also prepare a hosted Checkout link for a pack or a plan through POST /api/v1/billing/checkout; a key created in the dashboard cannot.
403Forbidden
Either the key lacks the required scope (the body says which), or a signed URL's token is malformed, expired, or was minted by a key that has since been revoked. Create a key with the right scope, or mint a fresh URL.
Two further cases carry a code. With "account_suspended" the account is suspended and every key and connected app answers this until it is resolved; the detail says why, and support@carimage.dev is the way out. With "origin_not_allowed" the signed URL was minted by a key that lists allowed origins and the browser's Referer or Origin names another site; requests without either header (email clients, curl) pass. Add the site to the key's list in the dashboard, or mint from a key without one. See Restrict URLs to your sites.
404Not Found
The API only renders vehicles that exist in the open-source vehicle-db catalog for the requested year, read by the rules in how names are matched. A vehicle 404 carries suggestions: up to five real catalog vehicles closest to what you sent — the same vehicle in its nearest year, models of the make sharing a word, fuzzy matches, or the closest makes when the make is unknown — each with its id and image_path, and detail repeats them in prose. Retry with vehicle=<id>, discover valid values with GET /api/v1/vehicles, resolve free text with POST /api/v1/images/resolve, or add the vehicle upstream. An unknown vehicle id is a 404 without suggestions. A vehicle miss is not retryable unchanged: skip its other views and colors, follow the free catalog search, and use a confirmed vehicle id. Resolve each distinct vehicle before expanding a batch.
409Conflict
A retry of POST /api/v1/image-urls overtook the original request with the same Idempotency-Key. Nothing new was charged. Wait the integer seconds in Retry-After (2), then send the same key and body again to receive the original response, marked Idempotent-Replayed: true.
410Gone
The signed URL reached its max_uses. Mint a new one; creating URLs with max_uses: 0 (unlimited within the TTL) avoids this for public pages.
413Payload Too Large
Split the batch. POST /api/v1/image-urls accepts up to 50 images per call, which fits comfortably under the limit.
422Unprocessable Content
Within the last 24 hours the same Idempotency-Key was sent to POST /api/v1/image-urls with a different body. Nothing was charged. Use a new key for a new request, or resend the original body to replay the earlier response. Keys are 1–255 characters of letters, digits, ., _, : or -, scoped to your account and the route; the SDK generates one per call.
429Too Many Requests
Wait the integer number of seconds in Retry-After, then retry with exponential backoff and jitter. Three cases share this status; the code extension member tells them apart.
- No
code: this key's window (120 on Free, 120 on Pro, 600 on Business, 1,200 on Enterprise requests per minute).RateLimit-Remainingtells you how close you are before it happens. "account_rate_limited": the account's window across every key and connected app (240 on Free, 600 on Pro, 2,000 on Business, 5,000 on Enterprise requests per minute).Retry-Afterapplies the same way,plannames the plan, and a higher plan has a higher limit."account_generation_cap": the account has spent its plan's share of today's render budget on cold renders. The problem carriesretry_after_secondsandreset_at; the share resets at midnight UTC, cached images keep serving, and retrying beforereset_atcannot succeed. Nothing was charged.
500Internal Server Error
Something failed on our side. Nothing was charged for a failed image. Retry once after a short pause and report the request_id if it persists.
502Bad Gateway
The cold render did not produce a usable image. The credit is refunded automatically. For a keyed request, retry after a pause. For a signed URL, the failed delivery still consumed the URL — mint a new one before retrying.
Two cases share this status; the code extension member on the problem document tells them apart. With no code, the render itself failed and a retry after a pause is reasonable. With "code": "generation_at_capacity", new renders have reached a daily ceiling: the problem also carries retry_after_seconds and reset_at, images already rendered keep serving normally, and retrying before reset_at cannot succeed.
503Service Unavailable
The response includes Retry-After when a short retry is appropriate. Nothing was charged.
304 is free. A 402 is never retried by well-behaved agents. Every other status leaves the balance untouched.