Be resilient
Calls can fail: you may be calling too fast, the network may drop or Bazoodle may be briefly unavailable. A good tool slows down politely and keeps working. This guide covers retries, listing every game, and a status line.
Handle 429 and errors
Section titled “Handle 429 and errors”| Status | Meaning | What to do |
|---|---|---|
200 |
Success | Use the data |
400 |
Your request has a value that is not allowed | Fix the request; do not retry unchanged |
404 |
Not found or not public | Show “not found”; do not retry |
429 |
Too many requests | Wait Retry-After seconds, then try again |
5xx or no answer |
A problem on the way | Wait and try again, a little longer each time |
A retry helper in Python 3 (standard library only):
import jsonimport timeimport urllib.errorimport urllib.request
BASE = "https://bazoodle.com/api/v1"
def get_json(path, tries=4): delay = 2 for attempt in range(tries): request = urllib.request.Request(BASE + path, headers={"User-Agent": "MyGameList/1.0 (contact: you@example.com)"}) try: with urllib.request.urlopen(request, timeout=20) as response: return json.load(response) except urllib.error.HTTPError as error: if error.code in (400, 404): raise if error.code == 429: wait = int(error.headers.get("Retry-After", "60")) else: wait = delay delay *= 2 except urllib.error.URLError: wait = delay delay *= 2 if attempt == tries - 1: raise RuntimeError("giving up after " + str(tries) + " tries") time.sleep(min(wait, 120))
print(get_json("/catalog/categories")["max_tags_per_game"])The rules behind it:
- Obey
Retry-After. Retrying sooner only keeps you blocked. - Back off. For errors with no
Retry-After, wait 2 seconds, then 4, then 8, and give up after a few tries. - Never retry a
400or404: the answer will not change. - Keep what you cached. If the API is not answering, show the last good data instead of nothing.
- Find out whether it is us. Status says whether Bazoodle has a problem; check it before you tell your users something is broken.
List every game you can see
Section titled “List every game you can see”Sometimes you want every game you are allowed to see, for an index or a quiz. This Python loop uses the api helper from the Python guide:
def all_games(**params): seen = set() offset = 0 while offset is not None: page = api("/catalog/browse", limit=50, offset=offset, **params) for game in page["games"]: if game["id"] not in seen: seen.add(game["id"]) yield game offset = page["next_offset"]
games = list(all_games(sort="new"))print(len(games), "games")It asks for 50 games at a time (the largest page), follows next_offset until it is null, and skips a game it has already seen, because lists change while you read them. You can reach at most the first 500 games of a list, so for more use different lists, one for each tag or filter, each with its own first 500:
categories = api("/catalog/categories")by_tag = {}for tag in categories["tags"]: by_tag[tag["id"]] = [g["title"] for g in all_games(tag=tag["id"], sort="top")]print({tag: len(titles) for tag, titles in by_tag.items()})A full pass is about ten requests for 500 games, well inside the rate limit. Do it at most a few times a day and save the result, and do not use many addresses to get around the limits.
Show the service status
Section titled “Show the service status”A small “Bazoodle status” line is easy to build on Status. This JavaScript for Node.js 18 or later turns the answer into one line you can show:
const response = await fetch("https://bazoodle.com/api/v1/status", { headers: { "User-Agent": "MyGameList/1.0 (contact: you@example.com)" },});const status = await response.json();
function describe(s) { if (s.maintenance.mode !== "off" && s.maintenance.message) { return `Notice from Bazoodle: ${s.maintenance.message}`; } if (s.status === "operational") return "Bazoodle is working normally."; if (s.status === "maintenance") return "Bazoodle is down for planned work."; const broken = s.components.filter((c) => c.status !== "ok").map((c) => c.label); return `Bazoodle is having trouble: ${broken.join(", ")}.`;}
console.log(describe(status));console.log("checked", new Date(status.checked_at * 1000).toISOString());The answer is cached for 30 seconds, so once a minute is plenty for a page; for a plain yes or no from a monitor use /healthz (Small endpoints). Keep the text honest and short, link to https://bazoodle.com/status, and if your own request to the API fails, say “we can’t reach Bazoodle right now” instead of “Bazoodle is down”: the problem may be your network.