Rate Limits
How request quotas, rate limiting, and overage billing work.
Per-Tier Limits
| Plan | Monthly Requests | Rate Limit | Overage |
|---|---|---|---|
| Free | 1,000/mo | 30 req/min | Hard cap |
| Starter | 25,000/mo | 100 req/min | $0.003/req |
| Pro | 100,000/mo | 200 req/min | $0.002/req |
| Scale | 250,000/mo | 500 req/min | $0.001/req |
Sliding Window Algorithm
Rate limits use a 60-second sliding window per API key. The window moves continuously—there is no fixed reset boundary. If you send 100 requests at second 0 and wait 30 seconds, you will have approximately 50 requests available as the oldest requests age out of the window.
Rate Limit Headers
Every API response includes three headers that describe your current rate limit status:
| Header | Description | Example |
|---|---|---|
| X-RateLimit-Limit | Maximum requests per window | 100 |
| X-RateLimit-Remaining | Requests remaining in the current window | 87 |
| X-RateLimit-Reset | Unix timestamp when the window resets | 1711843200 |
Handling 429 Responses
When you exceed the rate limit, the API returns a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait:
{
"error": {
"status": 429,
"message": "Rate limit exceeded. Retry after 12 seconds.",
"requestId": "req_abc123def456"
}
}
// Response headers:
// Retry-After: 12
// X-RateLimit-Limit: 100
// X-RateLimit-Remaining: 0
// X-RateLimit-Reset: 1711843200Retry Strategy
Implement exponential backoff with the Retry-After header as your baseline:
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "5", 10);
const backoff = retryAfter * Math.pow(2, attempt);
await new Promise((resolve) => setTimeout(resolve, backoff * 1000));
}
throw new Error("Rate limit exceeded after max retries");
}Server-Side Caching
Responses are cached server-side with per-route TTLs. The X-Cache header indicates whether the response was served from cache. Cached responses still count toward
your monthly quota.
| Route Pattern | TTL |
|---|---|
| /positions/today, /moon/today, /aspects/today, /almanac | 5 minutes |
| /horoscope/daily, /horoscope/all | 15 minutes |
| All other endpoints | 1 hour |
Related
- Authentication — Bearer token format and setup
- Error Codes — All HTTP status codes and error shapes
- Pricing — Plan details and endpoint access tiers