Rate Limits & Pricing
Rate limits keep the API reliable for everyone. Your limit is per API key and resets on a rolling hourly window.
Tiers
| Tier | Requests per hour | Price | Best for |
|---|---|---|---|
| Free | 100 | Free | Testing and prototyping |
| Hobby | 500 | €4.99/mo | Small apps and personal projects |
| Premium | 2,000 | €14.99/mo | Production apps with real traffic |
Rate limit headers
Every response includes three headers so you always know where you stand:
| Header | Description |
|---|---|
X-RateLimit-Limit | Your tier's hourly request cap |
X-RateLimit-Remaining | Requests left in the current window |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets |
HTTP/1.1 200 OK
X-RateLimit-Limit: 500
X-RateLimit-Remaining: 487
X-RateLimit-Reset: 1704070800
When you hit the limit
A 429 response means you've exhausted your quota for the current window:
{
"error": "Rate limit exceeded",
"status": 429
}
The X-RateLimit-Reset header tells you exactly when to retry. The recommended pattern is to wait until the reset time rather than using fixed backoff:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options)
if (response.status !== 429) return response
const reset = response.headers.get('X-RateLimit-Reset')
const waitMs = reset
? (Number(reset) * 1000) - Date.now()
: Math.pow(2, attempt) * 1000
await new Promise((resolve) => setTimeout(resolve, Math.max(waitMs, 1000)))
}
throw new Error('Max retries exceeded')
}
import time, requests
def fetch_with_retry(url, headers, params=None, max_retries=3):
for attempt in range(max_retries + 1):
response = requests.get(url, headers=headers, params=params)
if response.status_code != 429:
return response
reset = response.headers.get('X-RateLimit-Reset')
wait = max(int(reset) - int(time.time()), 1) if reset else 2 ** attempt
time.sleep(wait)
raise Exception('Max retries exceeded')
Staying within your limit
A few habits go a long way:
- Cache aggressively. Categories change rarely — cache them for hours. Random quotes for quote-of-the-day features only need one request per day.
- Use the right page size. Fetch what you need. If you're displaying 5 quotes, don't request 100.
- Watch
X-RateLimit-Remaining. If you're regularly hitting zero, that's your signal to upgrade. - Batch with filters. Use
categories,author_id, orplaylist_idto get the right quotes in one request rather than filtering on your end.
Bundle discount
| Tier | Regular | With Supporter bundle |
|---|---|---|
| Hobby | €4.99/mo | €3.74/mo |
| Premium | €14.99/mo | €11.24/mo |
FAQ
What counts as one request? Every API call, regardless of which endpoint. Errors count too, except 429 responses.
Can I get more than 2,000 requests/hour? For enterprise needs, contact support@quotegallery.nl.
What happens when I cancel? Access reverts to the Free tier immediately. Your API key stays valid — only the rate limit changes.
Do all endpoints share the same pool? Yes, there are no per-endpoint limits. All endpoints draw from the same hourly quota.