Rate limits
Complete table of per-IP, per-merchant, and per-partner rate limits.
Overview
JoPay applies rate limits to all API endpoints to prevent abuse and ensure fair usage. When a rate limit is exceeded, the API returns a 429 Too Many Requests response with the error code rate_limited.
Rate limits are enforced at three scopes: per-IP address, per-merchant, and per-partner. Some endpoints have limits at multiple scopes simultaneously.
Per-IP limits
These limits apply based on the caller's IP address, regardless of authentication status. They protect public-facing endpoints from abuse.
| Endpoint | Limit | Window | Notes |
|---|---|---|---|
| Pay page load | 60 | 1 minute | Customer-facing payment page. Prevents scraping. |
| Pay detail / proof | 200 | 1 hour | Payment details and proof submission endpoints. |
| Events (analytics) | 100 | 1 hour | Event tracking endpoints. |
| Fraud reports | 3 | 1 hour | Abuse report submission. Intentionally very low to prevent spam. |
| Proxy (general) | 1,200 | 1 minute | General proxy route limit. Covers all proxied requests. |
Per-merchant limits
These limits apply per authenticated merchant and protect merchant-specific operations.
| Endpoint | Limit | Window | Notes |
|---|---|---|---|
| Create payment request | Configurable | Varies | Set by the partner. Default varies by partner configuration. |
| Send invites | 5 | 1 hour | Merchant-initiated invite emails. |
| Recurring operations | 20 | 1 hour | Create, pause, resume, or cancel recurring plans. |
| Settings updates | 60 | 1 hour | Merchant settings changes (display currency, auto-forward, etc.). |
| Dashboard polling | 120 | 1 minute | Dashboard data refresh. Supports adaptive polling. |
| Proof submission | 30 | 1 hour | Submitting payment proofs (transaction hashes). |
Per-partner limits
These limits apply per partner and protect partner-level administrative operations.
| Endpoint | Limit | Window | Notes |
|---|---|---|---|
| Send invites | 20 | 1 hour | Partner-initiated merchant invite emails. |
| FX rate refresh | 1 | 1 minute | Manual FX rate refresh trigger. Automatic refreshes are not rate-limited. |
| Webhook configuration | 10 | 1 minute | Webhook URL updates and test deliveries. |
Handling rate limits
When you receive a 429 response, the best approach is:
- Do not retry immediately. Wait for the rate limit window to reset.
- Use exponential backoff. If you must retry, wait 1 second, then 2, then 4, etc.
- Reduce request frequency. If you are hitting poll limits, increase your polling interval.
- Check your integration. Hitting rate limits consistently usually indicates a logic issue (e.g., polling in a tight loop).
Rate limit response format
When rate-limited, the API returns:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
{
"error": "rate_limited",
"message": "Too many requests. Please try again later."
}