Rate limiting and throttling
This article covers where the ReceiptRoller API limits the number of requests, what comes back when you hit a limit, and how to keep your client's traffic reasonable even where there is no limit. The store and business-account APIs you call with your app's access token have no request limit today. Limits exist only on a few individual endpoints, such as receiving a receipt or a printer sending one in.
APIs you call with your app's token
- The
/api/v1/...endpoints you call with an access token (products, sales, reservations and so on) have no request-rate limit, no per-plan limit and no daily quota. - So no rate-limit headers such as
X-RateLimit-Limit,X-RateLimit-RemainingorX-RateLimit-Resetare returned. - The token endpoint
POST /api/v1/auth/tokenhas no limit on exchanging an authorization code (authorization_code) or a refresh token (refresh_token) either.
No limit does not mean any amount of parallel traffic is fine. Follow "Keeping your traffic reasonable" below.
Endpoints with their own limits
These endpoints limit the number of requests in the last minute (the last hour for email). Requests are counted per client IP address, or per the combination in the "Counted per" column.
| Endpoint | Limit | Counted per |
|---|---|---|
POST /api/v1/devices/printer/receipts, POST /api/v1/devices/printer/receipts/{token}/ad-printed | 300 per minute | IP address |
GET /api/v1/apps/{shopId}/ads | 30 per minute | IP address |
GET /api/v1/receipt-ad | 60 per minute | IP address |
GET /api/v1/terminals/{publicTerminalId}/last-receipt, GET /api/v1/rx/{claimToken} | 10 per minute | IP address and terminal (or token) |
POST /api/v1/terminals/{publicTerminalId}/claim, POST /api/v1/rx/{claimToken}/claim | 3 per minute (also counted toward the 10 per minute above) | IP address and terminal (or token) |
POST /api/v1/user/terminals/{publicTerminalId}/claim, POST /api/v1/user/rx/{claimToken}/claim | 3 per minute | IP address and terminal (or token) |
POST /api/v1/rx/{claimToken}/claim with delivery by email | 3 per hour | Destination email address |
Time-clock punches (POST /api/v1/stores/{storeId}/timecards/punch) are not rate-limited, but five wrong PINs in a row for the same person lock that person's punching for 15 minutes, and the call returns 429 with {"error": "pin_locked", "message": "...", "lockedUntil": "..."}.
The sign-in steps of the official ReceiptRoller apps (exchanging an ID token, creating an anonymous account) also have their own limits, but they do not apply to the authorization-code and refresh-token exchanges that developer apps use.
The response when you hit a limit
The status is 429 Too Many Requests. Depending on the endpoint it comes back in one of two ways.
- For printer submissions (
/api/v1/devices/printer/...),GET /api/v1/apps/{shopId}/adsand the 3-per-minute limit on each.../claim, you get aRetry-After: 60header and the text bodyToo many requests. Please try again in a minute. - For the other limits (
last-receipt,GET /api/v1/rx/{claimToken},GET /api/v1/receipt-ad, the 10 per minute on claims, and 3 per hour for email), you get a429with no body and noRetry-After.
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Too many requests. Please try again in a minute.
There is no JSON body or error code. Detect a 429 by its status code.
Keeping your traffic reasonable
- Send bulk work one request at a time. Don't fire hundreds of requests at once with
Promise.allor similar. - Use list endpoints instead of fetching items one by one. For example, the product list
GET /api/v1/productsreturns everything in one response, so a sync fetches it once and matches on your side. - Cache data that rarely changes, such as stores and the product master.
- Use webhooks instead of polling for changes (Receive new sales by webhook and post them to Slack).
Retry guidance
429: ifRetry-Afteris present, wait that many seconds and send again. If it is not, wait about a minute, since the limits count the last minute.503and other5xx, timeouts and connection errors: retry a few times, doubling the wait each time and adding some random jitter.400,401,403,404: sending the same request again gives the same result. For401, refresh the token and retry once.- Resending a create
POSTafter a timeout can create a duplicate. There is no generalIdempotency-Keyheader, so check whether the item was created (for example, in the list) before sending it again.
async function callWithRetry(send, maxAttempts = 5) {
for (let attempt = 1; ; attempt++) {
const response = await send();
const retryable = response.status === 429 || response.status >= 500;
if (!retryable || attempt === maxAttempts) return response;
const retryAfter = Number(response.headers.get("Retry-After"));
const backoff = Math.min(2 ** attempt, 60) * (0.75 + Math.random() * 0.5);
const waitSeconds = response.status === 429 ? (retryAfter || 60) : backoff;
await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
}
}
Related articles
-
Using the Store Information APIA guide to the REST API for fetching and updating a store's basic information (store name, store type, contact details, and address). Lets you implement a store information editing screen from token-authenticated clients such as staff apps.
-
Purchase and receipt data integrationExplains the structure of the purchase and receipt data ReceiptRoller handles, how to retrieve it, related scopes, Webhooks, and common use cases.
-
PosTransactionDto specification — field reference for transaction dataA complete field reference for PosTransactionDto, the canonical model for the transaction data ReceiptRoller handles. For each category — identifiers, dates, amounts, line items, payments, staff, status, and CRM linkage — it summarizes the field names, types, meanings, and how each POS vendor populates them. A reference for developers and external-system integrators. Also referenced from the Smaregi and Square mapping articles.
-
Using the Orders / OMS APIA guide to CRUD operations on the orders under a business account using ReceiptRoller's Orders / OMS API (/api/v1/orders). Covers creating, updating, transitioning status (confirm, process, cancel), and deleting orders, plus the flow for Android / iOS apps and server integrations.
-
Using the Business Hours APIA guide to ReceiptRoller's Business Hours API (/api/v1/stores/{storeId}/business-hours). Covers retrieving and updating per-day business hours, registering special business days (temporary closures and hour changes), configuring a store's workable hours (the upper bound for shift creation), and determining whether the store is currently open.