Rate limits
Limits exist to keep the service responsive for everyone and to make abusive usage obvious in analytics. They are generous enough that a single engineer's workflow never hits them — and structured so real team workloads scale cleanly by spreading across multiple keys.
Per-key limit
- 60 requests per minute per key. Applied on a 60-second rolling window.
- All endpoints count — including
GET /credits. - Limit exhausted →
429 rate_limitedwithRetry-After: 60header.
Per-workspace quotas
- 10 active API keys per workspace. Revoked keys don't count, so rotation is free.
- Monthly credits are set by the workspace's plan — see pricing. Credits are shared across web + API.
How to scale beyond 60 rpm
If you're hitting the per-key limit, create additional keys and spread load across them:
// Create 3 keys in Settings → API Keys
const KEYS = [
process.env.PLANMYSAAS_KEY_1,
process.env.PLANMYSAAS_KEY_2,
process.env.PLANMYSAAS_KEY_3,
]
function pickKey() {
return KEYS[Math.floor(Math.random() * KEYS.length)]
}Each key has its own 60 rpm bucket, so N keys = N × 60 rpm headroom.
Retry strategy
When you get a 429, honour the Retry-After header. Exponential backoff works well for transient 5xx responses:
async function callWithRetry(input, { maxAttempts = 4 } = {}) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const res = await fetch(URL, { method: "POST", headers: HEADERS, body: JSON.stringify(input) })
if (res.ok) return res.json()
if (res.status === 429) {
const wait = Number(res.headers.get("Retry-After") ?? 60) * 1000
await sleep(wait)
continue
}
if (res.status >= 500 && attempt < maxAttempts) {
await sleep(1000 * 2 ** (attempt - 1)) // 1s, 2s, 4s
continue
}
throw new Error(`API failed: ${res.status}`)
}
}Retries that are always safe
Every generation endpoint is idempotent in the sense that retrying never causes a double-charge on a single failed request — failed generations do not deduct credits. A retried request is a fresh generation (you'll get a new, slightly different output).
Concurrency guidance
- Small scripts (1-3 devs): single key is fine. You won't feel the limit.
- CI / batch jobs: dedicated key per pipeline keeps the main dev key fast.
- Agencies / resellers: one key per end-client for clean billing attribution.
Headers you'll see
| Header | Meaning |
|---|---|
X-Request-Id | Unique id for this request. Include in support tickets. |
X-Credits-Charged | Credits deducted on this call (0 on read / error). |
X-Credits-Remaining | Workspace balance after the call. |
Retry-After | Seconds to wait before retrying (only on 429). |
Abuse
Keys that generate abnormal traffic patterns (e.g. 100% failed requests, obvious credential stuffing, scraping patterns) may be staff-revoked. If your key is revoked for this reason, the Revoked reason field on the key will explain the trigger. Reach out at support@planmysaas.com if you think it was a false positive.