Reliability

Rate limits & quotas

Rate limiting protects the shared AI pipeline and keeps latency low for everyone. Limits are applied per API key (developer keys) and measured over a rolling window. Every response includes headers so you can throttle proactively.

Tier limits

TierRate (per minute)Daily quotaPrice
Sandbox10 req/min10 calls total$0trial
Starter60 req/min5,000 req/day$19/mo
Pro120 req/min25,000 req/day$49/mo
ScalecustomcustomCustom

Behind the numbers

The platform enforces two layers of limits, and both must be respected:

  • Laravel gateway: 60 req/min per identity (configurable per tier). Enforced by the throttle.api middleware on every /api/* route.
  • AI service: 30 req / 60s per identity on the FastAPI microservice (Slowapi). Generation and transform endpoints share this budget.

Caching reduces your usage

Specific-dish requests are cached on a tiered SQLite + Redis store and do not count against your AI quota. Vague cravings and wizard flows always generate fresh.

Response headers

Every rate-limited response includes X-RateLimit-Limit and X-RateLimit-Remaining so you can throttle proactively. When you exceed the limit, the 429 additionally carries X-RateLimit-Reset and Retry-After (seconds).

Rate-limit headers (success)
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42

# On a 429 you additionally get:
X-RateLimit-Reset: 1719900000
Retry-After: 12

# AI endpoints additionally expose:
X-AI-RateLimit-Remaining: 18
X-AI-RateLimit-Reset: 60

When you exceed the limit

You’ll receive a 429 Too Many Requests with a Retry-After header (seconds). Back off and retry; there’s no penalty or charge.

429 Too Many Requests
HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after 12 seconds.",
    "retry_after": 12
  }
}

Sandbox trial exhaustion (402)

Sandbox keys carry a one-time 10-call allowance, not a daily quota. Once those calls are used up, subsequent AI requests return 402 Payment Required with an upgrade nudge:

402 Payment Required
HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "error": {
    "code": "trial_exhausted",
    "message": "Your free trial allowance has been used. Upgrade to a paid tier to continue."
  }
}

Recommended client pattern

Honor Retry-After, use exponential backoff, and spread bursts with a small jitter. Most SDKs wrap this for you.