Rate Limits
Von Payments enforces rate limits via sliding-window counters. Some buckets are counted per-IP and some per API key — the table says which for each. Two routes are metered on both axes at once (session create, and the public session read), so a request can be refused by either.
Buckets
| Bucket | Endpoint(s) | Limit |
|---|---|---|
sessions | POST /v1/sessions, POST /api/sessions | 10 / 60s per IP |
sessionsPerKey | POST /v1/sessions (per-API-key axis) | 30 / 60s per key |
paymentOps | POST /v1/payment_intents (+ /{id} sub-routes: retrieve, capture, void), POST /v1/refunds, POST /v1/tokens, POST /v1/public/tokens, /v1/buyers + /v1/buyers/{id} | 100 / 60s per key |
sessionRead | GET /v1/sessions/:id, GET /v1/capabilities, GET /api/checkout/session | 30 / 60s per IP |
publicSessionRead | GET /v1/public/sessions/:id, POST /v1/public/binder-load | 1000 / 60s per key |
publicSessionReadPerIp | the same two routes (per-IP axis) | 80 / 60s per IP |
publicWebhookRead | GET /v1/webhook_subscriptions + /{id}, GET /v1/webhook_events/{id} | 100 / 60s per key |
webhook_subscription_writes | POST / PATCH / DELETE /v1/webhook_subscriptions (+ sub-routes) | 30 / 60s per key |
webhookListenSseConnections | GET /v1/webhook_listen | 30 / 60s per key |
sdkTelemetry | POST /v1/sdk-telemetry | 30 / 60s per key |
browserTelemetry | POST /v1/public/telemetry/browser | 30 / 60s per IP |
checkoutInit | POST /api/checkout/init, POST /api/checkout/complete | 20 / 60s per IP |
webhooks | POST /api/webhooks/* (provider inbound) | 100 / 60s per IP |
clientError | POST /api/checkout/client-error, POST /api/csp-report | 10 / 60s per IP |
admin | POST /api/admin/*, POST /api/merchant-accounts, /api/cron/* | 5 / 60s per IP |
healthDeep | GET /api/health?deep=true | 5 / 60s per IP |
Shallow GET /api/health (no deep param) is intentionally unmetered — it's what uptime monitors hit.
A session-create request can be rate-limited on either the per-IP sessions bucket or the per-key sessionsPerKey bucket. The per-IP rejection emits rate_limit_exceeded; the per-key rejection emits rate_limit_exceeded_per_key so SDKs can tell them apart and react differently.
The public session read is metered the same way, and the two axes are far apart: 1000/min against your publishable key, but only 80/min from a single IP. The per-key ceiling is generous because a publishable key is shared by every buyer on your site; the per-IP one is what a single browser — or a load test running from one machine — will hit first. If you are polling session state from the browser, that 80 is the number that matters.
POST /v1/public/binder-load shares BOTH of those buckets, not just the per-key one. It routes to the same limiter as the session read, and the per-IP axis is applied to that limiter rather than to the individual route — so binder-load calls and session reads from one browser draw down the same 80/min allowance. Budget them together.
Response headers
X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset are emitted on both successful responses and 429s — the proxy sets them when it admits a request and when it rejects one. Retry-After is emitted on 429 responses only.
| Header | When | Description |
|---|---|---|
X-RateLimit-Limit | success + 429 | Maximum requests allowed in the current window |
X-RateLimit-Remaining | success + 429 | Requests remaining in the current window (0 on a 429) |
X-RateLimit-Reset | success + 429 | Unix epoch seconds when the window resets |
Retry-After | 429 only | Seconds to wait before retrying |
Handling 429
{
"error": "Too many requests",
"code": "rate_limit_exceeded",
"fix": "Too many requests — wait and retry (see Retry-After header)",
"docs": "https://docs.vonpay.com/reference/api#rate-limits"
}
The error envelope is flat (not nested). The example above shows the fields you'll branch on (error, code, fix, docs); every error response also carries a selfHeal object — see Error Codes for the full envelope.
SDK auto-retry
The Node and Python SDKs automatically retry on 429 and 5xx responses with exponential backoff:
- The SDK reads
Retry-Afterwhen present - Retry delay capped at 60 seconds
- Default
maxRetriesis 2 — configurable via the constructor
Hitting the per-key ceiling
If you legitimately need to exceed 30 session creates/minute per API key (e.g. high-volume batch fulfillment), contact support. The per-key limit is platform-protective, not commercial — we can raise it for real integrations.