Rate limits
How many requests each API accepts, how limits are counted and how to back off.
How limits work
Limits protect every store on the platform from a single runaway client. They are counted in fixed one-minute windows and, depending on the endpoint, per client IP, per key or per person. Some endpoints have two limits at once — for example per IP and per publishable key — and a request must stay within both.
Public endpoints that shoppers' browsers call count per shopper IP, so a busy storefront does not run out: each visitor has their own allowance.
Limits by endpoint
This table is built from the API specifications. The endpoint's reference page explains any extra rule (for example the returns lookup locks an order number after 5 failed attempts in 15 minutes).
| API | Endpoint | Limit | Counted |
|---|---|---|---|
| OAuth & OpenID Connect | post/oauth/register | 10 / minute | per IP |
| OAuth & OpenID Connect | post/oauth/token | 60 / minute | per IP |
| OAuth & OpenID Connect | get/api/public/organizations/{slug}/branding | 120 / minute | per IP |
| AI Tools | get/api/v1/ai-tools | 120 / minute | per user |
| AI Tools | post/api/v1/ai-tools/{name} | 120 / minute | per user |
| Collector | post/events | 120 / minute | per IP |
| Collector | post/consent | 120 / minute | per IP |
| Collector | get/config | 120 / minute | per IP |
| Collector | get/size/{product} | 120 / minute | per IP |
| Delivery Promise | get/public/promise | 120 / minute | per IP |
| Returns Portal | get/ | 60 / minute | per IP |
| Returns Portal | post/lookup | 10 / minute | per IP |
| Returns Portal | get/order | 60 / minute | per IP |
| Returns Portal | post/returns | 10 / minute | per IP |
| Returns Portal | get/r/{token} | 60 / minute | per IP |
| Returns Portal | get/r/{token}/label | 60 / minute | per IP |
| Shipment Tracking | get/public/tracking/{token} | 60 / minute | per IP |
| Shipment Tracking | post/webhooks/tracking/{connection} | 600 / minute | per IP |
| WMS T-Data | get/V1/Orders/New | 600 / minute | per key |
| WMS T-Data | post/V1/Orders/Acknowledge | 600 / minute | per key |
| WMS T-Data | post/V1/Orders/Processed | 600 / minute | per key |
| WMS T-Data | post/V1/Orders/Shipped | 600 / minute | per key |
| WMS T-Data | post/V1/Orders/Canceled | 600 / minute | per key |
| WMS T-Data | post/V1/Orders/Cancel | 600 / minute | per key |
| WMS T-Data | post/V1/Stock/Update | 600 / minute | per key |
| WMS T-Data | put/V1/Stock/Update | 600 / minute | per key |
| WMS T-Data | get/V1/Returns/New | 600 / minute | per key |
| WMS T-Data | post/V1/Returns/Acknowledge | 600 / minute | per key |
| WMS T-Data | post/V1/Return/Update | 600 / minute | per key |
| WMS T-Data | post/V1/Returns/Update | 600 / minute | per key |
| WMS T-Data | post/V1/Returns/Canceled | 600 / minute | per key |
| WMS T-Data | get/V1/Labels/Get | 600 / minute | per key |
| WMS T-Data | post/V1/Labels/Add | 600 / minute | per key |
| WMS T-Data | get/V1/Documents/Get | 600 / minute | per key |
| WMS T-Data | post/V1/Catalogue | 600 / minute | per key |
| WMS FFW | get/api/admin/ws/ws_orders | 600 / minute | per key |
| WMS FFW | post/api/aggiorna-giacenze-impegni | 600 / minute | per key |
| WMS FFW | get/api/admin/resi/list | 600 / minute | per key |
| WMS FFW | post/api/admin/resi/notify-reso | 600 / minute | per key |
| WMS FFW | post/api/admin/spedizioni/notify-spedizione | 600 / minute | per key |
| WMS FFW | get/api/admin/documenti/get-order-docs | 600 / minute | per key |
| WMS FFW | get/api/documenti/admin/get-order-docs | 600 / minute | per key |
| WMS FFW | post/api/admin/spedizioni/get-etichette-corriere | 600 / minute | per key |
| WMS FFW | post/api/admin/spedizioni/del-etichette-corriere | 600 / minute | per key |
| WMS FFW | post/api/admin/spedizioni/ws-close-bordero | 600 / minute | per key |
| Content Delivery | get/ | 120 / minute | per IP |
| Content Delivery | get/page | 120 / minute | per IP |
| Content Delivery | get/collections/{collection}/entries | 120 / minute | per IP |
| Content Delivery | get/collections/{collection}/entries/{entry} | 120 / minute | per IP |
| Content Delivery | get/menus | 120 / minute | per IP |
| Content Delivery | get/redirects | 120 / minute | per IP |
| Content Delivery | get/sitemap | 120 / minute | per IP |
| Content Delivery | get/media/{mediaId} | 120 / minute | per IP |
When you hit a limit
Over the limit, Nucleo answers 429 Too Many Requests. Most endpoints add:
| Header | Meaning |
|---|---|
Retry-After | Seconds to wait before trying again |
X-RateLimit-Limit | Requests allowed in the window |
X-RateLimit-Remaining | Requests left in the current window |
Where the header is missing, the body carries the wait in retry_after. Wait at least that long, then retry with exponential backoff and a little jitter:
async function withRetry(doRequest, attempts = 5) {
for (let i = 0; i < attempts; i++) {
const res = await doRequest();
if (res.status !== 429 && res.status < 500) return res;
const header = Number(res.headers.get("Retry-After"));
const body = res.status === 429 ? await res.clone().json().catch(() => ({})) : {};
const wait = (header || body.retry_after || 2 ** i) * 1000 + Math.random() * 250;
await new Promise((r) => setTimeout(r, wait));
}
throw new Error("Nucleo API: too many retries");
}<?php
function withRetry(callable $doRequest, int $attempts = 5)
{
for ($i = 0; $i < $attempts; $i++) {
$res = $doRequest();
$status = $res->getStatusCode();
if ($status !== 429 && $status < 500) {
return $res;
}
$wait = (int) $res->getHeaderLine('Retry-After') ?: 2 ** $i;
usleep(($wait * 1000 + random_int(0, 250)) * 1000);
}
throw new RuntimeException('Nucleo API: too many retries');
}Stay well below the limits
- Batch. The Collector accepts up to 50 events per request; send them together.
- Cache. The delivery promise is already cached by Nucleo for the time the merchant chose; cache CMS content on your side and refresh it on the revalidate webhook.
- Poll calmly. Warehouse interfaces are designed for polling every few minutes, not every second.
- Do not retry
4xxerrors other than429: the same request will fail the same way.