> ## Documentation Index
> Fetch the complete documentation index at: https://help.polygon-one.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors & limits

> Status codes, error bodies, rate limit, retries and size limits of the Polygon One API

## Status codes

| Code | Meaning | Retry? |
| :- | :- | :- |
| `200` | **OK** — request successful | — |
| `201` | **Created** — one record was created | — |
| `202` | **Accepted** — a batch was staged as an import job; poll `GET /api/v1/import-jobs/{jobId}` | — |
| `400` | **Bad Request** — invalid input or validation error | No — fix the request |
| `401` | **Unauthorized** — the API key is invalid or expired | No — fix the key |
| `403` | **Forbidden** — the key lacks the permission, the module is not enabled, exports are switched off for your workspace (`code: "EXPORTS_BLOCKED"` on downloads), or the account setup is incomplete | No |
| `404` | **Not Found** — the record does not exist or belongs to another company (on evidence and plot links to a supplier article: `403`) | No |
| `409` | **Conflict** — the record collides with existing data, your plot limit is reached (`PLOT_LIMIT_REACHED`), or a confirmation is needed (`warnings`, `overwriteRequired`) | No — change the request |
| `413` | **Payload Too Large** — an async batch (`articles`, `suppliers`, `orders`, `ppwr/quantities`) exceeds 5,000 rows (`batchTooLarge`, with `maxBatchSize`); nothing is created | No — split the batch |
| `422` | **Unprocessable** — the request is valid but cannot be carried out yet: a PPWR volume report that is not declarable (`blockingErrors`), a DDS statement blocked by the compliance check (`compliance_blocked`, `details.blockingArticles`), or a PDF that cannot be rendered | No — fix the data |
| `423` | **Locked** — the uploaded file failed the malware scan (`scan_blocked`) | No |
| `429` | **Too Many Requests** — the [rate limit](#rate-limit) was hit, or the request had [no API key](#requests-without-an-api-key) | Only the JSON form |
| `500` | **Internal Server Error** — usually an error on our side | A few times — see [Retries](#retries) |
| `502` `503` `504` | Gateway or timeout error | Yes |

## Error body

Errors are JSON with an `error` field. Depending on the endpoint, more fields are added:

```json theme={null}
{
  "error": "Validation failed",
  "details": [
    { "code": "too_small", "path": ["address", "street"], "message": "Address is required." }
  ]
}
```

| Field | Content |
| :- | :- |
| `error` | A string — a human-readable sentence or a stable key (see below) |
| `details` | Optional, on a `400` validation error: a list of issues, one per failing field — `path` (the field, as a list of keys), `message`, `code`, plus further keys depending on the issue |
| `warnings` | Optional, `409` only: confirmable warnings, e.g. on [article create](https://help.polygon-one.com/en/api-reference/introduction.md#duplicate-warnings) |
| `code` | Optional: a stable code, e.g. on geometry and intake refusals of the [plot endpoints](https://help.polygon-one.com/en/api-reference/geo-analysis.md#error-codes), or `EXPORTS_BLOCKED` on downloads |

The one exception: on a rate-limit `429`, `error` is an **object**, not a string — see
[Rate limit](#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 `400`s carry an English sentence, also on
  batch imports and PPWR — decide on the status code there. The lists: [batch import error keys](https://help.polygon-one.com/en/api-reference/batch-import.md#error-keys-stable,-non-localized),
  [PPWR error keys](https://help.polygon-one.com/en/api-reference/ppwr.md#error-keys),
  [plot error codes](https://help.polygon-one.com/en/api-reference/geo-analysis.md#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:

```json theme={null}
{ "error": { "code": "rate_limited", "message": "…" } }
```

* 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

| Situation | Retry? | How |
| :- | :- | :- |
| `429` with JSON `rate_limited` | Yes | After the next minute, with exponential backoff and jitter |
| `429` with an HTML page | No | Send the `Authorization: Bearer …` header |
| `500`, `502`, `503`, `504`, network timeout | Yes | Exponential backoff with jitter, and stop after a few attempts; check below whether the call is safe to replay |
| `400`, `401`, `403`, `404`, `409`, `413`, `422`, `423` | No | Change the request first |

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:

| Call | Safe to replay? |
| :- | :- |
| Batch imports (`POST` articles, suppliers, orders as array; `POST /api/v1/ppwr/quantities`) | Yes — rows are upserted on their key; existing rows come back as skipped or updated. See [Batch Import](https://help.polygon-one.com/en/api-reference/batch-import.md#partial-accept-and-idempotency) |
| PPWR master data (`components`, `units`, `formats`, `links`, `article-links`) | Yes — **except unit rows without `internalRef`**, which create a new unit each time. See [PPWR](https://help.polygon-one.com/en/api-reference/ppwr.md#units:-the-reference-trap) |
| `POST /api/v1/articles` with one object | Yes — a repeat answers `409` because the `internalArticleNr` exists. Look the article up with `GET /api/v1/articles?internalArticleNr=…` |
| `POST /api/v1/suppliers` with one object | Yes — a repeat answers `409` (same name or same `externalSupplierId`) |
| `POST /api/v1/orders` with one object | Yes — a repeat answers `409` because the `orderNumber` exists. Before you resend, look the order up with `GET /api/v1/orders?orderNumbers=<nr>`, or send it as a one-row batch (upserted on `orderNumber`) |
| `POST /api/v1/plots` | Yes — the same plot again is recognised and answered with `"deduplicated": true`. See [Idempotency](https://help.polygon-one.com/en/api-reference/geo-analysis.md#idempotency) |
| `POST /api/v1/ppwr/documents` | See [Duplicates and retries](https://help.polygon-one.com/en/api-reference/ppwr.md#duplicates-and-retries) |
| `DELETE /api/v1/suppliers/{id}` | Yes — an id that names no live supplier of your company answers `200` and deletes nothing |

## 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](mailto: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

| Limit | Value |
| :- | :- |
| Rate limit | 1000 requests per 60 seconds per client IP, all `/api` paths |
| Request body | \~4.5 MB (platform limit, applied before the application) |
| Async batch (`articles`, `suppliers`, `orders`, `ppwr/quantities`) | 5,000 rows per request; above that `413 batchTooLarge` with `maxBatchSize: 5000` |
| Sync batch (PPWR master data, `ppwr/tonnage`) | 1,000 rows per request; a unit request also at most 1,000 `sizes` entries in total. Above either cap the response is `400` with `error` `"Too many rows: max 1000 per request"` or `"Too many sizes: max 1000 per request"` (not `413`); nothing is written |
| List endpoints | 100 rows without `limit`; `limit` at most 1000; page with `offset` until a page is shorter than `limit`. There is no total count. Exception: without `limit` and `offset`, `GET /api/v1/evidence` returns every row |
| EUDR evidence | up to 10 MB via `POST /api/v1/evidence/upload-url` (the `PUT` sends exactly `fileSize` bytes); the direct `file` field accepts at most 4 MB (larger → `400`) |
| PPWR documents | PDF, JPEG or PNG, at most 10 MB (the `PUT` sends exactly `fileSize` bytes, otherwise `403`); upload within 10 minutes of requesting the URL |
| Plot geometry | at most 250 parts, 20,000 points, 100,000 ha, 1.0° bounding box per axis — see [Error codes](https://help.polygon-one.com/en/api-reference/geo-analysis.md#error-codes) |
| Import jobs | the input is deleted \~24 hours after the job finishes; the job record is deleted \~30 days after creation |

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](https://help.polygon-one.com/en/api-reference/introduction.md#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](https://help.polygon-one.com/api-reference/openapi.json).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.