API rate limits
Per-endpoint reference
Goldenhour’s public surface uses in-memory token- bucket rate limiting on the customer-facing booking flow + the AI-vision endpoint. The MCP endpoints (the bulk of the AI-agent surface) have no per-IP limits: they’re aggressively edge-cached instead.
The token-bucket model
Each rate-limited endpoint has two parameters:
- Capacity: burst allowance. You can hit this many times back-to-back from a cold start.
- Refill rate: tokens regenerated per second. Sustained throughput converges to this once burst is exhausted.
On the 429 response, the body is { kind: "rate_limited" } + a Retry-After header in seconds. Wait that long + retry.
Rate-limited endpoints
Per-IP buckets
| Endpoint | Burst | Sustained | Notes |
|---|---|---|---|
| /api/book/quote-travel | 20 | 30/min | 30/minute sustained. Customer-facing live travel-fee quote endpoint as they type their address. Per-IP token bucket. |
| /api/book (submitBooking action) | 5 | 6/min | 6/minute sustained. Per-IP. Booking submission, waitlist signup, gift-card purchase share the same bucket. |
| /api/book/manage/[token] (cancel/reschedule) | 10 | 12/min | 12/minute sustained. Token-protected + IP rate-limited as credential-stuffing defense. |
| /api/shade-analyzer (vision) | 5 | 1/min | 1/minute sustained. Pre-auth public surface (anonymous client on booking page). Anthropic billing per call; tight cap. |
MCP + redirect endpoints
Cache-friendly, no per-IP cap
- MCP endpoints (/api/mcp/manifest, /api/mcp/[tenant], etc.) have NO per-IP rate limit. Aggressively edge-cached at 5 min for tenant data + 60s for availability + 1h for manifests / spec.json.
- QR codes (/api/qr/[tenant]) are 1h edge-cached + 6h stale-while-revalidate. A flood of unique-query-string requests would land at origin only on first miss per cache key.
- Booking-link redirects (/api/booking-link/[tenant]) have Cache-Control: no-store. Unbounded: but each call is a cheap DB lookup + a 302; non-issue for any realistic call volume.
Best practices for callers
- Cache aggressively on your side. Tenant data changes minute-scale; availability data changes minute-scale; manifests are essentially static. Respect Cache-Control headers.
- Respect Retry-Afterwhen you do hit a 429. Don’t retry-loop tighter than the header value.
- Polling cadence: for availability data, 60s is more than enough. The slot picture shifts when other customers book; sub-minute polling is paranoia, not freshness.
- Send a real User-Agent. Helps us reach out if anything breaks on our side. Format preferred:
<product>/<version> (<contact-email>).