Developers
Rate limits
Frontpage Public API rate-limit classes, windows, Retry-After behavior, CLI exit codes, and which commands hit which bucket.
How limiting works
Every /api/v1/* call is counted against a fail-closed rate limiter (KV-backed). When a bucket is
exhausted the API returns HTTP 429 with envelope code rate_limited and a
Retry-After header (seconds). The CLI maps that to exit code 2.
If the rate-limit store is unavailable, the API fails closed with HTTP 503
(unavailable) rather than allowing unlimited traffic. That is also exit 2 or
5 depending on how the CLI classifies the error — always prefer --json and read
error.code.
Rate limits are separate from plan AI/bandwidth budgets. Hitting a 429 does not mean you are out of AI credits.
Check frontpage usage for plan meters, and this page for request
throttling.
Classes
| Class | Limit | Window | Bucket key |
|---|---|---|---|
RL-AUTH | 30 | 1 minute | Client IP (failed / missing Bearer) |
RL-READ | 120 | 1 minute | API key id |
RL-WRITE-LIGHT | 30 | 1 minute | API key id |
RL-SYNC-PULL | 10 | 1 minute | API key id |
RL-SYNC-PUSH | 6 | 1 minute | API key id (+ site slug) |
RL-PUBLISH | 5 | 10 minutes | API key id + site slug |
RL-AGENT | 20 | 10 minutes | API key id + site slug |
RL-AGENT-CTRL | 40 | 10 minutes | API key id + site slug |
RL-MINT | 5 | 24 hours | User id (session cookie — key creation in the editor) |
Successful authenticated calls also consume an RL-AUTH check during key verification, then the
route’s success class (for example RL-READ). Failed auth is charged only to
RL-AUTH by IP so credential stuffing cannot burn a valid key’s read budget.
CLI command → class
| CLI commands | Class |
|---|---|
whoami, sites, doctor, ls, cat,
search, status, diff (one list read; blobSha compare),
history,
runs / runs get / runs list / runs current,
usage, analytics (dashboard GET + ops POST), pages,
modules (list|docs), extensions (list|docs), fonts,
media list, inbox (read), seo get, articles list,
articles content get, settings get | RL-READ |
pull | RL-SYNC-PULL |
push, tokens set | RL-SYNC-PUSH |
edit, seo set, settings set, media upload,
articles mutations,
modules / extensions enable|disable|config|archive|request-setup,
inbox … --mark-read, revert | RL-WRITE-LIGHT |
prompt (enqueue a new agent run) | RL-AGENT |
runs cancel, runs retry | RL-AGENT-CTRL |
publish, unpublish | RL-PUBLISH |
| Create key in editor (Developers → Create key) | RL-MINT (5 / 24h per user) |
runs wait and deploy wait poll with read-class GETs. Aggressive tight loops can exhaust
RL-READ — back off using the wait helpers rather than spinning your own 10ms poll.
429 response shape
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
"ok": false,
"error": {
"code": "rate_limited",
"message": "Too many requests. Try again later."
}
}
With the CLI and --json, failures still set the process exit code. Expect exit 2 for
rate limits. Honor Retry-After (or wait at least the class window) before retrying.
frontpage pull --json
# ok:false, error.code === "rate_limited" → sleep, then retry
echo $? Key mint limit (RL-MINT)
Creating API keys is limited to 5 per user per 24 hours. Minting happens only from the editor session (profile → Developers), not via Bearer. If you hit the mint cap, wait for the window or delete unused keys and reuse an existing secret where policy allows.
Practical guidance for agents
-
Prefer
pullonce per work session (or whentipDriftis true); do not pull in a tight loop (RL-SYNC-PULLis 10 / min). -
Use
diff --jsonfreely for dirty checks — it is oneRL-READlist call (git blob SHA compare), not one read per file. - Batch local edits, then a single
push(6 / min). - Do not fire many
promptcalls in parallel (20 / 10 min per site). - On exit 2, check whether it is rate-limit vs plan budget (
frontpage usage).
See also Errors & exit codes and Analytics & usage.