# Errors

Last updated: 2026-10-10

Every error code the API can return, what it means and whether the call was charged. All errors share one JSON shape:

```json
{ "error": { "code": "invalid_url", "message": "url: …. You were not charged.", "details": { "field": "url" } } }
```

`code` is stable and meant for programs; `message` is for people; `details` is optional (for example `field` or `retry_after_ms`). An answer with status 400 or above is never charged (with L402, the token is not spent and still works). The one exception is `settle_failed`: the payment may have gone through even though settling reported a failure. Resend the same request with the same payment id (the x402 `payment-identifier` extension): if the charge went through, you get the withheld result without paying again (marked `payment-redelivered: true`); if not, the payment is settled at most once. A charge that went through is also recorded for reconciliation. So a `payment_already_used` answer can follow a charged `settle_failed`; that call itself charged nothing.

| Status | Code | Meaning |
|---|---|---|
| 400 | `bad_request` | Body is not a JSON object, or a field has the wrong type. |
| 400 | `invalid_request` | A field is missing or out of range (for example a quote over 1,000 characters), or both an x402 payment header and an L402 `Authorization` header or a credit key were sent (`details.reason` `two_payment_headers`). |
| 400 | `invalid_url` | The URL is malformed, too long, not http(s) or on another port; `details.reason` says which. |
| 400 | `blocked_target` | The URL points at a private, loopback or otherwise internal address. |
| 402 | `payment_required` | Pay with x402: see the `PAYMENT-REQUIRED` header and [Payments](/docs/payments). Where L402 is on, the `WWW-Authenticate: L402` header carries a macaroon and a Lightning invoice too. A free-trial call to `verify/citations`, `verify/quotes`, `verify/document` or `check/links` with more than 3 citations, quotes, pairs or URLs gets this too, with `details.reason` `trial_too_large`: pay for it, or send at most 3 per trial call. |
| 402 | `payment_invalid` | The payment did not verify (`details.reason` says why). Sign a new one from the `PAYMENT-REQUIRED` header. For an L402 token the reason starts with `l402_` (for example `l402_wrong_route`, `l402_expired`, `l402_preimage_mismatch`) and a fresh `WWW-Authenticate` offer is included. A resend for a withheld `settle_failed` result whose signature is not the payer's own gets this with `details.reason` `invalid_exact_evm_payload_signature`: resend the original signed payment, or a new one signed with the payer's key. |
| 403 | `robots_disallowed` | robots.txt or our opt-out list disallows the page (or a redirect from it). |
| 403 | `payer_refused` | The paying address is on a sanctions list. For a credit key: a payment to it came from a listed address, so the key is blocked (`details.reason` `credits_payer_refused`). |
| 404 | `not_found` | No such route or sample, or (`GET /v1/credits`) no such credit key. |
| 409 | `payment_id_conflict` | The payment id was already used for a different request. |
| 409 | `payment_already_used` | This payment signature was already spent, and this call did not charge again. Sign a new payment; the `PAYMENT-REQUIRED` header is included. If an earlier call with it ended in `settle_failed`, resend that request with its payment id to get the result. `details.reason` `result_not_kept`: that earlier call was charged but its result is no longer kept; the charge is recorded for reconciliation. For L402: the token already paid for another call (`details.reason` `l402_spent`); pay the fresh invoice in `WWW-Authenticate`. |
| 409 | `settle_pending` | An earlier call with this payment id ended in `settle_failed`, it is not charged on chain yet, and its payment could still go through. Resend the same signed payment now, or a new one after `Retry-After` (`details.retry_after_ms`). |
| 409 | `idempotency_conflict` | The attest `idempotency_key` was already used with a different hash or note. |
| 413 | `body_too_large` | Body over 64 KB (168 KB on `receipt/verify` and `/mcp`). |
| 422 | `host_not_found` | The URL's host name does not resolve (for `check/links` and `verify/citations`: none of the URLs resolves; for `verify/document`: none in the part). |
| 422 | `page_too_large_for_edge` | Edge server only: the page is over the 64 KB it reads, or (check/packages) a registry answer is over the 256 KB it reads, with only the names over that size in `details.too_large` (no results for the others); `details.full_service` is the server to send the call to. |
| 422 | `edge_subrequest_limit` | Edge server only: the call needs more outbound requests than the edge allows (many hosts or redirects); use `details.full_service`. |
| 422 | `match_too_costly` | Fuzzy matching this quote would take too long; use `"match": "exact"` or a shorter quote. For `verify/citations` and `verify/quotes` this is an `error` on that citation or quote only and the call is charged; only when every one fails does the call get 422, with `details.failed` listing them. `verify/document` works the same way per part. |
| 422 | `no_pairs` | `verify/document`: no quote with a link was found in the document; `details.skipped` counts what was skipped and why. Never charged. |
| 429 | `rate_limited` | Too many requests to that host or from your address, or (for `check/packages`) the crates.io queue of one lookup a second is full; see `Retry-After`. Where L402 is the only offer, also too many unpaid Lightning invoices (`details.reason` `l402_invoice_cap`). For `verify/citations`, `verify/quotes` and `verify/document` (one shared count), also too many calls from your address that failed uncharged (`details.reason` `uncharged_failures`). |
| 429 | `trial_exhausted` | Free trial calls used up for the day; see `Retry-After`. |
| 500 | `internal_error` | Something failed on our side. |
| 502 | `registry_unavailable` | `check/packages` only: every registry lookup in the batch failed (`details.reasons`), so nothing was charged. Try again later. |
| 502 | `facilitator_unavailable` | The payment facilitator could not be reached, so no payment was taken. Try again later. |
| 502 | `settle_failed` | The work ran but the payment could not be settled, so the result is withheld. Resend the same request with the same payment id: if the charge went through on chain, you get the result without paying again; if not, the payment is settled at most once. If a charge went through, it is recorded for reconciliation. Redelivery runs on the full service only; on the edge server see [Payments](/docs/payments). |
| 501 | `not_on_edge` | Edge server only: `POST /v1/attest`, `POST /v1/verify/citations`, `POST /v1/verify/quotes` and `POST /v1/verify/document` run on the full service (the free `verify/document/preview` works on the edge) (`details.full_service`). Also `fetch` and `verify/quote` when the page is a PDF (`details.reason` `pdf`, with `details.final_url`): only the full service reads PDFs. Never charged. |
| 503 | `paused` | Paid routes are paused for maintenance. |
| 503 | `over_capacity` | `verify/citations`, `verify/quotes` and `verify/document`: too many batches are waiting to be matched (`details.reason` `match_queue_full`). `fetch`, `verify/quote`, `verify/quotes` and `verify/citations`: the page is a PDF and too many PDFs are waiting to be read (`details.reason` `pdf_queue_full`, with `details.field`). Retry after `Retry-After`. Never charged, even when it comes after your payment was verified. |
| 503 | `signing_unavailable` | Signing keys are not loaded. |
| 503 | `payments_unavailable` | Payments are not set up on this server, (with L402 only) no invoice could be made, or a new payment for a payment id whose earlier call ended in `settle_failed` came while the chain could not be read. Where credit keys are on, also: no credit address could be issued (the signet explorer is down, the server's addresses are used up, or too many issued addresses are still unpaid: `details.reason` `credits_gap_full`). |
| 503 | `sanctions_list_stale` | Edge server only: its built-in sanctions list is more than 8 days old, so it refuses payments until it is rebuilt (`details.list_date`, `details.full_service`). Never charged. |
| 503 | `payment_screening_unavailable` | The payment check could not be completed (the chain read failed or timed out, or the sanctions list could not be read); retry after `Retry-After` and do not pay again. For a credit key that needs a new payment counted: `details.reason` `credits_screening_unavailable`. |

## L402 reasons

Where L402 is on, a token that is refused carries one of these in `details.reason`. None of them spends the token or charges anything.

| Reason | Code | Meaning |
|---|---|---|
| `l402_malformed` | `payment_invalid` | The `Authorization: L402 <macaroon>:<preimage>` header or the macaroon cannot be read. |
| `l402_bad_signature` | `payment_invalid` | The macaroon was not issued by this server, or was changed after it was issued. |
| `l402_bad_version` | `payment_invalid` | The macaroon identifier has a version this server does not issue. |
| `l402_unknown_caveat` | `payment_invalid` | The macaroon has a caveat this server does not understand. |
| `l402_missing_caveat` | `payment_invalid` | The macaroon lacks its `route`, `price` or `expires` caveat. |
| `l402_wrong_route` | `payment_invalid` | The token was issued for a different route. |
| `l402_price_mismatch` | `payment_invalid` | The token's price is below this route's sats price. |
| `l402_expired` | `payment_invalid` | The token's `expires` time has passed. |
| `l402_preimage_mismatch` | `payment_invalid` | The preimage does not hash to the invoice's payment hash (the invoice was not paid). |
| `l402_spent` | `payment_already_used` | The token already paid for a different call. |
| `l402_invoice_cap` | `rate_limited` | No new Lightning invoice: too many unpaid ones from your address (see [Limits](/docs/limits)) (`details.scope` `ip`) or open on the server (`global`). Wait for `Retry-After`. When x402 can be offered, the answer is instead the usual 402 `payment_required` with the x402 offer, no L402 offer, this reason and `Retry-After`. |
| `two_payment_headers` | `invalid_request` | Both an x402 payment header and an L402 `Authorization` header were sent. |

## Credit key reasons

Where prepaid credit keys are on (`POST /v1/credits/order`, then `Authorization: Bearer qpc_…` on a paid route; Bitcoin signet, full service only), a refusal carries one of these in `details.reason`. None of them takes sats from the key. Before a payment to a key's address is credited, every input address of that payment is checked against the OFAC sanctions list; a payment with an input that has no address (a coinbase or bare script) cannot be checked and is held (`held_sats`, status `held`), not credited.

| Reason | Code | Meaning |
|---|---|---|
| `credits_amount` | `invalid_request` | `POST /v1/credits/order`: `sats` is not a whole number from `details.min_sats` to `details.max_sats`. |
| `credits_order_cap` | `rate_limited` | Too many credit orders from your address in the last hour; pay a key you have, or wait for `Retry-After` (`details.retry_after_ms`). |
| `credits_gap_full` | `payments_unavailable` | `POST /v1/credits/order`: too many credit addresses in a row are still waiting for payment, so no new one is issued until one is paid or expires unpaid (see [Payments](payments.md)). Pay a key you already have, or try again later. |
| `credits_no_key` | `invalid_request` | `GET /v1/credits` without `Authorization: Bearer <credit key>`. |
| `credits_malformed` | `payment_invalid` on a paid route, `not_found` on `GET /v1/credits` | The `qpc_` key is not 47 characters of base64url. |
| `credits_unknown_key` | `payment_invalid` on a paid route, `not_found` on `GET /v1/credits` | This server never issued that key. |
| `credits_insufficient` | `payment_required` | The key has fewer confirmed, screened sats available than the route's price (`details.price_sats`); `details` also carries the key's `address`, `status` and balances. Pay the address again to top up. |
| `credits_payer_refused` | `payer_refused` | A payment to the key's address came from an address on the sanctions list: the key is blocked for good and that payment is not credited. |
| `credits_screening_unavailable` | `payment_screening_unavailable` | The key has fewer sats than the price, and its address could not be read and screened now (the chain read failed or timed out, or the sanctions list cannot be read), so a new payment may not be counted yet. Do not pay again: retry after `Retry-After` (`details.retry_after_ms`). Sats already credited still work. |
| `two_payment_headers` | `invalid_request` | A credit key was sent with an x402 payment header. |

## Results that are not errors

A 404, 500, bot wall, timeout or dead link on the page you asked about is a result, not an error. It comes back with status 200, a `content_kind` such as `http_error` or `bot_wall`, and a receipt, and it is charged. See [How it works](/docs/how-it-works).
