Rate limits
How many requests a key can make — separate budgets for reads, writes and incoming conversions — the headers that show your remaining budget, and how to back off.
Each key has three independent budgets. Using one never eats into another — a dashboard polling stats can’t starve its own pause button, and a burst of conversions from your tracker can’t block either.
| Budget | Applies to | Limit |
|---|---|---|
| Read | every GET | 60 requests per minute |
| Write | every POST, PATCH, DELETE except conversions | 30 requests per minute |
| Conversions in | POST /v1/postback, GET /v1/ingest/{token} | 20 events per second, bursts up to 1,000 |
The limits are token buckets, not fixed windows: the budget refills continuously, and a short burst up to the full minute’s allowance is fine. A typical sync — campaigns, stats, totals — is 3–4 read requests.
Need more for a specific integration? Ask @adsly_pro — limits can be raised per key.
Headers
Every response says where you stand:
X-RateLimit-Limit: 60 the budget for this kind of request, per minute
X-RateLimit-Remaining: 47 requests you can make right now
X-RateLimit-Reset: 1791364800 unix time when the budget is full again
Retry-After: 23 only on 429 — seconds to wait
Doing more with fewer calls
- Bulk instead of loops. Pausing 300 campaigns is one
POST /v1/campaigns/bulk, not 300 pause calls. See Manage campaigns. - Create in batches.
POST /v1/campaignstakes up to 100 campaigns. - Webhooks instead of polling. Status changes arrive the moment they happen; hourly stats arrive by themselves. See Webhooks.
- Batch conversions.
POST /v1/postbackaccepts up to 1,000 events per request (each event still counts against the events budget — batching saves round trips, not quota).
Other limits
| What | Limit |
|---|---|
| Queued or running operations per cabinet | 5 creates + 5 other operations (copy, bulk). The 6th returns 429 QUEUE_FULL. |
| Campaigns per create request | 100 |
| Copies per copy request | 100 |
| Campaigns per bulk request | 1,000 |
| Uploaded file | 10 MB |
Page size for GET /v1/campaigns | 500 |
Backing off
On 429, wait Retry-After seconds and retry. For anything automated, add jitter so many workers don’t retry at the same instant:
async function call(url, init, attempt = 0) {
const res = await fetch(url, init);
if (res.status !== 429 || attempt >= 5) return res;
const wait = Number(res.headers.get('Retry-After') || 1) * 1000;
await new Promise(r => setTimeout(r, wait + Math.random() * 500));
return call(url, init, attempt + 1);
} Updated 2026-10-08