Errors
Every API error code, when it happens, and how to fix it.
All errors use the same JSON envelope:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Human-readable summary.",
"request_id": "req_01ARZ3NDEKTSV4RRFFQ69G5FAV"
}
}Every response — success or error — includes X-Request-Id matching error.request_id. Quote this ID when contacting support.
Some codes include extra fields (shown below). Validation errors add field and issue.
Error code reference
| Status | Code | When it happens | How to fix |
|---|---|---|---|
| 400 | invalid_input | Request body failed validation (out-of-bounds viewport, bad format, malformed URL shape, etc.) | Read error.field and error.issue in the response. Fix the named field and retry. |
| 401 | missing_api_key | No Authorization header | Add Authorization: Bearer shot_…. See Authentication. |
| 401 | invalid_api_key | Key hash not found in the database | Check for typos. Create a new key if needed. |
| 401 | revoked_api_key | Key was revoked from the dashboard | Create a new API Key and update your integration. Revocation propagates within ~60 seconds. |
| 403 | url_not_allowed | SSRF defense rejected the URL (private/loopback/link-local address, blocked scheme, or post-DNS resolution to a blocked range) | Use a public http or https URL that resolves to a routable address. Do not target internal networks. |
| 422 | idempotency_key_reused | Same Idempotency-Key header was previously used with a different request body | Reuse the key only when retrying the identical body, or generate a new key for a changed request. See Caching & idempotency. |
| 422 | page_too_tall | full_page: true and the page exceeds 32,768 px tall | Disable full-page capture, clip to a viewport, or target a shorter page. Response may include actual_height. |
| 422 | render_failed | Target returned 5xx, JavaScript crash, or navigation failure | Verify the URL loads in a normal browser. Retry later if the site is down. Response may include reason. |
| 422 | selector_not_found | wait_for CSS selector never appeared within the timeout | Fix the selector or increase delay. Response includes selector. |
| 429 | rate_limit_exceeded | Per-minute request cap for your Plan | Slow down or wait. Response includes retry_after, limit, and window ("1m"). Obey the Retry-After header. |
| 429 | concurrency_limit_exceeded | Too many in-flight Renders for this API Key | Wait for an existing Render to finish before starting another. Response includes limit. |
| 429 | quota_exceeded | Monthly Billable Render Quota exhausted | Upgrade your Plan or wait until the calendar month resets (UTC). Response includes quota_used, quota_limit, resets_at, and upgrade_url. |
| 500 | internal_error | Unhandled server error or deploy-time misconfiguration | Retry with backoff. Quote request_id when escalating — the response intentionally omits internal details. |
| 503 | render_timeout | Render exceeded the 30-second wall-time budget | Simplify the page, reduce wait_for / delay, or retry. Response includes timeout_ms (30000). |
| 503 | browser_unavailable | Browserless (managed Playwright) is not reachable | Retry later. If persistent, check service status on the operator side. |
Example: validation error
{
"error": {
"code": "invalid_input",
"message": "viewport.width must be between 320 and 3840",
"request_id": "req_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"field": "viewport.width",
"issue": "must be between 320 and 3840"
}
}Example: rate limit
{
"error": {
"code": "rate_limit_exceeded",
"message": "5 requests per minute. Retry in 27s.",
"request_id": "req_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"retry_after": 27,
"limit": 5,
"window": "1m"
}
}Failed Renders (4xx/5xx) do not consume monthly Quota. See Rate limits & quotas.