Rate limiting
Control the rate and concurrency of Raims API calls and handle 429 responses with bounded retries.
The API limits how often you send calls and how many requests remain in
progress. Your integration should control both, handle 429 Too Many Requests and spread work over time.
Use this guide when preparing an integration, investigating refusals due to excess traffic or planning higher volume. The limits listed here apply to the Raims API; endpoint-specific rules must also be respected.
Shared limits
The allowance belongs to the company and environment identified by the API key. All keys for the same company share the limits in that environment, even when calls come from different servers, processes or applications. Creating or rotating a key does not increase the allowance. Production and Sandbox have separate allowances.
Each endpoint has an additional rate limit. Its HTTP method and path in the
reference identify the endpoint: GET /v1/charges/{id} shares the same
allowance across all IDs. GET /v1/charges and POST /v1/charges have separate
endpoint allowances and share the company's total allowance in the environment.
Coordinate usage across your integration's components. Local control in each process alone does not control their combined traffic.
API limits by company and environment
The values below are the default limits. They may vary according to your company's configured limits.
| Limit | Production | Sandbox |
|---|---|---|
| Total requests per second | 100 | 25 |
| Requests per second for each endpoint | 25 | 25 |
| Total requests in progress | 50 | 50 |
The limits apply together. In Production, for example, the total limit of 100 requests per second does not allow that volume to be concentrated on an endpoint limited to 25. In Sandbox, calls to different endpoints also share the total limit of 25.
Treat them as usage ceilings, rather than a permanent sending target. An HTTP call may query or change a resource; the allowance does not represent a number of payments processed per second.
Rate and concurrency
Rate is how many calls are sent over time. Concurrency is how many have not finished. A slow query continues to occupy capacity even when no new calls are sent.
For example, sending ten calls per second that each take five seconds can keep about fifty calls in progress. Reducing the rate alone does not immediately release calls that are already running.
Spread out call start times and limit how many can remain in progress.
Avoid sending an entire batch at once. Short bursts can receive 429 even
when the observed average is below the limit; do not use the transition
between seconds to concentrate two batches.
Understand the 429 response
429 RATE_LIMITED means the request was refused due to excess traffic and
was not executed. The response tells you how long to wait and which control
to adjust:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Cache-Control: no-store
Retry-After: 1
Raims-Rate-Limited-Reason: endpoint-rate
{
"error": {
"code": "RATE_LIMITED",
"message": "The API request limit was exceeded. Wait for Retry-After and reduce the request rate or the number of concurrent requests."
}
}Retry-After is a positive integer number of seconds. Wait at least that
long before retrying. The value 1 above is an example; always use the value
received in the response.
Raims-Rate-Limited-Reason | What to adjust |
|---|---|
global-rate | Reduce the company's total call rate in the environment. |
endpoint-rate | Reduce calls to the same endpoint, including different IDs. |
global-concurrency | Reduce the company's total simultaneous calls in the environment. |
endpoint-concurrency | When enabled for the company, reduce simultaneous calls to the same endpoint. |
resource-specific | Follow the resource's own interval identified by error.code. |
Some endpoints have their own limits and waiting intervals in addition to the API's general limits. See each endpoint's reference for the applicable rules.
Make decisions using the status, error.code and the header reason. The
text in error.message helps with diagnosis and must not be compared in
your system's logic. General errors do not need error.details.
Wait before retrying
When you receive 429, preserve pending work and reschedule the attempt:
- Read
Retry-Afterand the reason for the limit. - Reduce the rate or concurrency according to the reason.
- Wait at least the received interval. After further refusals, increase the delay and add a small random variation, called jitter.
- Set a maximum number of attempts and an overall deadline. If either is reached, stop the current attempt and preserve the work for later handling.
One possible rule is to use the greater of Retry-After and an increasing
delay, then add jitter. Never shorten the server's waiting interval to fit
a local deadline: if it exceeds that deadline, defer the work instead of
retrying early.
Do not restart all refused calls together when the waiting interval ends. Use a queue and maintain the outgoing rate during retries as well. Keep retry handling in one place so that different application layers do not repeat the same call simultaneously.
For calls that create or change data, or when a connection ends without a response, check the result before submitting again. Losing a response does not prove that the request was not processed. Do not recreate a charge to resolve uncertainty about the previous one.
Queued query example
This Node.js example retrieves a customer using GET. It keeps one call in
progress per process, waits 100 ms between items and makes at most four
attempts when it receives 429. Each attempt has a timeout of up to 30
seconds; an item's execution deadline is two minutes, starting when it
leaves the queue. These values are example choices, not additional API limits.
Store the key in RAIMS_API_KEY on your integration's backend. Pass a
customer ID from the same company and environment as the key. Do not run
the code in a browser or write the key to logs.
const apiKey = process.env.RAIMS_API_KEY;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
let queue = Promise.resolve();
function retrieveCustomer(id) {
const task = queue.then(() => retrieveWithRetry(id));
queue = task.catch(() => {}).then(() => sleep(100));
return task;
}
async function retrieveWithRetry(id) {
if (!apiKey) throw new Error('RAIMS_API_KEY is required.');
const deadline = Date.now() + 120_000;
for (let attempt = 0; attempt < 4; attempt += 1) {
const remaining = deadline - Date.now();
if (remaining <= 0) throw new Error('Request deadline exceeded.');
const response = await fetch(
`https://api.raims.com/v1/customers/${encodeURIComponent(id)}`,
{
method: 'GET',
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: 'application/json',
},
redirect: 'error',
signal: AbortSignal.timeout(Math.min(30_000, remaining)),
},
);
if (response.ok) return response.json();
const retryAfter = response.headers.get('Retry-After');
await response.body?.cancel();
if (response.status !== 429) throw new Error(`HTTP ${response.status}`);
const seconds = /^[1-9]\d*$/.test(retryAfter ?? '')
? Number(retryAfter)
: NaN;
if (attempt === 3 || !Number.isSafeInteger(seconds)) {
throw new Error('Rate limit: stop and reschedule the request.');
}
const delay =
Math.max(seconds * 1000, 500 * 2 ** attempt) +
Math.floor(Math.random() * 250);
if (delay >= deadline - Date.now()) {
throw new Error('Retry-After exceeds the remaining request deadline.');
}
await sleep(delay);
}
}The function returns an error to its caller when the deadline expires,
the header cannot be interpreted or another failure occurs. It does not
automatically retry timeouts, connection errors or HTTP responses other than
429. Handle those results in your application and preserve work that needs
to be rescheduled.
This queue is local. When running multiple instances, coordinate their combined usage and limit pending item counts and time spent in the queue. The example does not replace that shared control or provide a complete API client.
Avoid unnecessary calls
Use webhooks to receive supported changes instead of continuously querying the same resource. Validate the signature and handle redeliveries without repeating the effect. Query the resource when you need to retrieve its current state; do not depend on event order.
For lists, apply available filters, process each page and advance only
while page.has_next is true. Copy page.next_after to after, keeping
the filters. Do not restart the entire list on each attempt. See
Listing and pagination.
Separate batch query tasks from calls needed for payment. Reserve capacity for interactive use and avoid repeating queries whose results your application can still use.
Diagnose and request a review
Measure the total and per-endpoint call rate, simultaneous calls, response
duration and refusals by reason. Record the method, endpoint template,
environment, time, HTTP status, error.code, reason for the limit,
Retry-After and number of attempts. Use aggregate metrics.
Do not record API keys, authentication headers, bodies containing personal
data or sensitive links. For diagnosis, using the /v1/charges/{id} template
avoids recording each charge's identifier in the metric.
If refusals persist, check usage across all applications for the same company and environment. To discuss higher limits for your integration's needs, contact your account manager through the Raims Dashboard chat or email integracoes@raims.com. Include the endpoints, expected volume, bursts, concurrency, latency and busiest times. Do not send your key. The request will be reviewed; contacting us does not confirm an increase.