Rate limits
Windows slide. Over a limit you get 429 and how many seconds to wait.
| Limit | Per | Counts |
|---|---|---|
| 60 a minute | Key | API requests |
| 600 an hour | Key | API requests |
| 30 an hour | Key | Webhook creates, deletes and tests |
| 5,000 a day | Workspace | Every key and app, API and MCP together |
| 1,500 a minute | IP address | Any request with a bearer token, before the key is read |
The API's per-key windows are its own: a busy integration doesn't use up an AI conversation on the same key.
Over a limit
{
"error": {
"code": "rate_limited",
"message": "Too many requests from this key. Try again in 42 seconds."
}
}Retry-After holds the seconds. Wait that long. There are no headers for what's left: Taseli counts on the server and tells you when to stop.
async function get(url, tries = 3) {
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.TASELI_KEY}` } });
if (res.status !== 429 || tries === 0) return res;
const seconds = Number(res.headers.get("retry-after") ?? 60);
await new Promise((done) => setTimeout(done, seconds * 1000));
return get(url, tries - 1);
}Staying under
- Ad accounts sync once a day, so metrics don't move between syncs.
lastSyncAton an account says when the last one ran. Pull once a day, after it. - Use webhooks for proposals and changes instead of polling.