Skip to main content

Status codes

Error body

Errors are JSON with an error 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[].error and invalid[].error in PPWR sync results, failed[].error.key in async job results, and the job’s error (a key, or a JSON string whose key field is the stable part). The plot endpoints send a stable code next to the sentence on geometry and intake refusals. Match on these. Envelope and validation 400s 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), error is an English sentence. Its wording may change — decide on the status code (and warnings[].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-After header 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 an Authorization: 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-id value,
  • the time of the request (with time zone),
  • the method and path (e.g. POST /api/v1/orders).
Log the header on every failed call, so you have it when you need it.

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 limit and offset.
  • Webhooks — integrations poll (e.g. changedSince on orders, import-job status).
  • A sandbox environment — every call acts on your live workspace.
  • Official SDKs — generate a client from the OpenAPI spec.