Rate limits

Per-key rate buckets, the headers you can read, and back-off recipes.

Spawn rate limits are enforced per API key on two sliding windows: a 60-second bucket and a 3,600-second (1 hour) bucket. Hitting either one returns 429 rate_limited with a Retry-After header.

Read-only endpoints (GET agents, GET memory) have generous default limits and rarely hit them in practice — the limits you'll actually feel are on agent spawns.

Spawn limits per plan

| Plan | Spawns / minute | Spawns / hour | | --- | --- | --- | | Free | 5 | 30 | | Pro | 30 | 500 | | Scale | 100 | 5,000 |

These reset on a rolling window, not a fixed boundary. If you spawn 5 agents in 10 seconds on the Free tier, the 6th waits about 50 seconds before it gets through.

Per-key, not per-account

Multiple keys on the same account each get their own bucket. So a CI key and a production key don't compete. This also means you can't game limits by rotating keys faster — the underlying account quota (agent-hours/month) still applies.

Response headers

Every rate-limited response includes:

text
HTTP/2 429
Retry-After: 12
Content-Type: application/json

Retry-After is the number of seconds until the current window rolls over. Always honor it.

Successful responses don't currently include X-RateLimit-Remaining style headers — they're on the roadmap for a future release.

Error response

json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many spawn requests. Try again in 12s.",
    "details": {
      "plan": "pro",
      "window": "minute",
      "limitPerMinute": 30,
      "limitPerHour": 500
    }
  }
}

details.window tells you whether you tripped the minute or the hour bucket. The hour bucket is what hits users running batch jobs.

Back-off recipes

Honor Retry-After (minimum viable)

bash
spawn() {
  resp=$(curl -sS -w '\n%{http_code}\n%header{Retry-After}' \
    -X POST https://jettson.dev/api/v1/agents \
    -H "Authorization: Bearer $JETTSON_API_KEY" \
    -H "Content-Type: application/json" -d "{\"task\": \"$1\"}")
  status=$(echo "$resp" | tail -2 | head -1)
  if [ "$status" = "429" ]; then
    retry=$(echo "$resp" | tail -1)
    echo "rate-limited; sleeping $retry s"
    sleep "$retry"
    spawn "$1"
  fi
}

Bulk-spawn pacing

If you're spawning a large batch, throttle ahead of time rather than colliding with the rate-limiter:

bash
# Pro tier — 30/min. Spawn every 2.1s with a small jitter.
for task in "${tasks[@]}"; do
  spawn "$task"
  sleep $(awk -v min=2 -v max=2.5 'BEGIN { srand(); print min + (max-min) * rand() }')
done

Exponential back-off for 5xx

Rate limits aren't the only thing that returns Retry-After. 503 temporarily_unavailable and 503 mind_unavailable set it too. The same handler covers both.

Lifting the cap

If your use case legitimately needs higher limits — most often a Scale customer running a heavy batch job — email support@jettson.dev. Per-key rate-limit overrides are on the roadmap; we're happy to grant them ad-hoc in the meantime.