Limits and retries
This page explains how many requests you can make, what happens when you go over the limit, and how to retry write calls without creating duplicates.
Rate limit
Every API token may make 600 requests per minute. The limit is counted per token, so two integrations with their own token do not share a budget.
Requests without a token (for example a failed login attempt) are counted per IP address with a lower limit of 60 requests per minute.
Every response carries the current state of your budget:
- Name
X-RateLimit-Limit- Type
- integer
- Description
The number of requests allowed per minute.
- Name
X-RateLimit-Remaining- Type
- integer
- Description
The number of requests you have left in the current window.
Inbound webhook endpoints that Walnut exposes for third parties are not part of this limit.
Over the limit: 429
When you exceed the limit the API answers 429 Too Many Requests and does not process the request. The response tells you when to try again:
- Name
Retry-After- Type
- integer
- Description
The number of seconds to wait before sending the next request.
- Name
X-RateLimit-Limit- Type
- integer
- Description
The number of requests allowed per minute.
- Name
X-RateLimit-Remaining- Type
- integer
- Description
0while you are limited.
429 response
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
Content-Type: application/json
{
"message": "Too many requests. Slow down and retry after the number of seconds in the Retry-After header."
}
A request that was answered with 429 has had no effect, so it is always safe to send it again.
How to retry
- Wait at least
Retry-Afterseconds before the next request, then continue. - For
5xxerrors and network timeouts, retry with exponential backoff (1s, 2s, 4s, ...) with a bit of random jitter, and give up after a handful of attempts. - Do not retry other
4xxerrors, they will not succeed without a change to the request. - For bulk work, use the batch endpoints (for example
POST /products/batchorPOST /tags/members/bulk) instead of many single calls.
Idempotency-Key
A timeout on a POST, PUT or PATCH leaves you unsure whether the call was processed. Add an Idempotency-Key header with a unique value (a UUID works well) and you can repeat the exact same call safely:
Request with an idempotency key
curl -X POST https://api.walletapp.co/passes/scan \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "Authorization: Bearer <your_token>" \
-H "Idempotency-Key: 6f1c2f9e-3b1a-4f0e-9a52-0d7d1f2c8a11" \
-d '{ "pass_code": "<pass_code>", "brand_id": "<brand_id>" }'
The header is optional and only applies to POST, PUT and PATCH. Without it nothing changes.
- Name
Same key, same request- Type
- replay
- Description
The first response is returned again with the header
Idempotent-Replayed: true. The action is not executed a second time.
- Name
Same key, different request- Type
- 422
- Description
Answered with
422and"error": "idempotency_key_reuse". Use a new key for a new request.
- Name
Same key, first request still running- Type
- 409
- Description
Answered with
409and"error": "idempotency_in_progress"plus aRetry-Afterheader. Wait and send the same request again.
- Name
Invalid key- Type
- 400
- Description
The key must be 1 to 255 characters (
"error": "idempotency_key_invalid").
Rules
- A key is scoped to your token, the HTTP method and the path. The same value on another endpoint or with another token is a different key.
- "Same request" means the same body and query string.
- Responses are kept for 24 hours. After that the key can be used again.
- Only successful (
2xx) responses are stored. If a request fails with a4xxor5xx, the key is released and you can fix the request and retry with the same key. - Some endpoints are idempotent in their own right, for example the
transaction_referenceonPOST /passes/scan/storecard. Those keep working as before; the header is an extra safety net on top, and you do not need both.
Reuse with a different body
HTTP/1.1 422 Unprocessable Entity
{
"message": "This Idempotency-Key was already used with a different request payload.",
"error": "idempotency_key_reuse"
}
