Merchant API rate limits
July 30, 2026
FlowAlp Pay Merchant API rate limits: 600 requests per 5 minutes, 405/403 signals under load, and a retry strategy with backoff.
The Merchant API applies a request quota so that all merchants get predictable performance. Design your integration to stay well below the limit and to slow down gracefully when it is reached.
The limit
| Property | Value |
|---|---|
| Quota | 600 requests per 5 minutes |
| Scope | All Merchant API requests |
| Enforcement | Web application firewall at the platform edge (AWS WAF) |
The quota covers bursts as well as sustained traffic: 600 requests in five minutes corresponds to an average of two requests per second.
What happens when you exceed it
| Signal | Meaning | Recommended action |
|---|---|---|
405 Method Not Allowed | Typical first signal from the platform edge | Pause, then retry with backoff |
403 Forbidden | Follow-up block while the limit stays exceeded | Stop the burst; increase delays substantially |
Both codes also occur for unrelated reasons — a wrong HTTP verb or missing permissions. Treat them as rate-limit signals only when they correlate with high request volume; the full status semantics are in Merchant API errors.
Backoff strategy
Retry with exponential backoff and jitter:
| Parameter | Suggested value |
|---|---|
| Initial delay | 500 ms |
| Multiplier | 2.0 per attempt |
| Jitter | random 0–300 ms added per attempt |
| Maximum delay | 30 s |
| Maximum attempts | 5 |
async function withBackoff(fn, { maxAttempts = 5, baseMs = 500, maxDelayMs = 30000 } = {}) {
for (let attempt = 1; ; attempt++) {
try {
return await fn();
} catch (err) {
const retriable = [403, 405, 429].includes(err.status) || err.status >= 500;
if (!retriable || attempt >= maxAttempts) {
throw err;
}
const delay =
Math.min(baseMs * 2 ** (attempt - 1), maxDelayMs) + Math.random() * 300;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
}<?php
$delayMs = 500;
for ($attempt = 1; $attempt <= 5; $attempt++) {
[$status, $body] = sendFlowAlpRequest(); // your HTTP call
if ($status < 400) {
break;
}
if (!in_array($status, [403, 405, 429], true) && $status < 500) {
throw new RuntimeException('Non-retriable error: HTTP ' . $status);
}
usleep(($delayMs + random_int(0, 300)) * 1000);
$delayMs = min($delayMs * 2, 30000);
}Reduce your request volume
- Use webhooks instead of polling payment status in a loop.
- Cache slow-changing data such as the provider list from PaymentProvider.
- Aggregate work: fetch lists with one request instead of fetching entities one by one.
- Spread scheduled jobs such as reconciliation over time instead of firing them all at the top of the hour.
Never react to 403/405 with an immediate tight-loop retry — that keeps the block active. Always increase the delay between attempts.
Next: harden your error handling with Merchant API errors and set up webhooks.