Rate limits
Plugipay applies per-workspace rate limits to keep the API responsive for everyone. This page covers the limits, the headers we send, and how to handle them in your code.
Default limits
Per-workspace:
| Endpoint class | Limit |
|---|---|
| Read (GET) | 100 requests/second |
| Write (POST/PUT/PATCH/DELETE) | 20 requests/second |
| Heavy operations (export, batch endpoints) | 5 requests/minute |
| Webhook delivery (Plugipay → you) | No limit on our side, backoff on failure |
These are per-workspace, not per-key. All keys on a workspace share the same buckets.
Test and live keys have separate buckets — hammering test mode doesn't affect your live capacity.
Response headers
Every API response includes:
| Header | Notes |
|---|---|
X-RateLimit-Limit |
Bucket size (e.g., 100) |
X-RateLimit-Remaining |
Requests left in the current window |
X-RateLimit-Reset |
Epoch seconds when the bucket fully refills |
When you exceed the limit, you get 429 Too Many Requests plus:
Retry-After: 12
That's seconds to wait before retrying. The number is conservative — you can sometimes retry sooner, but Retry-After is the contractual guarantee.
When you'd hit a limit
Most workspaces never hit limits. The defaults handle moderate-volume merchants comfortably. You'd hit them if:
- Migration scripts. Bulk-importing 100K customers from another provider in a tight loop will saturate writes. Cap parallelism at 4-8.
- Reporting jobs. Pulling a year of payments page-by-page can saturate reads. Use
/exportinstead. - Webhook backfill. If you're re-fetching all events to rebuild local state, throttle to under 100/sec.
How to handle 429
With an SDK
All our SDKs auto-retry on 429 with exponential backoff. You don't need to do anything. If the retry budget is exhausted, the error surfaces normally.
You can tune the retry behavior:
Node:
const plugipay = new PlugipayClient({
keyId, keySecret,
retry: {
maxAttempts: 5,
initialDelayMs: 200,
maxDelayMs: 30_000,
},
});
Python:
plugipay = PlugipayClient(
key_id=..., key_secret=...,
max_retries=5,
)
With raw HTTP
Look for 429, read Retry-After, sleep, retry:
while true; do
resp=$(curl -sS -i ... /v1/customers/cus_xxx)
status=$(echo "$resp" | head -1 | awk '{print $2}')
if [ "$status" != "429" ]; then break; fi
sleep_s=$(echo "$resp" | grep -i '^Retry-After:' | awk '{print $2}' | tr -d '\r')
sleep "${sleep_s:-5}"
done
Exponential backoff (generic)
If Retry-After isn't provided (shouldn't happen, but just in case):
attempt 1: wait 200ms
attempt 2: wait 400ms
attempt 3: wait 800ms
...
attempt N: min(2^N * 200ms, 30s)
Cap the wait at 30 seconds. Add jitter to avoid thundering herd.
Bulk operations — staying under the limit
For batch operations:
# Cap parallelism with xargs -P
plugipay payments list --since 1d --output ids | \
xargs -n1 -P4 -I{} plugipay refunds create --payment-id {} --reason fraudulent
-P4 means 4 parallel refunds. With a 20-write/sec limit, 4 parallel × the latency per refund (a few hundred ms) usually keeps you comfortably under.
If you have a script that needs to do thousands of operations:
- Use idempotency keys so partial-failure retries don't double-process.
- Cap parallelism at 4-8 for writes, 16-32 for reads.
- Watch
X-RateLimit-Remaining— if it drops below 10% of the limit, slow down. - Use
/exportendpoints for read-heavy reporting instead of paginating.
Per-endpoint limits
Some endpoints have stricter or looser limits than the class default:
| Endpoint | Limit | Reason |
|---|---|---|
/v1/uploads |
10/minute | File uploads are heavy |
/v1/payments/export |
1/minute | Streaming export |
/v1/reports/* |
30/minute | Aggregation is expensive |
/v1/payments (read) |
200/sec | Common high-volume endpoint |
If an endpoint deviates from the class default, the per-endpoint limit is documented on its resource page.
Requesting limit increases
If your business genuinely needs more headroom (e.g., processing 50K transactions/day with a need for spiky read access), email hello@plugipay.com:
- What workspace
- What endpoints
- Sustained vs peak rate you need
- A brief description of your use case
We tune limits per-account based on stable usage history. For new accounts, we'd rather see a few days of traffic first — instant requests at sign-up are deferred.
Webhooks and rate limits
Webhooks (events Plugipay sends to you) are not limited on our side. We'll deliver as many as fire.
For your endpoint:
- We time out at 10 seconds. If your handler takes longer, the delivery is marked failed and retried per the webhook retry policy.
- If you're seeing 5xx or timeouts under load, queue events to a worker pool rather than processing inline.
Test mode for high-volume testing
Test mode has the same rate limits as live mode. If you're load-testing an integration, this matters — you can't get higher test-mode throughput without us raising your limits.
If you need temporary high test-mode limits for a load-testing window, let us know in advance — we can grant a 24-hour boost.
Common pitfalls
- Setting retry parallelism without coordination. Two scripts each running 10-parallel adds up to 20 — over the write limit. Coordinate across scripts.
- Treating 429 as a hard failure. It's transient. Always retry with backoff.
- Ignoring
X-RateLimit-Remaining. Watching it lets you slow down preemptively instead of recovering from 429s reactively. - Per-request retry of webhook DELIVERY (not endpoint call). We deliver webhooks; your endpoint's behavior under load is up to you.
Next
- Errors —
RATE_LIMITEDand the broader error catalog. - Authentication
- Idempotency — how to retry safely.