
Rate limits and errors on the Zid API: pace your requests, handle 429s and read error responses
How the 60-requests-per-minute limit works, how to back off on 429s, what error responses look like, and how pagination, caching and webhooks keep you under the limit.
The limit: 60 requests per minute, per app, per store
Zid applies a limit of 60 requests per minute per application per store to third-party calls. Because the limit is counted per store, your requests to one store don't use up another store's allowance. Within a single store, though, every job your app runs shares the same allowance.
Zid uses the Leaky Bucket algorithm: incoming requests queue as if poured into a bucket, the bucket leaks at a steady rate, and when it's full additional requests are rejected. Under the throttling policy, excess requests are held and retried with a configurable retry delay and retry limit, then finally rejected if they still don't go through.
- The changelog states that HTTP 429 responses count toward your quota. Retrying immediately in a tight loop keeps the bucket full.
- If you believe your use case needs an exception, Zid asks you to email the details for evaluation.
Reading Zid's responses
The Zid API follows REST principles and standard HTTP status codes. Successful responses use 200, 201, 202 (accepted, processing not complete) or 204 (success, no content). Errors come in this shape:
{
"status": 422,
"success": false,
"error": {
"code": "validation_failed",
"message": "Invalid input data.",
"fields": {
"email": ["Email format is invalid."]
}
}
}400bad_request: check the request syntax and required fields.401unauthorized: authentication is missing or failed. Check your tokens.403forbidden: understood but refused. Check permissions.404not_found,405method_not_allowed,406not_acceptable(adjust yourAcceptheader) and410gone(permanently removed, so clean up any references).422validation_failed: thefieldsobject tells you which inputs failed and why.429too_many_requests: reduce your request rate and retry later.500server_error: retry later, and contact support if it persists.503service_unavailable: wait, then retry.
Not every error has the same shape
Parse errors defensively. Some endpoints and help-centre examples return a different shape, with status: "error" and a message object holding type, code, name and description:
{
"status": "error",
"message": {
"type": "error",
"code": "ERROR_ZID_INVENTORY_ADDRESSES_NOT_FOUND",
"name": "Sorry",
"description": "Inventory address not found"
}
}The docs also show validation errors in Arabic, where even the code value itself is in Arabic (خطأ في التحقق). So base your logic on the HTTP status first, treat code as a helpful hint rather than a guaranteed constant, and don't rely on message text. A small function can handle both shapes:
function readZidError(status, body) {
if (body && body.error) {
return { status, code: body.error.code,
message: body.error.message, fields: body.error.fields || {} };
}
if (body && body.message && typeof body.message === 'object') {
return { status, code: body.message.code,
message: body.message.description, fields: {} };
}
return { status, code: null, message: null, fields: {} };
}Handling 429 and 5xx with backoff
The docs recommend exponential backoff for 429 Too Many Requests. Zid doesn't publish specific delay values, so the numbers below are a starting point, not a Zid rule:
async function callZid(doRequest, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const res = await doRequest();
const retryable = [429, 500, 503].includes(res.status);
if (!retryable) return res;
const cap = Math.min(60000, 1000 * 2 ** attempt); // 1s, 2s, 4s ... max 60s
const wait = cap / 2 + Math.random() * (cap / 2); // add jitter
await new Promise(r => setTimeout(r, wait));
}
throw new Error('Zid API still failing after retries');
}- Only retry what's safe to repeat. Retrying a
GETis harmless; retrying a create can produce duplicates unless you check first. - Don't resend
400,401,403,404or422unchanged. They won't succeed until you fix the request, token or scope. - Treat a
401as a signal to check the token, not to retry. Refreshing is covered in our OAuth guide.
The help centre's advice for "Too Many Attempts, Please slow down for few seconds !" points the same way: divide the requests or schedule them.
Paginate and ask for less
List endpoints are paginated, and parameter names vary from one endpoint to another, so check each one. Some examples:
- List Orders (
GET /v1/managers/store/orders):pageandper_pageare required, with a maximum of 100 records per page.payload_typecontrols response size:simple(the default) leaves out products,tinyreturns essential identifiers only, anddefaultincludes products and is the slowest. - List Customers (
GET /v1/managers/store/customers):pageandper_pageare optional. - Blog endpoints (such as
/v2/blogs/categories/):pageandpage_size.
If a large request times out with a 504, the help centre advises paginating with a smaller page size, such as 50 per page. For recurring syncs, List Orders also accepts date_from, date_to and date_attribute (created_at or updated_at), so you can fetch only what changed since your last run instead of the whole history:
GET /v1/managers/store/orders?page=1&per_page=50&payload_type=simple&date_attribute=updated_at&date_from=2026-09-01T00:00:00.000%2B0000Stop polling: webhooks and caching
Zid explicitly asks partners not to check repeatedly for status updates. For order and payment status, use webhooks: order.status.update and order.payment_status.update push the change to you, so you spend no requests waiting for it. Conditions on order.create and order.status.update narrow deliveries further, as our webhooks guide explains.
- A good practice is to cache data that rarely changes, such as store details and settings, and refresh it on a schedule or when a related webhook arrives.
- Run one queue per store, so a large import for one merchant can't use up the allowance that store's real-time work needs, and put interactive requests ahead of background jobs.
- Log every request and response during development, as the Responses page recommends, so you can see where your allowance goes.
- Test your integration on a development store before launch.
Key takeaways
- Plan for 60 requests per minute per app per store, spread evenly, and remember that 429 responses count toward the quota.
- Back off exponentially on
429,500and503, and never resend validation or auth errors without a fix. - Handle both error shapes, and base your logic on the HTTP status first, not message text; even
codecan arrive in Arabic. - Paginate (up to 100 per page on List Orders), request lighter payloads, and sync by
updated_at. - Replace polling with webhooks, and cache what rarely changes.
Sources
- Rate Limiting
- [Improvement - Apps] Updates to Apps Rate Limiting
- Responses
- Too Many Attempts, Please slow down for few seconds !
- Inventory address not found in calling List Store Locations endpoint
- Store Scripts
- Timeout response in Get Product / Customer endpoint
- List Orders
- List Customers
- List (Blog Categories)
- Webhooks Overview
Start building on Zid
Sign up in the partner portal, then build and test on a development store.


