API REFERENCE
Errors
Failed requests return a JSON error body instead of an image. Here's every code.
Error shape
On any error the response is JSON (not an image) with a stable machine-readable error string and, for screenshot-specific errors, a human-readable message:
{
"error": "bad_request",
"message": "Provide exactly one of \"url\" or \"html\", not both."
}Branch on the error field and the HTTP status — the message wording may change, but the codes below are stable. A quick way to tell success from failure: a 2xx response carries content-type: image/png (or image/jpeg); any error carries content-type: application/json. A credit is only spent on success — every status ≥ 400 is refunded, so failures are never billed.
Status codes
| Status | Error | When it happens |
|---|---|---|
| 400 | bad_request | Body is not JSON; neither or both of url/html were sent; an option failed validation (e.g. quality without format:"jpeg", viewport out of range); or the url is malformed or resolves to a private/loopback/link-local/metadata address (SSRF-blocked before navigating). |
| 400 | selector_not_found | A "selector" was given but no element on the page matched it. |
| 401 | invalid_api_key | Missing or invalid API key — check the Authorization: Bearer header. |
| 402 | payment_required | Out of credits for the month. Upgrade your plan to continue. |
| 403 | insufficient_scope | The key is valid but lacks the screenshot:write scope. |
| 429 | rate_limited | Too many requests — slow down and retry. The limit is per account. |
| 501 | engine_unavailable | The screenshot engine (Cloudflare Browser Rendering) is not available — local dev with the dev fallback turned off. Does not occur in production. |
| 503 | engine_busy | The engine is briefly at capacity / rate-limited after an internal retry. Transient and retryable — honour the Retry-After header (seconds) and back off. |
| 504 | render_timeout | The capture exceeded the 20s render budget. Try a simpler page, a lighter waitUntil, or remove slow external assets. |
Handling errors
- 400 — fix the request: send exactly one of
url/html, keep options in range, dropqualityunlessformatis"jpeg", and only capture public URLs. See the endpoint reference. - 401 — check the key and the
Authorization: Bearer …header. See Authentication. - 402 — you're out of credits; upgrade in Billing.
- 403 — the key lacks the
screenshot:writescope; mint a key with it. - 429 — back off and retry with exponential backoff.
- 503 — transient: wait the
Retry-Afterseconds and retry; the capture wasn't billed. - 504 — the capture hit the 20s budget; simplify the page, use a lighter
waitUntil, or drop slow assets, then retry.