quizbase
Skip to content
Command Palette
Search for a command to run...
QuizBase · Docs
by Maciej Dzierżek · published Apr 24, 2026 · updated May 15, 2026 · 15 min read · Beginner
migrating-from-opentdb hero illustration
Illustration for: OpenTDB alternative — migrating to QuizBase v1 · Generated with Nano Banana, brand style

Migrating from OpenTDB#

QuizBase is designed for drop-in migration from Open Trivia DB. The endpoints, fields, and category ids map cleanly — most existing clients need only two changes.

The two changes#

  1. Base URL: https://opentdb.com/api.phphttps://quizbase.runriva.com/api/v1/questions/random
  2. Add auth: X-API-Key: qb_pk_* header

That’s it for the happy path. The response shape is different — see below — but the semantics are identical.

Field mapping (question)#

OpenTDB fieldQuizBase fieldNotes
category (string)category.name + category.slug + category.idQuizBase returns a nested object. category.id equals the OpenTDB numeric id when applicable.
typetypeSame enum: multiple, boolean. QuizBase adds text_input.
difficultydifficultyExtended enum: trivial, easy, medium, hard, expert — LLM-calibrated with distractor-set context. OpenTDB’s 3 levels map roughly to QuizBase easy/medium/hard; pre-rescore records hold an importer placeholder (mostly medium).
questiontextRenamedquestiontext.
correct_answercorrectAnswercamelCase.
incorrect_answersincorrectAnswerscamelCase.
languageNew — ISO 639-1. OpenTDB is English-only.
idNew — UUID v7. Use for caching/dedup.
attributionNew — { source, author, license, licenseUrl, url, sourceId }. Mandatory to preserve (CC-BY-SA chain).
tags, subcategoriesNew — filter-friendly metadata. Both arrays of { slug, label }: slug is the stable filter key (kebab-case), label is the human-readable string in the requested ?lang=. Tags are short concept identifiers, subcategories are broader topic groupings.
regionsNew — array of cultural affinity codes (lowercase ISO 3166-1 alpha-2 + jewish / christian-catholic / islam). Empty array = universally accessible. See /docs/api/regions.

Envelope mapping#

OpenTDBQuizBase
response_code❌ removed — use HTTP status codes + RFC 9457 error body
results[]data[]
meta: { count, requestId, language }

OpenTDB’s response_code: 1/2/3/4/5 (no results / invalid param / token not found / token empty / rate limit) maps to:

OpenTDB codeQuizBase response
0 (success)200 + data populated
1 (no results)200 + data: []
2 (invalid param)400 + RFC 9457 with errors[]
3/4 (session token)❌ not applicable — use exclude=<ids> for de-dup
5 (rate limit)429 + Retry-After header

Category ids#

OpenTDB uses numeric ids 9–32. QuizBase preserves those same ids on the category.id field, so you can pass either the numeric id or our slug — both work:

# OpenTDB-style numeric id
curl -H "X-API-Key: qb_pk_YOUR_KEY" 
     "https://quizbase.runriva.com/api/v1/questions/random?amount=5&category=22"

# QuizBase slug
curl -H "X-API-Key: qb_pk_YOUR_KEY" 
     "https://quizbase.runriva.com/api/v1/questions/random?amount=5&category=geography"

Call GET /v1/categories once and build your id↔slug map.

Side-by-side diff#

Session tokens (OpenTDB) → exclude (QuizBase)#

OpenTDB’s session-token system (codes 3/4) prevents repeats in a session. QuizBase replaces it with explicit exclude:

# Fetch 5 questions, excluding those you've already shown
curl -H "X-API-Key: qb_pk_YOUR_KEY" 
     "https://quizbase.runriva.com/api/v1/questions/random?amount=5&exclude=id1,id2,id3"

Max 100 ids per request. Track them client-side.

Rate limits#

OpenTDB is “don’t abuse” without headers. QuizBase uses IETF RateLimit-* headers on every response — measure yourself and respect Retry-After on 429. See Rate limits and retries.

See also#