Rate limits
Limits are enforced per API key, not per client and not per IP. Two keys held by the same integration get their own budgets.
The default limits
Section titled “The default limits”| Window | Requests |
|---|---|
| Per minute | 100 |
| Per day | 10,000 |
These are the defaults attached to a newly issued key. A key can be issued with higher limits — ask, with a number and the reason, and we will set it on the key rather than making you thread a workaround through your code.
Exceeding either window returns:
{ "type": "https://developers.vaki.co/errors/rate_limit_exceeded", "title": "Too many requests", "status": 429, "code": "rate_limit_exceeded", "detail": "Limit of 100 requests per minute exceeded for this API key.", "instance": "/v1/checkout_links"}Response headers
Section titled “Response headers”The contract is the standard trio, on every response:
| Header | Meaning |
|---|---|
RateLimit-Limit | Requests allowed in the current window |
RateLimit-Remaining | Requests left in the current window |
RateLimit-Reset | Seconds until the window resets |
Backing off
Section titled “Backing off”Exponential backoff with jitter, capped, with a maximum number of attempts. The jitter matters: without it, every client you have retries in lockstep and recreates the spike you are backing off from.
async function callVaki(request, attempt = 0) { const res = await fetch(request);
const retryable = res.status === 429 || res.status >= 500; if (retryable && attempt < 5) { const base = Math.min(2 ** attempt * 500, 8000); // 0.5s → 8s, capped await sleep(base + Math.random() * 500); // jitter return callVaki(request, attempt + 1); }
return res;}Never retry a 400, 401, 403, 404, 415 or 422 — the answer will not
change and you are spending budget to be told the same thing. See
Errors.
How to not need the limit
Section titled “How to not need the limit”Most 429s we see are self-inflicted, and all three causes are cheap to fix.
Do not poll on a timer over everything you have ever created. Poll a link when
the donor returns from checkout, then a handful of times with backoff, then stop.
A link at completed, expired or cancelled is terminal — never poll it again.
Cache what does not change. GET /v1/vakis/{key} for a published cause is
effectively static. Cache the need→vaki-key mapping in your own database at
creation time rather than resolving it per donation.
Do not fan out. A nightly reconciliation that walks 10,000 donations in parallel will exhaust the daily budget in one burst and starve your live checkout traffic. Walk it serially with a small concurrency limit and only for donations still in a non-terminal state.
Before a traffic spike
Section titled “Before a traffic spike”A launch video, a media hit or a telethon is a burst, and a 429 during the one
hour that matters is the worst possible time for one. Two weeks is plenty of
notice; the day before is not.
Mail soporte@vaki.co with:
- the date and time window, with the timezone spelled out;
- expected peak donations per minute;
- your API key’s name (not the key itself, ever).
We raise the limit on your key for the window. There is no self-service control for this yet.