Limits
Every ceiling a live Service meets, where it is set and what happens at it. Requests per key, AI per job and per day, file sizes, the buyer's own bounds and your plan's limits. At every limit nothing already running stops, and each answer names its next move.
A limit answers one question: what happens at the ceiling? The same rule holds at every one of them: nothing already running stops, and the answer says what to do next, in the same shape every other answer has.
| Limit | Default | At it | Set by |
|---|---|---|---|
| writes per key | 25 a second live, 10 in test | 429 rate_limited, with Retry-After and next | Brand.limits.writes, up to 100 |
| reads per key | 100 a second live, 25 in test | 429 rate_limited | Brand.limits.reads, up to 500 |
| AI per job | a fifth of the price | ai.exhausted, then 'ask', 'person' or 'end': the buyer is never billed for AI | price.ai |
| AI per day, the whole brand | none | AI steps wait with ai.capped until the next day or a raise; deadlines keep counting | Brand.ai.daily |
| a file | 50 MB, and 500 MB a job | 413 file_too_large, naming the field | — |
| what a buyer will pay | the order's max_usd | OFFER with the price, never a charge past it | the buyer |
| what an agent may spend | its delegated key's cap | OFFER, and hand_to_person past the cap | the party who delegated it: dlg_ |
| a member's load | the Pool's integrity limits | the Task goes to the next eligible member | integrity |
| members, tenants, regions, audit log | your plan | OFFER for the plan that seats the next one | your plan's limits |
A limit set in a file is checked by check before a deploy, so a ceiling you write never surprises a buyer.
A seller's agent loads its pipeline into api.forsale in one burst. The 26th write in a second answers:
curl -i https://api.forsale/deals -H "authorization: Bearer sk_live_…" -d @account-26.json # the 26th write this second{
"type": "BLOCKED",
"category": "rate_limit",
"code": "rate_limited",
"reason": "This key sent 26 writes in one second; it may send 25.",
"param": null,
"next": { "do": "retry", "after_seconds": 1 },
"doc_url": "https://api.forsale/docs/errors/rate_limited",
"request_id": "req_9Fw2Lk",
"simulated": false
}Every answer carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, so a client slows before it is refused. An order sent again with the same Idempotency-Key never acts twice, so retrying is always safe.
npx services.dev limits --liveapi.forsale · live · Team
writes 25 a second per key · the busiest key used 25 at 14:02:11 · 3 answered 429
reads 100 a second per key · peak 41
AI $40.00 a deal, from deal.ts's price.ai · 2 deals reached it this month · ai.exhausted asked you to fund each one, in the console
AI, all none set · this month $3,912.40 on 1,204 deals
members 50 of 50 in closers, a Pool you own · 2 applicants waiting · Scale seats any number
files 50 MB a file · 500 MB a deal
at a limit nothing running stops: a write retries, a deal waits on your answer, an applicant waits for a seatapi.forsale's closers Pool is full on Team. The next verified closer waits on the door, told only that a seat will open, and the console carries the OFFER:
open https://api.forsale/console/limits limit closers · 50 of 50 members · Team's limit for a Pool you own · 2 verified applicants waiting
offer OFFER · Scale · because closers is at its member limit · cure: upgrade to Scale, or let a member go before you seat the next
members the applicants read "verified · you'll hear when a seat opens" on closers.deals, and nothing elseYour coding agent accepts an OFFER up to owner.agent.offers_up_to; above it, the OFFER waits for you. Members are never shown your plan, and no member is charged for a seat.
| Call (CLI) | API | Event | On the console | Plan |
|---|---|---|---|---|
limits | GET /owner/limits | limit.reached | /console/limits | every plan |
plan upgrade <plan> | POST https://services.dev/v1/plan | plan.accepted | /console/limits | every plan |