How-to

Rate limits

A 429 did nothing. Wait the delay it gives, then send it again. The SDKs do it for you.

  1. 429 rate_limit_exceededA request past a limitNothing was done. Retry-After says how long to wait
  2. Wait Retry-After secondsThenSooner is refused again
  3. Send it again, once ✓ThenA create keeps its Idempotency-Key

The SDKs retry 429s with growing waits; set how many times on the client (the retry code).

Without an SDK

Every 429 answers this body, and the same delay in the standard Retry-After header (whole seconds):

A 429 body

{  "error": "Too many requests, please try again later",  "code": "rate_limit_exceeded",  "details": {    "limit": 60,    "periodSeconds": 60,    "retryAfterSeconds": 12  }}
  1. details.limit: The requests allowed per period.
  2. details.periodSeconds: The period's length.
  3. details.retryAfterSeconds: How long until a request can succeed again, rounded up.

Wait at least Retry-After, then retry once, with the same Idempotency-Key for a create. Sooner is refused again.

The limits an API key can meet

WhatCounted perLimit
Every route (except the health probes /healthz, /internal/healthz)client IPthe deployment's global limit, checked before the credential
Currency lookups (GET /currencies, GET /currencies/:id)client IP60 a minute
Invitations (POST /invitations)organization (both modes)50 an hour
Webhook test deliveries (POST /webhooks/endpoints/:uuid/test)space30 an hour
Webhook resends (POST /webhooks/events/:id/resend)space100 an hour

Every route also shares the deployment's global limit per client IP, checked before the key.

Stay under them

  • Don't poll for payments. Webhooks tell you when something happens.
  • Cache the currency catalog. It changes rarely, and its lookups are limited per IP.
  • Servers behind one IP share its limits, and a space's keys share its per-space limits.