Status codes
Error body
Errors are JSON with anerror field. Depending on the endpoint, more fields are added:
The one exception: on a rate-limit
429, error is an object, not a string — see
Rate limit.
Keys or sentences
- Stable keys. These places carry a stable, non-localised key:
413 batchTooLarge,409 overwriteRequired,failed[].errorandinvalid[].errorin PPWR sync results,failed[].error.keyin async job results, and the job’serror(a key, or a JSON string whosekeyfield is the stable part). The plot endpoints send a stablecodenext to the sentence on geometry and intake refusals. Match on these. Envelope and validation400s carry an English sentence, also on batch imports and PPWR — decide on the status code there. The lists: batch import error keys, PPWR error keys, plot error codes. - Sentences. On the other endpoints (e.g. creating a single article, supplier or order),
erroris an English sentence. Its wording may change — decide on the status code (andwarnings[].type), and log the sentence.
Rate limit
All/api paths share one limit: 1000 requests per 60 seconds per client IP address (fixed
window). It applies before the request reaches the application. There is no separate per-key limit.
Above the limit you get 429 with a JSON body whose error is an object:
- A
Retry-Afterheader is not guaranteed. Wait until the next minute, then retry with exponential backoff and jitter. - The limit counts per IP address, not per key. All calls from one office or server IP share it — including people using the web app from the same network.
- Spread scheduled jobs over time, and use the batch endpoints instead of one request per record.
Requests without an API key
A request without anAuthorization: Bearer … header never reaches the API. Bot protection answers
with an HTML challenge page and status 429.
So 429 with an HTML body on a first test almost always means the header is missing or malformed.
The value must start with Bearer . Do not retry — fix the header.
Retries
A
500 usually means an error on our side. If the same request keeps failing, the request itself is
the likely cause — stop and check it. A single-order POST /api/v1/orders whose orderNumber
already exists answers 409, and so does a PATCH /api/v1/orders/{id} that changes the order number
to one that already exists.
Whether a replay can create a duplicate depends on the endpoint:
Request reference (x-vercel-id)
Every response carries an x-vercel-id header. It identifies that single request. When you contact
support at help@polygon-one.eu, include:
- the
x-vercel-idvalue, - the time of the request (with time zone),
- the method and path (e.g.
POST /api/v1/orders).
Limits
Timestamps are ISO 8601 (e.g.
2026-10-05T08:00:00.000Z). Record ids are UUIDs. Lists are bare JSON
arrays, except GET /api/v1/evidence ({ "evidence": [...] }).
Not in v1
The following is not available in v1. Any breaking change ships as/api/v2/ with at least six
months of parallel operation (see Versioning).
- Cursor pagination and total counts — page with
limitandoffset. - Webhooks — integrations poll (e.g.
changedSinceon orders, import-job status). - A sandbox environment — every call acts on your live workspace.
- Official SDKs — generate a client from the OpenAPI spec.