TattooBookingDevelopers

Rate limits

Token-bucket rate limiting on every /v1/* endpoint, with three layered scopes: per-key, per-tenant, and per-endpoint.

Tiers

TierPer-key limitDefault for
free60 req/min(reserved)
standard100 req/minSolo, Duo, Crew plans
pro500 req/minStudio, Empire plans
customPer-key overrideContact support for enterprise needs

Layered scopes

  • Per key. Each API key gets the tier rate above.
  • Per tenant. All keys in a tenant combined are capped at 1000 req/min.
  • Per endpoint. Expensive routes have tighter limits — e.g.POST /v1/appointments is capped at 30/min/key, POST /v1/forms/:id/submissions at 60/min/key.

The most-restrictive scope wins. A 429 response identifies which scope tripped via the X-RateLimit-Scope header (key, tenant, or endpoint).

Headers on every response

RateLimit-Limit: 100
RateLimit-Remaining: 97
RateLimit-Reset: 42

(IETF draft format.) When you hit the limit:

HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 35
Retry-After: 35
X-RateLimit-Scope: key
Content-Type: application/problem+json

{
  "type": "/errors/rate_limit_exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "The key-level rate limit of 100 requests per minute was exceeded. Retry in 35s.",
  "code": "rate_limit_exceeded",
  "extra": { "scope": "key", "limit": 100, "retry_after_seconds": 35 }
}

Backoff

Respect Retry-After. If you don't, you'll keep getting 429s and burn your quota.

async function callWithBackoff(fn, maxAttempts = 5) {
  for (let i = 0; i < maxAttempts; i++) {
    const res = await fn();
    if (res.status !== 429) return res;
    const retryAfter = Number(res.headers.get('retry-after')) || (2 ** i);
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  }
  throw new Error('Rate-limited after max attempts');
}

Bursting

The bucket allows natural bursts up to its capacity. A standard-tier key can burst ~100 requests instantly, then refill at ~1.67 req/sec. Plan around the long-run average, not the instantaneous limit.