Errors.
Every error is a JSON body with a code, a message and whether a retry can succeed.
Every non-2xx response has error (the code below), message (for people) and retryable (whether sending the same request again can succeed). Most also carry request_id, and some add the detail fields listed below.
HTTP/1.1 402 Payment Required
X-Request-Id: 8c3d…
{
"error": "insufficient_credits",
"message": "Your balance does not cover this request. Top up to continue.",
"retryable": false,
"request_id": "8c3d…",
"required_usd": 0.0255,
"balance_usd": 0.004,
"top_up_url": "https://metasearch.sh/dashboard/billing"
}| status | error | retry | meaning |
|---|---|---|---|
| 400 | invalid_json | no | The body is not valid JSON. |
| 400 | invalid_request | no | A field is unknown, has the wrong type or is out of range, or both provider and providers were sent. Details: field, reason. |
| 400 | missing_query | no | query is missing or empty after trimming. |
| 400 | query_too_long | no | query is over 2,000 characters (text modes) or 2,048 (URL modes). Details: max. |
| 400 | invalid_mode | no | No such mode. Details: valid_modes. |
| 400 | invalid_url | no | In extract, scrape and similar, query must be an absolute http(s) URL whose host resolves to a public address. |
| 400 | unknown_provider | no | No engine by that name. Details: valid_providers. |
| 400 | provider_mode_mismatch | no | The engine you named does not serve that mode. Details: provider, supported_modes. |
| 400 | filter_unsupported | no | A filter cannot be honoured: the pinned engine does not support it, or filters were sent in a URL mode. Details: filter, provider (when pinned), supported_by. |
| 401 | invalid_api_key | no | The bearer key is missing, unknown or revoked. |
| 401 | key_expired | no | The key has passed its expiry date. Create a new key, run search login, or ask an owner or admin to extend this one. Details: expired_at. |
| 402 | insufficient_credits | no | Your balance is below the reservation for this request. Top up, or choose a cheaper engine or mode. Details: required_usd, balance_usd, top_up_url. |
| 402 | spend_cap_reached | no | This key's daily cap would be exceeded. Raise the cap in the dashboard or wait for the reset at midnight UTC. Details: cap_usd, spent_today_usd, resets_at. |
| 404 | not_found | no | No API route at that path. The manifest at /api/v1/manifest lists the real ones. |
| 409 | idempotency_in_progress | yes | A request with the same Idempotency-Key is still running. Retry after the Retry-After seconds. |
| 422 | idempotency_mismatch | no | That Idempotency-Key was already used with a different body. Use a new key. |
| 429 | rate_limited | yes | Over 300 requests a minute for your organisation (reason "requests"), or too much upstream cost from failed attempts in the last hour (reason "failed_upstream_cost"). Wait the Retry-After seconds. Details: reason, limit, Retry-After header. |
| 500 | internal_error | yes | A fault on our side. Nothing is charged and the reservation is returned. Retry. Details: request_id. |
| 502 | provider_error | yes | Every engine tried failed. Nothing is charged. Retry, or name another engine. Details: attempts. |
| 503 | provider_not_available | no | The engine you named is switched off, not configured or failing. Name another, or omit providers. Details: provider, reason. |
| 503 | no_provider_for_mode | no | No engine can serve this mode right now. Check /status. Details: mode. |
| 503 | service_unavailable | yes | Our database is unreachable. Retry after the Retry-After seconds. This is never reported as a 401. Details: Retry-After header. |
What is charged#
A request refused with a 4xx is refused before any engine is called, so nothing is charged. A 502 or 500 charges nothing and returns the whole reservation. Billing has the full rules.
Retrying#
Retry only when retryable is true, and wait the Retry-After seconds when the header is present. Send an Idempotency-Key with each request so a retry after a dropped connection cannot charge twice.
Getting help#
If an error looks wrong, send the request_id to support@paperfoot.com. It identifies the call, its attempts and its ledger rows.