{
"error": {
"code": "plan_required",
"message": "'history' requires the pro plan or above.",
"request_id": "req_0192d3a4b5c67d8e9f0a1b2c3d4e5f65",
"details": {
"capability": "history",
"required_plan": "pro"
},
"required_plan": "pro"
}
}
code is stable and machine-readable. message is for a person, in English, and names what was wrong: the parameter, the value, what was expected.request_id is also in the X-Request-Id header. Quote it when you write to us: it is the id in our logs.details is always an object, empty when there is nothing to add. required_plan appears only on plan_required.- A
429 carries Retry-After in seconds.
{
"error": {
"code": "invalid_body",
"message": "company.postcode: company.postcode is not a Belgian postcode",
"request_id": "req_0192d3a4b5c67d8e9f0a1b2c3d4e5f66",
"details": {
"field": "company.postcode"
}
}
}
{
"error": {
"code": "plan_required",
"message": "companies=800 is above the 500 this plan allows. The enterprise plan or above allows it.",
"request_id": "req_0192d3a4b5c67d8e9f0a1b2c3d4e5f67",
"details": {
"capability": "companies_max",
"parameter": "companies",
"requested": "800",
"allowed": "500",
"required_plan": "enterprise"
},
"required_plan": "enterprise"
}
}
400 invalid_parameter means the request could not be read as asked: an unknown or repeated query parameter, a value outside its vocabulary, a limit above your plan, or a JSON body that does not parse, has a property the route does not know, or a value of the wrong type. 422 invalid_body means the body was read and says something that cannot be right: a postcode that is not Belgian, an enterprise number whose check digits fail, a negative budget. details.field names the property.
Request
Request errors| Code | Status | When | What to do |
|---|
| invalid_parameter | 400 | A query parameter is malformed, out of range, repeated, or not a parameter of the route; a vocabulary value is not one of its members; a limit is above your plan's page size; or a JSON body does not parse, has a property the route does not know, or has a value of the wrong type. | The message names the parameter and what was expected. Fix the request: retrying it unchanged fails the same way. Unknown parameters are refused rather than ignored, so a typo never silently widens a result. |
| invalid_cursor | 400 | The cursor is not one the API issued, or was issued for a different listing or sort order. | Start again without a cursor. The API refuses rather than silently restarting at page one. |
| invalid_body | 422 | A JSON body parsed but says something impossible: an enterprise number that fails its check digits, a postcode that is not Belgian, an invalid NACE code, an unknown legal form, a negative amount, a limit out of range, a duplicate reference.details: field: the property at fault, for example company.postcode. | Correct the property named in details.field. |
| route_not_found | 404 | No endpoint exists at this path. The answer is still JSON in the error envelope. | Check the path against /v1/openapi.json. |
| method_not_allowed | 405 | The path exists but does not take this method, for example a GET on /mcp. | The Allow header lists the methods the path takes. |
Key and plan
Key and plan errors| Code | Status | When | What to do |
|---|
| unauthorized | 401 | The key is missing, malformed or revoked, on a route that needs one. | Send Authorization: Bearer with a live or test key from the dashboard. |
| subscription_required | 402 | The key's account has no plan yet. It can sign in and create keys, but no key serves data until a plan is chosen. | Choose a plan in the dashboard. The same key works as soon as the plan is active. |
| plan_required | 403 | The route needs a capability your plan does not include (the change feed, history, as_of, webhooks, bulk matching, watchlists, the bulk export), or a number is above what your plan allows (companies in a bulk match, companies across your watchlists, webhook endpoints).details: capability and required_plan for a missing capability; for a number above the plan, also parameter, requested and allowed. error.required_plan repeats the plan. | required_plan is the cheapest plan that would have answered. Upgrade, or leave the feature out. |
Data
Data errors| Code | Status | When | What to do |
|---|
| subsidy_not_found | 404 | No measure has this id or slug.details: subsidy: the id or slug you asked for. | GET /v1/subsidies lists them. A measure merged into another keeps resolving to the one it was merged into, so an old id is not a reason for this. |
| version_not_found | 404 | The measure exists, but not the version you asked for, or it did not exist yet at the as_of instant. | The message says which version is current, or when the first version is from. |
| source_not_found | 404 | No source has this id. Also answered when a webhook filter names an unknown source.details: source: the id you gave. | GET /v1/sources lists them. |
| watchlist_not_found | 404 | No watchlist with this id belongs to your account. Also answered when a webhook filter names one. | GET /v1/watchlists lists yours. |
| webhook_not_found | 404 | No webhook endpoint with this id belongs to your account. | GET /v1/webhooks lists yours. |
| not_found | 404 | The company reference you asked to remove is not on the watchlist. | GET /v1/watchlists/{id}/companies lists the references on it. |
| conflict | 409 | The request is valid but the state does not allow it, for example a 51st watchlist on one account. | The message says why. Change the state first (delete a watchlist), then retry. |
Limits
Limits errors| Code | Status | When | What to do |
|---|
| rate_limit_exceeded | 429 | Too many requests per second for your plan (or, on the routes that need no key, for your address). Also answered when both bulk export download slots are in use. | Wait the seconds in Retry-After (1, or 5 for the export). Page one request at a time rather than in parallel. |
| quota_exceeded | 429 | This month's request quota is used up. | Retry-After counts the seconds to the start of the next UTC month. RateLimit-Remaining shows it coming. |
| match_quota_exceeded | 429 | This month's match evaluations are used up, or a bulk match or watchlist upload would take you past them. Reading measures still works.details: limit: your plan's monthly match evaluations. | Retry-After counts the seconds to the next UTC month. A larger plan has a larger allowance. Nothing of a refused bulk request is evaluated or counted. |
Server
Server errors| Code | Status | When | What to do |
|---|
| internal_error | 500 | Something failed on our side. The detail is logged, never sent. | Retry with backoff. If it persists, send us the request_id. |
| billing_unavailable | 503 | Card payment is not configured or the payment provider did not answer. Answered by the dashboard's billing routes only. | Try again later, or write to us. |
| timeout | 504 | The request took longer than 30 seconds and was stopped. No route should come near it; the export streams. | Retry it. A narrower filter or a smaller page answers faster. |
Dashboard sign-in
Dashboard sign-in errors| Code | Status | When | What to do |
|---|
| google_sign_in_failed | 401 | A Google sign-in token could not be verified. | Sign in again. |
| password_sign_in_disabled | 403 | Password registration or sign-in was attempted on a deployment that offers Google sign-in only. | Sign in with Google. |
- Request bodies are limited to 2 MB.
- Past authentication, the MCP endpoint answers protocol problems as JSON-RPC errors (HTTP
200, or 400 for a body that is not JSON), and a failed tool as a result with isError; see MCP.
Retrying
Retry 429 after Retry-After, and 500, 503 and 504 with exponential backoff. Do not retry 400, 401, 402, 403, 404 or 422 unchanged: the same request fails the same way.