Skip to main content
Every Zenrows error response tells you what to change: an HTTP status, a code, and a detail sentence in a JSON body. This page maps each common code to the change that gets you the page, then puts the rules into one function you can drop into your scraper. Uses: Fetch with Adaptive Stealth Mode (mode=auto), allowed_status_codes, the Concurrency-Limit header and the API error codes

Ask your AI agent

With the Zenrows MCP server connected to Claude, Cursor or any MCP client, paste this prompt. Your agent picks the right Zenrows tools and settings on its own.

Read the error body

Every error is an RFC 7807 JSON object. code names the cause and type links to its entry in the error code reference. This request combines js_render with mode=auto on purpose, to show an error body:
cURL
Log code with the URL on every failure, so you can group failures by cause and apply one fix per group.

Quick reference

422: get the page with a stronger configuration

RESP001 means that attempt returned no usable content. Let Zenrows choose the configuration:
  • Send mode=auto. Adaptive Stealth Mode adds JavaScript rendering and premium proxies only when a page needs them, and bills only the configuration that succeeds.
  • For full control, send js_render=true and premium_proxy=true, add wait_for with a selector of the content you need, and proxy_country when the site serves one region.
  • Retry with backoff. A 422 is retryable, and requests that don’t succeed aren’t charged.
For Extract requests, no_data_extracted (also 422) means the page had no values for Extract’s fields. Make sure it rendered fully (js_render=true with wait_for, instead of mode=auto), or omit extract_fields. See no_data_extracted.

499: give the request time to finish

CTX0001 (HTTP 499) means your client closed the connection before Zenrows returned the page, usually because the client’s timeout fired. Set the timeout to 180 seconds or more on every Fetch call, for example requests.get(..., timeout=180) in Python or AbortSignal.timeout(180_000) in Node.js. Requests that end in 499 aren’t charged.

429: match your plan’s concurrency

AUTH006 means more requests were in flight than your plan runs at once. Every response carries Concurrency-Limit and Concurrency-Remaining headers. Size your worker pool to the limit:
Python (Requests)
Keep client timeouts at 180 seconds here too: a request your client abandons keeps its slot until Zenrows finishes it. For thousands of URLs, a Batch job manages concurrency for you.

402: check credits and caps

AUTH004 means the plan’s credits for this billing period are used. AUTH014 means this API key reached a credit cap you set, while your other keys keep working. Check where you stand, free and without using a concurrency slot:
cURL
The response includes usage_credits, credit_limit, usage_percent, period_ends_at and, under api_key.caps, any caps on this key. To continue right away, top up on the Billing page, upgrade on the Plans page, or raise the key’s cap on the API keys page. See Track usage and control spend.

413: ask for the part of the page you need

RESP005 means the page is larger than one response carries. Request less of it: If you use json_response=true, its captured network calls count toward the size too: remove it when you don’t need them. More strategies: Response too large.

400 and 401: correct the request

These return the same answer until the request changes, so fix and resend instead of retrying.
  • REQS004: a parameter value or the URL is invalid. The detail names it. Two common ones: js_render or premium_proxy sent with mode=auto (keep one approach), and a target URL that isn’t URL-encoded.
  • REQS002: the domain needs JavaScript rendering or premium proxies. Send mode=auto and Zenrows applies them.
  • RESP004: css_extractor isn’t valid JSON or has a malformed selector. Validate it with json.dumps on a dict.
  • AUTH001, AUTH002, AUTH003: the key is missing or not recognized. Copy it again from the API keys page and send it as apikey.

404 and 410: handle pages the target removed

RESP002 means the target site itself answered 404: the page is gone, so drop or fix the URL. To receive the target’s own error page as content instead of an error, add allowed_status_codes=404,410. The response is then 200 and the target’s status is in the Zr-Status-Code header. Like every 404 and 410, these responses are billed as successful requests (see Pricing):
cURL
RESP007 (also 404) means the domain doesn’t resolve: check the hostname for typos.

Handle every code in one function

This function sends mode=auto, retries the retryable statuses with backoff, treats pages the target removed as “gone”, and raises with the code, detail and docs link for anything that needs a change:
Python (Requests)

Example output

The last call adds js_render on top of mode=auto on purpose, to show how a request that needs a fix surfaces.

Customize it

Retry failed requests

Backoff, jitter and SDK retries in Python and Node.js.

Track usage and control spend

Read per-request cost and set credit caps.

Scrape pages protected by anti-bot systems

Let Adaptive Stealth Mode pick the right configuration per page.

Run many requests concurrently

Use your plan’s concurrency to its full extent.