Zilfu caps how often each token and user can call the API, so a runaway script can't overload the platform or get our social integrations flagged for abusive traffic. Normal use — composing, scheduling, listing a queue — never comes close. The ceilings only bite when a client misbehaves.
Two limits apply to API and MCP traffic: a global api ceiling on every authenticated request, and a tighter publish ceiling on the endpoints that create or reshape posts.
The limits
| Limit | Scope | Cap | Keyed by |
|---|---|---|---|
api |
Every /api/* request |
120 / min | API token, falling back to user, then IP |
publish |
Endpoints that create or reshape posts | 30 / min | User |
The two stack: a request to a publishing endpoint counts against both. In practice publish is the ceiling that bites first.
API requests
Every authenticated request to /api/* counts against the api limiter, capped at 120 requests per minute.
The bucket is keyed per personal access token, so each token you create under Settings → API tokens earns its own independent quota. If a noisy automation is eating its budget, moving it onto a second token gives it a fresh 120/min without touching your other integrations.
The MCP server authenticates with the same token as the REST API, so MCP traffic and direct API calls made with one token share a bucket.
Publishing
The publish limiter caps 30 requests per minute per user across:
| Endpoint | What it does |
|---|---|
POST /api/spaces/{space}/posts |
Create posts — immediate, scheduled, or queued |
PUT /api/spaces/{space}/posts/{post} |
Update a post |
POST /api/spaces/{space}/posts/{post}/retry |
Retry a failed post |
POST /api/spaces/{space}/posts/{post}/comment |
Schedule a first comment |
This bucket is keyed by user rather than by token, so splitting a publishing workload across several tokens on the same account does not raise the ceiling. To backfill a large batch of scheduled posts, spread the work across more than one minute.
Hitting a limit
Exceeding a limit returns HTTP 429 Too Many Requests:
{
"message": "Too Many Requests"
}
Every response, throttled or not, carries headers describing the bucket:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the current window |
X-RateLimit-Remaining |
Requests still available in the current window |
Retry-After |
Seconds until the bucket frees up — 429 responses only |
X-RateLimit-Reset |
Unix timestamp of when the bucket frees up — 429 responses only |
Where both limiters apply, the headers describe the one that was hit, usually publish.
Handling a 429
Retrying immediately after a 429 keeps failing and can extend how long you stay throttled. Wait for
Retry-Afterbefore the next attempt.
- Respect
Retry-After. Sleep at least that many seconds before reissuing the request. - Back off exponentially on repeated failures — double the wait each time, cap it somewhere sensible like 60 seconds, and give up after a handful of attempts.
- Watch
X-RateLimit-Remainingand slow down before it reaches zero, rather than waiting for the rejection. - Spread bursty work out. A backlog of 100 posts should be paced, not fired at once.
- Use separate tokens for separate workloads. The
apibucket is per token, so a one-off backfill script on its own token won't starve your production integration.