The problem document
An error answers with the content type application/problem+json (RFC 9457) and a body like this one. There is no success member and no other error shape.
An example problem document
{
"type": "https://perfbee.com/developers/errors#not_found",
"title": "Not found",
"status": 404,
"code": "not_found",
"detail": "No crawl with this id in your workspace.",
"request_id": "7c1f0a52-3d4b-4a39-8c0e-5e2a9d1b6f10"
}| Field | Type | Description |
|---|---|---|
| type | string | A URL naming the kind of error: https://perfbee.com/developers/errors#<code>. |
| title | string | A short, stable summary of the kind of error. |
| status | integer | The HTTP status, repeated. |
| code | string | The stable machine code (see the error registry in the docs). |
| detail | string | What went wrong this time, in a sentence. |
| request_id | string | The request's X-Request-ID: quote it when you contact support. |
| credits | object or null | 402 only: the credit arithmetic. |
| credits.needed | number | Credits the operation needs (worst case). |
| credits.available | number | Credits the workspace has left this month. |
| credits.held_by_running_crawls | number | Credits reserved by crawls that are still running. |
| errors | array of object or null | 422 only: every field that was refused. |
| errors[].in | string | Where the value was read: body, query, path or header. |
| errors[].field | string | The parameter or body field, dotted for nested ones. |
| errors[].message | string | Why it was refused. The value that was sent is never echoed. |
| errors[].type | string | A short machine word for the kind of mistake. |
| rate_limit | object or null | 429 from a rate limit: which one and how big. |
| rate_limit.scope | string | Which limit was hit: key, heavy, start, workspace or network. |
| rate_limit.limit | integer | Calls allowed in the window. |
| rate_limit.window_seconds | integer | Length of the window in seconds. |
| required_scope | string or null | 403 scope_required only: the scope the endpoint needs. |
code is the word to switch on. detail is for people and may change. type is a link to the row of the table below. Quote request_id, which is also the X-Request-ID header, when you write to support.
Handling errors
- A 429 carries
Retry-After: wait that many seconds, then repeat the same call. The same goes forcapacity_busyandcrawl_not_ready. - A 402 is not a rate problem: the workspace has too few credits.
creditssays what is needed and what is free. - A 422, 404, 403 and 401 will not change if you repeat the call. Fix the call.
- An
internal_erroris our fault. Try again after a short wait, and send therequest_idif it keeps happening. - A crawl started twice with the same
Idempotency-Keyis safe: see idempotency.
New codes can be added within version v1. A client should treat a code it does not know by its status.
Every error code
| Code | Status | What it means |
|---|---|---|
| bad_request | 400 | Bad request. The request could not be understood, for example a path or header the API cannot read. |
| invalid_cursor | 400 | Invalid cursor. The cursor was changed, belongs to another endpoint, another set of filters or another workspace. Start the list again without a cursor. |
| invalid_key | 401 | Invalid API key. The key is missing, malformed, revoked, expired, or its owner left the workspace. Send `Authorization: Bearer pbk_...`. |
| insufficient_credits | 402 | Not enough credits. The operation needs more credits than the workspace has left this month. `credits` says how many are needed and free. |
| forbidden | 403 | Forbidden. The key is valid but the API is not available to its workspace yet. |
| plan_required | 403 | Plan upgrade required. The workspace's plan does not include this feature. |
| scope_required | 403 | Scope required. The key lacks the scope this endpoint needs; `required_scope` names it. |
| ai_readiness_unavailable | 404 | No AI readiness report. The crawl has no AI readiness report: a fast crawl extracts no page data, and a detailed crawl that stopped early or is older than the report has none. `reason` says which. |
| not_found | 404 | Not found. The object does not exist, or belongs to another workspace (the two are never told apart). |
| method_not_allowed | 405 | Method not allowed. The path exists but not for this HTTP method; `Allow` lists the methods. |
| crawl_in_progress | 409 | Crawl in progress. A crawl of this site is already pending or running; `crawl_id` in the problem is that crawl. |
| crawl_not_active | 409 | Crawl is not active. The crawl is not pending or running, so there is nothing to cancel; `detail` says what it is. |
| crawl_not_ready | 409 | Crawl not ready. The crawl's data is not available yet or any more; retry after the time in `Retry-After`. |
| delivery_in_progress | 409 | Delivery in progress. The delivery is still pending or being retried. Wait until it is delivered or failed. |
| delivery_payload_unavailable | 409 | Payload is no longer stored. Only a failed delivery keeps its payload, for 3 days; a delivered event, or an older failure, cannot be sent again. Read the state through the API instead. |
| feed_limit_reached | 409 | Live link limit reached. The workspace already has as many live links as it may; revoke one first. |
| feed_site_has_staging_access | 409 | Site has staging access. The site has staging access set up, so its data cannot be shared through a live link. |
| idempotency_in_progress | 409 | Request in progress. A request with the same Idempotency-Key is still being handled; retry shortly. |
| webhook_endpoint_limit | 409 | Webhook endpoint limit reached. A workspace can have 5 webhook endpoints. Delete one first. |
| export_too_large | 413 | Export too large. The export holds more rows than a direct download allows; narrow it or use the dashboard's export job. |
| feed_too_large | 413 | Live link too large. The dataset holds more rows or cells than a live link can; narrow it with filters or download an export. |
| idempotency_key_reused | 422 | Idempotency key reused. The Idempotency-Key was already used with a different request body. |
| invalid_webhook_events | 422 | Invalid webhook events. `events` must name at least one event from GET /webhook-events, each once. |
| invalid_webhook_url | 422 | Invalid webhook URL. The URL must be an https:// address on a public host, without a user name, spaces or quote characters. The host is resolved when the endpoint is saved and again at every delivery; a private or internal address is refused. |
| validation_error | 422 | Validation failed. A parameter or body field is missing or has a wrong value; `errors` lists them. |
| crawl_limit_reached | 429 | Daily crawl limit reached. The key started 10 crawls today (UTC), or the workspace 20. `crawl_limit` says which; `Retry-After` is the time to the next UTC day. |
| crawl_too_soon | 429 | Crawl too soon. The site was crawled through the API less than 10 minutes ago (by REST or the MCP server). Crawls of one site through the API must be that far apart; `Retry-After` is the time left. |
| daily_credit_cap_reached | 429 | Daily credit cap reached. The crawls this key started today, counted at their worst case, plus this one would pass the key's daily credit cap. `daily_credit_cap` says the cap, what was used and what this crawl needs; `Retry-After` is the time to the next UTC day. |
| rate_limited | 429 | Rate limit exceeded. Too many requests this minute for the key, the workspace or the network; wait for `Retry-After` seconds. |
| too_many_heavy_requests | 429 | Too many heavy calls at once. Too many heavy calls of this workspace (lists of links, issues and pages, diffs, exports, webhook tests) are running at the same time. Send them one after another; retry after `Retry-After` seconds. |
| too_many_running_crawls | 429 | Too many running crawls. The workspace already has 2 API crawls running. Wait for one to end, or cancel one. |
| internal_error | 500 | Internal error. Something failed on our side; the error is logged under `request_id`. |
| capacity_busy | 503 | Service busy. The service has no free capacity for this request right now; retry after `Retry-After`. |
| query_too_slow | 503 | Query too slow. The data query ran past its time limit; narrow the filters or the page size and try again. |
| webhook_signing_unavailable | 503 | Webhook signing is not available. Signing secrets cannot be made on this server yet, so an endpoint cannot be created or its secret rotated. Try again later or write to support@perfbee.com. |