Skip to main content
Rate limiting is per workspace (not per key), implemented as a token bucket in Postgres. Every request consumes 1 token; tokens refill at the sustained rate per second, capped at burst.

Plan tiers

Headers on every response

X-RateLimit-Reset is a Unix timestamp — when the bucket will next be full.

When you hit the cap

Respect Retry-After — it’s whole seconds.

Hard caps

The token bucket above is about pacing. The hard cap is about cost — a customer-set ceiling on how many API calls an agent can consume per billing period. The effective cap on agent traffic is the lesser of (plan cap, hard cap). Plan caps come from the Billing page (free=500, solo=100_000, pro=1_000_000). workspaces.hard_cap_api_calls is a customer-set ceiling on top — settable via PATCH /workspace, defaults to null (no extra limit beyond the plan). The counter increments atomically per request and rolls over at the start of each calendar month. When you hit a cap, the response’s error message names which kind it was (plan vs hard):
Period rollover is lazy — checked on every call, no cron required. The first request on the 1st of the month resets the counter automatically.
When usage first crosses 80% of the monthly cap, Salty fires an internal usage_cap_warning event and emails the workspace owner (once per period) so you can act before hitting the limit.
Admin actions are exempt. The cap meters agent traffic only — requests authed with sk_live_… API keys or salty_oat_… OAuth tokens. JWT-authed requests from the admin UI (billing, raising the cap, browsing /records) stay reachable when the cap is exhausted, so a customer is never trapped. See Concepts → Billing for the plan limits.

Idempotency + rate limits

Cached idempotency replays do count against the rate limit. The cache speeds up your application; it doesn’t grant immunity from pacing.