MegalaniaDocs
Docs menu
  1. Docs
  2. Reference
  3. Error codes
Reference

Error codes

llms.txtUpdated 8 Nov 2026

Every failure is JSON with an error.code. The code is the contract, the message tells you what to change. A 400 also names the parameter in error.field, and so does a 422. You are never charged for a 4xx or a 5xx.

Every response, good or bad, carries X-Request-Id. Quote it if you write to support. Looking for a fix by symptom instead? See Troubleshooting.

The body

JSON
{
  "error": {
    "code": "bad_parameter",
    "field": "timeout",
    "message": "timeout must be a whole number of seconds, 1 to 90. The default is 60. Seconds, not milliseconds.",
    "report_url": "https://app.megalania.io/report#rid=0199f0a2-7c1e-7000-8000-000000000001"
  }
}

field is present on a 400, on a 422 convert_cap or unsupported_content, and on a 503 unavailable when one parameter is the cause. code, message and report_url are always there.

report_url opens the dashboard’s “Report a problem” page with this request id filled in. The id is the only thing in it, after the #. Open it, add a short note, and send.

The table

none and refunded both cost 0 credits. refunded means we held the credits while the request ran and gave them back.

HTTP error.code credits what to change
400 bad_parameter none Read error.field. That parameter is missing, misspelled, or has a value we do not accept. The message lists the values that work. On checkout: pack is missing, or campaign is not 1 to 64 of a-z, 0-9 and -. network in response_types is this 400 naming response_types; send network=true. network other than true or false, network=true with engine=http or with schema is this 400 naming network. network_url_globs without network=true, with an empty entry, more than 10 patterns or over 1,024 bytes is this 400 naming network_url_globs. screenshot other than viewport or full, or screenshot with engine=http, or screenshot with schema, is this 400 naming screenshot. engine=browser (or a wait_until other than load, which asks for the browser) is this 400 with a mobile proxy_tier, a POST, a session_id or an identity; the message names the one to drop. A bad Idempotency-Key, or the header sent more than once, is this 400 naming Idempotency-Key: send it once, 1 to 128 printable ASCII characters.
400 bad_request none Checkout and account writes. The body is not one JSON object. Send one JSON object, like {"pack": "starter"}.
400 bad_signature none Stripe’s webhook only. The Stripe-Signature header is missing or does not match the body. Not for you to call.
400 max_credits_month_too_low none Monitors only, on create, on an edit and on run now. The monitor’s max_credits_month is below what its checks can cost in a month (the quote’s high), or this month’s spend plus one more check would pass it, so its checks would be skipped. The message has both numbers. Raise max_credits_month to at least the number it names, or watch fewer pages or less often. You are not charged.
400 max_credits_too_low none, or the page only Every route that can serve this page costs more than your max_credits. The message names the lowest price on that site. Raise max_credits to at least that, or remove it. This one comes before any attempt, and you are not charged. With schema, it can also come after the page: your max_credits covered the page but not the most the extraction can cost. Then error.field is max_credits, the page is charged, the extraction is free (llm_credits is 0, credits and X-Credits-Charged are the page), and the body carries the page as markdown.
401 bad_session none Dashboard /api/* only. There is no session cookie, or the session is over: you logged out, sent nothing for 12 hours, signed in more than 7 days ago, the account was suspended, or you left the account. Sign in again with Google or GitHub. A key in Authorization or api_key is ignored on /api/*.
401 bad_token none Send the key in the Authorization header: Authorization: Bearer <your key>. The message says which of four it is: no key was sent; the key came in an X-Api-Key header; a key is in the URL (api_key in the query, refused on every route, even next to the header); or the key is wrong, expired or deleted. A key with an unknown prefix, a key of the wrong length and a deleted key get the same message on purpose. Never charged.
402 insufficient_credits none Add credits, or make the request cheaper with proxy_tier=datacenter. With schema, it can also come after the page: your balance covered the page but not the extraction. Then error.field is schema, the page is charged, the extraction is free (llm_credits is 0, credits and X-Credits-Charged are the page), and the body carries the page as markdown.
402 over_plan none You reached a limit of your plan: keys or members (GET /v1/account shows the limits under plan), or 5 referral codes. Revoke a key with DELETE /v1/account/keys/{key_id}, or remove a member with DELETE /v1/account/members/{person_id}, then try again.
402 payment_failed none POST /v1/account/subscription only. The payment for a higher plan failed, so your plan did not change and nothing was charged. Update your card in the billing portal (POST /v1/account/billing-portal) and try again.
402 spend_cap none Your account’s monthly spend cap is reached: this request would take this month’s credits past it. Nothing was charged. An owner raises or removes the cap with PATCH /v1/account (spend_cap), or on the dashboard’s Plan and billing page. It resets at 00:00 UTC on the 1st. Batches and monitors stop the same way: a batch item ends failed with this error, and a monitor check is skipped for free.
403 bad_origin none Dashboard /api/* only. A request other than GET must come from the dashboard page itself: Origin must be the dashboard’s address and Sec-Fetch-Site must be same-origin. A browser sets both. Call /v1 with a key from anywhere else.
403 forbidden_target none The url is on a domain we do not fetch, or a subdomain of one. The acceptable use policy says what we refuse. /v1 and batch submit answer this; a batch item on such a domain fails for free.
403 monitor_checks_exceeded none Monitors only, on create, on an edit that adds checks to the monitor, on resuming a monitor paused by you or after failed checks (money pauses already count), and on run now when this month’s page checks plus the run’s pages would pass your plan’s monthly checks. Your monitors would run more page checks in 30 days than your plan allows (pages × checks). Paused monitors do not count, except those paused for money, which start again by themselves. The message says how many this monitor adds and how many you use. Check less often, watch fewer pages, or pause another monitor.
403 monitor_limit none Monitors only. Your plan has no room for another monitor; paused monitors count too. The message names your plan and its limit. Delete a monitor, or upgrade your plan.
403 not_eligible none Referral program only. An account joins after at least one paid purchase that was not refunded or disputed. Buy a pack first.
403 notice_required none Making a key, or connecting an AI agent. The person has not accepted the current beta notice. Sign in to the dashboard, read the notice and press I agree. Then try again.
403 owner_only none Only an owner can do this: add or change members, read purchases, top up, the referral program. Your role is member. Ask an owner.
403 recent_sign_in_required none POST /api/account/delete (the dashboard) only. Deleting an account needs a sign-in from the last 10 minutes. Sign in again, then delete. An API key or an AI agent cannot delete an account.
403 wrong_scope none The key has the wrong scope. A scrape key (meg_sk_...) cannot manage the account: use an account key (meg_ak_...) from the Keys page. An account key (meg_ak_...) on /v1 or batches gets: “This is an account key. It can manage the account but cannot scrape. Use a scrape key: it starts with meg_sk_.” Only GET /v1/account takes both. DELETE /v1/batches/{id} (erase) takes only an account key: a scrape key there gets this 403, and can still cancel with POST /v1/batches/{id}/cancel.
404 not_enrolled none Referral program only. This account has not joined. Join with POST /v1/account/affiliate.
404 not_found none No batch, monitor, check, key, member or pack with that id or name in your account. Ids of other accounts look the same as ids that do not exist.
404 not_subscribed none POST /v1/account/billing-portal only. This account has no subscription yet. Subscribe with POST /v1/account/subscription.
405 (no body) none /v1 takes GET, POST and QUERY. The Allow header lists them.
409 already_member none This email is already a member of this account. Find its person_id in GET /v1/account/members.
409 already_on_plan none POST /v1/account/subscription only. You are already on this plan. Pick another plan from GET /v1/plans.
409 batch_running none DELETE /v1/batches/{id} only. The batch is still queued or running, so its data cannot be erased yet. Cancel it with POST /v1/batches/{id}/cancel, then erase it once its status is canceled.
409 checkout_open none POST /v1/account/subscription only. A payment page for this account is still open. Finish it, or wait 30 minutes.
409 last_owner none This would leave the account with no owner. Make another member an owner first with PATCH /v1/account/members/{person_id}.
409 monitor_paused none POST /v1/monitors/{id}/run only. The monitor is paused. Resume it with PATCH /v1/monitors/{id} and {"paused": false}, then run it.
409 monitor_running none Monitors only. A check is running. Run now waits until it ends; so does an edit of urls, target, scope, ignore or the scrape parameters. Try again in a minute.
409 monitor_too_soon none POST /v1/monitors/{id}/run only. The last check was sooner than your plan’s fastest schedule (once an hour), and run now keeps that limit. The message names your plan and the time left; Retry-After says it in seconds.
409 no_subscription none Packs only. A pack tops up a subscription. Subscribe to a plan first with POST /v1/account/subscription.
409 notice_changed none Dashboard only. The beta notice changed after the page loaded. Reload the page, read the new notice, and agree again.
409 past_due none POST /v1/account/subscription only. Your last payment failed. Update your card in the billing portal, then change the plan.
409 replayed none You already used this Idempotency-Key. Your first call went through and X-Request-Id names it. Do not retry. Send a new key for a new request.
413 body_too_large none The body is too large. POST /v1: 1 MB; a batch: 8 MB, split it; account writes, checkout included: 4 KiB. On POST /v1, body is the target’s own request body, base64, not the page you expect back.
415 unsupported_media_type none Every POST and PATCH takes JSON. Send Content-Type: application/json, even with an empty {} body. On /v1 you can also use GET /v1 with query parameters.
422 action_failed refunded An entry in actions could not run: no visible element matched its selector in time, or the 30 s for actions ran out. error.field is actions, and the message names the entry’s index (0 is the first). The answer carries no page, cookies or target status. Check the selector, add a wait_for before it, or raise timeout. Above the failure cap it is charged, with X-Billing: failure_cap.
422 bad_extract the extraction The model’s answer did not match your schema, or the model could not take the schema. The extraction is free; the page is still charged, and the body carries it as markdown. Loosen the schema (fewer required fields), or add a prompt. Each one counts toward 20 failed extractions per account per UTC day; past them, the next extraction is a free 429 llm_token_cap.
422 blocked none The site blocked us. A block never carries page content: we drop the blocked page, so every block we detect is this 422, free, with X-Billing: blocked. The answer keeps the blocked headers: X-Hops, X-Untried, and X-Hint, X-Block or X-Challenge when they apply. Read X-Untried for a route left to try; no X-Untried means every route was tried. Above the failure cap the same 422 is charged, with X-Billing: failure_cap.
422 convert_cap refunded The page is too large or too complex to convert into the response_types you asked for (markdown, text, links, metadata or json), or it is a document over our caps (64 MiB once unpacked, or more than 500 slides). error.field is response_types. Ask for html, or for fewer response_types. When inspect is the only output and the page shows no structure, error.field is engine: the page may build its content with JavaScript, so send engine=browser. A site that answers 4xx is not this error: inspect answers 200 with the structure as found and a note. When json is the only output and we had to cut the extract (time, size or step limits) before any field got a value, error.field is extract: ask for fewer or simpler rules. That answer counts as a failed call.
422 document_unreadable refunded The url answered with a PDF, Word, PowerPoint or Excel file we cannot read: it holds only scanned images (no text layer), it needs a password, or it is damaged. Nothing was charged. Scanned pages need OCR, which we do not run yet.
422 idempotency_mismatch none This Idempotency-Key was already used with a different request. On /v1 and POST /v1/map, X-Request-Id names the first request; this one did not run and nothing was charged. On POST /v1/batches and checkout, send the same body to get the first result back. Otherwise send a new key for a new request.
422 target_error refunded The target site answered with a server error (5xx), so there is no page to send. X-Target-Status has its status. Try again later; if it keeps failing, the site is down.
422 unknown_host refunded Not sent yet: the DNS check behind it is switched off on our servers. Today a host name that does not exist ends as 503 fetch_error, free, after we tried the other routes. When the check is on, this code means DNS answered that the name in url is unknown or has no address: we stop after the first attempt, nothing is charged, and another proxy_tier or country_code will not help. A DNS server that fails or times out stays fetch_error.
422 unsupported_content refunded The URL answers with a file we cannot turn into the text response_types you asked for: an image, an archive or another binary type. Send the URL of a web page.
422 upstream_cap refunded The page pulled more data than one request may use. This also happens when a home-page visit or a client-hints retry we made for you used most of that data first. Fetch a smaller page.
429 daily_cost_cap none Your plan’s usage limit for today is used up. It resets at 00:00 UTC. Blocks we detect stay free.
429 free_failures_spent none Today’s free browser and residential retries are used up: failed browser and residential tries may use a share of your plan’s daily limit: 20% on beta, 5% on the other plans. Only tries of a request that ends with nothing charged count; a request that ends charged never adds to it. Nothing was charged. Datacenter HTTP still works, and blocks we detect stay free. A request that asks for the browser or residential gets this before any attempt. A request without engine or proxy_tier still tries datacenter HTTP first, and gets this 429 only if it then needs the browser or residential. A batch item that needs them ends failed with this error and nothing charged. POST /v1/map answers 200 with the URLs found so far, truncated: true and stopped: { code, message }. GET /v1/account shows caps.free_failures.used_percent. Retry-After counts down to 00:00 UTC.
429 free_tier_cap none Free and beta traffic is paused for all users until the 1st of next month, 00:00 UTC. This is not your account. Blocks we detect stay free.
429 llm_global_stop none Schema extraction is paused for every customer until 00:00 UTC: a global safety limit. Nothing was charged; plain fetches and extract rules still work.
429 llm_token_cap none Two cases: your daily allowance of extraction tokens (per key and per account) is used up, or 20 extractions on this account failed today (bad_extract). We stop before the page is fetched. Nothing was charged. It resets at 00:00 UTC; extract rules still work.
429 over_concurrency none You have too many requests running at once. Wait for one to finish. Retry-After says how long.
429 period_cost_cap none The usage limit of your current grant is used up. A new grant resets it. Blocks we detect stay free. X-Usage-Warning warns you once at 80%.
429 rate_limited none Checkout, the account API, batches and monitors (2 calls a second per account, bursts of 10). POST /v1/monitors/test also answers this when 3 of your dry tests are running (Retry-After: 30). Too many requests to this endpoint right now. Wait the seconds in Retry-After, then send the same request again.
429 target_rate_limited none The target site is rate limiting you. We hold your requests to that host until Retry-After ends, so you do not pay for more 429s. Wait that many seconds. Sending both engine and proxy_tier skips the hold.
500 internal refunded Our bug. Retry once, then send us the X-Request-Id.
503 busy refunded No capacity right now. Retry after the seconds in Retry-After. Many slow pages of one site at once can cause it too: we run a few requests per site at a time, so spread a burst over more sites or send it as a batch. On the account API, batches and monitors nothing was charged: send the same request again in a few seconds. DELETE /v1/monitors/{id} and account delete answer this when the stored snapshots and diffs cannot be erased: nothing was deleted, so send it again.
503 capacity_spent none Today’s residential capacity is used up for everyone. It resets at 00:00 UTC. Leave proxy_tier out, or try again tomorrow.
503 checkout_closed none Buying credits is not open on this server. Nothing was charged.
503 checkout_failed none Checkout only. Stripe did not open the payment page. Nothing was charged. Send the same request again in a minute.
503 fetch_error refunded The page could not be fetched. Check the URL. If it is right, retry; if it keeps failing, try another proxy_tier or country_code.
503 llm_unavailable none, or the page only Two cases. Schema extraction is off on this server (field is schema): we stop before the page is fetched, nothing is charged and there is no page; use extract rules instead, retrying will not help. Or the page was fetched but the extraction could not run: fewer than 3 seconds of your timeout were left, the extraction hold could not be placed, or the model provider failed or did not answer within 30 seconds. Then the page is charged as delivered and the body carries it as markdown; the extraction is free. Retry with a longer timeout, or use extract rules.
503 timeout refunded The fetch did not finish in time. Raise timeout (up to 90).
503 unavailable refunded Five causes. Screenshots are off on this server (field is screenshot): leave screenshot out. No proxy serves this proxy_tier right now (field is proxy_tier): leave it out, or pick another one. No browser runs right now (field is engine): try again later, or drop engine=browser. Paid proxies are paused for every customer until 00:00 UTC (no field): try again after that. On a batch quote, prices are not loaded yet (no field): try again after Retry-After.

Four 400s are worth knowing about:

  • An unknown parameter is a 400. We do not ignore it. render=true copied from an old script would otherwise get you a plain fetch, without the JavaScript you asked for. error.field names the key you sent.
  • A query string we cannot read has no field. Almost always a url pasted before our parameters. Put url last and paste it as it is, or percent-encode it.
  • A JSON value of the wrong type names the key. On POST /v1 every value is a string, headers is a list of strings, and timeout, max_credits and max_age also take a whole JSON number. "timeout": 30.5 is a 400 naming timeout; send "timeout": 30.
  • A private address in url is a 400. We only fetch public addresses, so a literal loopback, private, link-local, unique-local, multicast or broadcast address is refused and nothing is charged.
  • A header we set for you is a 400. headers cannot set Host, Connection, Content-Length, Transfer-Encoding, TE, Trailer, Upgrade, Expect, Keep-Alive, Via, HTTP2-Settings or any Proxy- header; the message names the header and nothing is charged.

The two ways in

POST /v1 with a JSON object is the canonical call and the one that never needs encoding. GET /v1 with query parameters is the paste-a-URL one, and everything in the next section is about that one only.

method, body and content_type exist only in the JSON envelope. Sending one of them in a query string is a 400 naming it, so a request you meant as a POST is never quietly charged as a GET.

Passing a URL that has its own query string

Paste it. Put our parameters first and url last:

Text
/v1?engine=http&url=https://example.com/catalogue/page-2.html?sort=price&ref=nb

Everything after url= that is not one of our parameter names belongs to the target and reaches it as you sent it. Encoding only the ? is fine too. Two cases need a fully percent-encoded url instead: the target’s own query uses one of our names (engine, timeout, …) or a price word from another vendor (render, premium, country, …), or you want to put our parameters after it. The 400 says so when it happens.

Retry-After

Sent with busy (503), over_concurrency, rate_limited, target_rate_limited and free_failures_spent (429, counting down to 00:00 UTC), monitor_too_soon (409), and unavailable on a batch quote (503). It is a whole number of seconds, from 1 to 86400. Sleep that long, then send the same request again. The other 429s (daily_cost_cap, period_cost_cap, free_tier_cap, llm_token_cap, llm_global_stop) carry no Retry-After: their message names the time the limit resets. Nothing was charged, so a retry costs you nothing extra.

Retrying safely

Send Idempotency-Key: <your own id> on any call you might retry. One to 128 printable ASCII characters; a UUID is the usual choice. It is yours alone: another customer sending the same word does not touch your call. It works on GET /v1, POST /v1 and QUERY /v1.

  • The first call with a key runs and is charged as usual.
  • Any later call with the same key, inside 24 hours, is a 409 replayed. It charges nothing and carries the X-Request-Id of the first call.
  • A replay does not hand you the page again. We keep no bodies, so what a replay tells you is that your first call went through and your client should stop retrying.
  • Anything we refund releases the key, so retrying is safe and normal: a 402, a 429, and every 5xx of ours (busy, timeout, fetch_error, internal). Send the same request with the same key. You were not charged for the attempt that failed, and the retry is charged once.
  • A 422 convert_cap or upstream_cap is refunded too, so it releases the key. The same request fails the same way. Change response_types, or fetch a smaller page, first.
  • A delivered result keeps the key, and so does a 422 blocked. A block is free, but it is an answer, so a second call with that key is a replayed.
  • A 503 busy where our credit store did not answer stops before the call runs, so it is never charged, and it releases the key like any other 5xx. If the store is still down when we let go, the key frees itself within a minute. Only if the store took the call’s reserve and then lost its answer can the retry come back 409 replayed for up to 24 hours; nothing was charged then either, so send a new key.
  • Send the header once. Two Idempotency-Key headers on one call are a 400 naming Idempotency-Key.
  • After 24 hours a key is forgotten. The same key then starts a new request and is charged like one, so a key is not permanent protection.
  • Without the header nothing changes, and the call costs no extra time.
  • POST /v1/batches and checkout take the header too. A replay there is a 200 with the batch or checkout page the first call made. The same key with a different body is a 422 idempotency_mismatch. A replay that races the first call waits up to 2 seconds for it; if the first call is still not done, the answer is 503 busy with Retry-After: ask again with the same key and body.

200 is not always what you wanted

A target that answers under 500 is a delivered result, whatever it answered. You get HTTP 200 from us and the target’s own status in X-Target-Status. There is no error body to read, so read the headers.

what happened how you see it credits
The target answered, with any status under 500 that is not a block, 404 included. X-Target-Status full price
An http result that is only a JavaScript shell, and the ladder could not climb to the browser (your plan or max_credits). The body is empty. Send engine=browser to render it. X-Hint: needs_js free
A shell you asked for with engine=http. X-Hint: needs_js, the HTTP body full price of the route that answered
The body passed the 50 MB stop. The body ends early. The 200 was already sent, so there is no status to change. full price

A block is never a 200. When the target blocks us (401, 403, 407, or an anti-bot challenge) on every route we try, the answer is a 422 blocked with X-Billing: blocked, X-Hops, and X-Untried when a route is left. It is free. The one exception is a request with actions above the failure cap: still a 422 blocked, but with X-Billing: failure_cap, and charged.

Blocks we detect are free, however often they happen. Your plan’s usage limit still counts what they cost us, so a key that fails all day stops at that limit with a 429 daily_cost_cap. A target that answers 429 is not a block result: you get our own 429 target_rate_limited (see the table above).

Actions and the failure cap

A request with actions can click, type or submit on the target and then fail on purpose, so free failures there have a cap. We count your requests with actions over a sliding hour, this one included. A request counts as failed when it ends free: action_failed, a block, a timeout or any other error. When at least 20 of them ran and more than 30% failed, an action_failed or a block is charged the page’s price, actions included, and carries X-Billing: failure_cap. Other free errors stay free and only count. It is never charged twice, and bytes above the included data stay free. The count moves on as the hour slides; requests without actions never count. Schema extraction is never charged for a failure: after 20 failed extractions in a UTC day, the account’s next extraction is a free 429 llm_token_cap, before the page is fetched.

A free answer carries no page content, on /v1, map, MCP and in a batch. A block is a free 422 blocked: the headers (X-Billing, X-Block, X-Challenge, X-Vendor, X-Hops, X-Untried, X-Target-Status) say what happened, and the body is only our JSON error. A target 5xx is a free 422 target_error with X-Target-Status, never an empty 200. In a batch, a free item is done with no result_url, and its error says why: blocked, target_error or needs_js.

engine=browser sends the page straight to a real browser (Chrome, on datacenter exits). Add proxy_tier=residential for residential IPs. It pays the browser’s price: 15 credits on datacenter, 32 on residential, and only when the page comes back. Bytes above the included data add a price for each started 128 KiB, up to the byte cap. A browser request reserves that whole worst case from your balance before it runs, not only the floor; with less balance or a lower max_credits, the page is cut at the bytes you can pay for. You pay the actual price. Batches price it the same way. When every browser is busy, the answer is a free 503 busy with Retry-After. When no browser runs at all, it is a free 503 unavailable naming engine.

Money headers

header on what it says
X-Credits-Charged every 200 The charge if the body completes, overage included. It leaves before the body, so it cannot change. If we cut the body (an engine error mid-stream: the multipart closing boundary is missing or the stream ends in an error), the request is refunded in full. If you close the connection after the headers, or read so slowly that the body outlasts your timeout after its first bytes reached you, the charge stays. Your usage page is the final record.
X-Credits-Route, X-Credits-Output, X-Credits-Other with X-Credits-Charged The same charge in three parts: reaching the page, what we made of it, and the rest (actions, and a monitor check). They add up to X-Credits-Charged. A charged 422 with X-Billing: failure_cap carries them too. Credits has the details.
X-Billing a 422 blocked, and a charged 422 action_failed blocked: we detected a block, and you paid nothing. failure_cap: a request with actions failed or was blocked above the failure cap, and X-Credits-Charged says what it cost.
X-Engine every 200, and a 422 blocked http or browser: the engine that produced the result.
X-Route every 200, a 422 blocked, and a target_rate_limited 429 The engine and proxy tier that answered, such as http/datacenter, http/residential, browser/datacenter or browser/residential.
X-Geo every answer from a request that ran, blocked or not The country the request exited from, like DE: country_code when sent, else the default.
X-Ladder-Truncated a 200 where it applies true: your max_credits or your balance removed some routes we would have tried.
X-Truncated a 200 where it applies max_credits or byte_cap: the page reached the most bytes this request may pull. max_credits means your max_credits set that limit, and the charge stays at or under it. byte_cap means the route’s or your plan’s byte cap, or your balance, set it. The body is the part that fit. Raise it, or remove it, for the whole page.
X-Timing-Target-Ms every 200 Milliseconds from our request to the target’s first byte.
X-Identity every 200 firefox or chrome: the browser the fetch looked like on the wire. Both cost the same. A browser render is always chrome.
X-Block a 422 blocked challenge, deny or rate_limited: what kind of block we detected.
X-Vendor any 200 where we saw one The anti-bot vendor present on the page, even when it let us through. X-Challenge names the one that blocked us.
X-Hops every 200 Every hop we took, measured up to the moment these headers left. Time to stream the body is not in it. It is a JSON array of {"rung", "identity", "ms", "outcome"}, like [{"rung":"h:dc","identity":"chrome","ms":412,"outcome":"blocked"},{"rung":"c:res","identity":"chrome","ms":2900,"outcome":"ok"}]. outcome is ok, blocked, needs_js, or the error code of a try that failed, such as timeout or fetch_error. The rungs are h:dc (HTTP, datacenter), h:res (HTTP, residential), c:dc (the browser, datacenter) and c:res (the browser, residential).
X-Untried blocked and needs_js results, when a route is left The rungs your key may use that can serve now and this request did not try, such as h:res,c:dc. X-Hops lists the ones it tried. No header means every route we have was tried: retrying will not help today.
X-Untried-Reason with X-Untried, when our budget, not the site, stopped the climb budget: residential routes are unavailable right now, so every residential route was skipped (never charged) and the climb tried the next open one.
X-Hint when it applies needs_js: the page is a JavaScript shell, send engine=browser. browser_blocked: the site blocks our browser too, so we did not try it.
X-Network-Truncated a 200 with network=true, when it applies true: a cap cut the list of captured responses (200 responses, 8 MiB in all, a URL over 8 KiB, or a body we could not keep).
X-Compact-Truncated a 200 with compact_html, when it applies true: compact_html reached its cap of about 512 KiB and ends at an element boundary.
X-Usage-Warning the first 200 after your grant passes 80% of its usage limit Once per grant: at 100% requests answer 429 period_cost_cap until a new grant.
X-Request-Id every response Quote it to support.

Errors carry no X-Credits-Charged, because nothing was charged.

Safety headers

header on what it says
Content-Security-Policy every 200, and a 422 blocked sandbox; default-src 'none'; img-src data:; style-src 'unsafe-inline'. The body is the target’s page, not ours. If a browser opens it, it runs no script, has no origin and loads nothing from the network: only inline styles and data images show.
X-Content-Type-Options every 200, and a 422 blocked nosniff. A browser reads the body only as its Content-Type says.
Referrer-Policy every 200, and a 422 blocked no-referrer. A link in the page tells the next site nothing.

Result and screenshot download links (batches, monitors) download as a file: they carry Content-Disposition: attachment, so a stored page never opens on our storage host.