Skip to content

Limits and caching

To keep Bazoodle fast and free for everyone, the public API limits how often one address can call it, and tells you how long each answer can be reused. Staying inside both makes your tool faster too.

Reads are counted per caller IP address, per group of endpoints, per minute. The default is 120 requests a minute for each group:

Group What it covers
catalogue home, categories, browse, feedback tags, game detail
search search and trending searches
media files under /media/

Each group has its own count, so a busy search box does not use up your browsing allowance. The numbers are settings and may change; do not hard-code them. The counters use a scrambled (hashed) version of your address and are kept for about a minute, never as a log of who called what.

The API answers 429 Too Many Requests with a Retry-After header that says how many seconds to wait:

HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 42
Content-Type: application/json
{"error":{"code":"rate_limited","message":"too many requests; slow down"}}

Wait at least Retry-After seconds, then continue. Do not retry immediately in a loop: that keeps you blocked. Be resilient has a retry helper.

Every counted response (and the 429) says where you stand, so you can slow down before you are blocked:

Header Meaning
X-RateLimit-Limit How many requests this group allows per minute.
X-RateLimit-Remaining How many you have left in the current minute.
X-RateLimit-Reset Seconds until the minute resets and the count starts again.
  • Cache responses (below). Thumbnails never change and can be kept for a year.
  • A game’s detail response already contains similar games; do not call again for each of them.
  • Use limit=50 for lists instead of many small requests.
  • Do not search on every key press: wait until a person pauses typing.
  • If you run a server, share one cache between your users. A website that calls the API from each visitor’s own computer spends that visitor’s allowance, not yours.

Every response carries a Cache-Control header. These are the values at the time of writing; always read the header rather than relying on this table.

Endpoint Cache-Control Fresh for
/catalog/home, /catalog/browse, /catalog/search, /catalog/games/:id private, max-age=15 15 seconds
/catalog/search/trending private, max-age=60 1 minute
/catalog/categories, /catalog/feedback-tags private, max-age=300 5 minutes
/status public, max-age=30 30 seconds, shared caches allowed
/terms-version public, max-age=300 5 minutes, shared caches allowed
/media/:game/:file public, max-age=31536000, immutable never changes: keep it a year

private means the response may depend on who asks (for example, the kid-friendly filter), so shared caches such as a CDN should not store it. If you serve many people, cache in your own server’s memory or database, keyed by the full request address.

JSON responses that can be cached carry an ETag. Send it back in If-None-Match on your next request for the same address. If nothing changed you get 304 Not Modified with no body, which is faster. It still counts as a request toward the limit.

Terminal window
curl -s -D - -o /dev/null -H 'If-None-Match: "0123456789abcdef"' "https://bazoodle.com/api/v1/catalog/categories"

A media file’s name contains a fingerprint of its content. If a creator replaces a picture, the new picture gets a new address, which is why files are immutable. Use the address you are given in thumbnail_url (and in a game’s gallery) and keep the file as long as you like. The ETag header is also set, so conditional requests work.

  • Re-request a list at most once per max-age.
  • Cache a game’s detail by its id and drop it after a minute or two.
  • Cache categories and feedback tags for a day if you like; they rarely change.
  • When a request fails or answers 429, keep showing what you cached rather than nothing.