quizbase
Skip to content
Command Palette
Search for a command to run...
QuizBase · Docs

GET /v1/questions#

GET /api/v1/questions API key required

Paginated browse of the full question catalog. Use this when you need all matching questions (e.g. building a local cache, a study app, or a data exporter) — not random samples.

Cursor-based pagination using UUID v7 ordering — stable across inserts, no page-number drift.

Parameters#

ParameterTypeDefaultDescription
cursor UUID v7Last `id` from the previous page. Omit for the first page.
limit integer20Page size, max 100.
lang enumenSupported: `en`, `pl`. Strict — any other value returns `400`.
updated_since ISO 8601Only questions with `updatedAt >= this`. Delta-sync pattern.
category integer or slugCategory id (1-24 internal, or 9-32 opentdbId) or slug.
difficulty enum`trivial` | `easy` | `medium` | `hard` | `expert` — LLM-calibrated 5-level difficulty graded with distractor-set context. `trivial` = common-knowledge (>80% of adults), `easy` = approachable, `medium` = requires-thought, `hard` = needs-domain-context, `expert` = specialist (<10%). Pre-rescore records hold an importer placeholder (mostly `medium` for factoid sources).
type enumDefault response includes `multiple` and `boolean` only. Pass `?type=text_input` to opt into open-ended questions explicitly.
tags CSV kebab-caseAll-of match (AND). Each returned question echoes tags as `{slug, label}` objects.
tags_any CSV kebab-caseAny-of match (OR), max 10. Use when AND logic is too restrictive.
topic kebab-caseCurated topic slug (resolves through aliases). 2,184 curated topics — see [GET /v1/topics](/docs/api/topics).
topics_any CSV kebab-caseAny-of match (OR) on curated topics, max 10.
subcategory kebab-caseRaw subcategory slug. Single-value filter; prefer `topic`/`topics_any` for OR semantics across many.
quality enumhighThree tiers, nested `high` ⊆ `standard` ⊆ `all`. `high` (default) returns only the cleanest, most broadly-useful questions. `standard` broadens to the full reviewed pool including niche/narrowly-scoped questions (more volume). `all` includes everything, even questions flagged for review — useful for audit, research, BY-SA dump. When `quality=all` each returned question gains a `"quality": "high" | "needs_review"` field so you can tell which records were flagged; `high`/`standard` skip the field.
regions CSV lowercase ISO + cultural**Cultural affinity** — residents of the country or members of the cultural/religious group are statistically more likely to know the answer (NOT geography of the subject). Lowercase ISO 3166-1 alpha-2 (`pl`, `us`, `gb`) plus cultural codes (`jewish`, `christian-catholic`, `islam`). AND-logic. Empty array on a question = universally accessible (no cultural advantage). Uppercase tolerated (normalized server-side). Discover the full catalog via [GET /v1/regions](/docs/api/regions).
source CSV source idsInclude only these sources. One or more of: `arc`, `creak`, `entityq`, `kqa-pro`, `mintaka`, `mkqa`, `nq-open`, `opentdb`, `opentriviaqa`, `qasc`, `quizbase`, `webq`. Comma-separated for multiple (OR), e.g. `source=mintaka,opentdb`.
exclude_source CSV source idsExclude these sources, e.g. `exclude_source=entityq` to drop a noisy auto-generated source. Comma-separated for multiple. Applied after `source` if both are present.
license SPDXe.g. `CC-BY-SA-4.0`, `MIT`. Filter by license string.
count enumnoneTotal strategy: `none` (default — page via cursor/`_links.next`, no total); `exact` (precise `COUNT(*)`, ~25-90ms) → `meta.total`.
ids CSV UUIDBatch fetch — up to 250 comma-separated ids in one call. Returns those exact records (anti-repeat, deep-links, restoring a saved set), no quality filter. Terminal selector: browse filters and `cursor` are ignored. Missing ids are reported in `meta.missing`.
content_language enumLanguage of the returned question **text** (`en`, `pl`). With `ids`, maps each id to its sibling in this language across the translation chain — the same questions in another language. Distinct from `lang` (display labels only).

Examples#

First page#

Delta sync#

Fetch only questions updated since your last sync:

curl -H "X-API-Key: qb_pk_YOUR_KEY" 
     "https://quizbase.runriva.com/api/v1/questions?lang=pl&updated_since=2026-04-01T00:00:00Z&limit=100"

Persist the highest updatedAt you’ve seen and use it as the next updated_since.

Batch by id + translation mapping#

Fetch a known set in one call, or map it to another language across the translation chain. Ids without a result come back in meta.missing; results are ordered to match the ids you sent.

Pass the ids you hold — the server resolves the translation family (originals are the root of their chain, so their rootQuestionId is null; you don’t compute roots). Full walkthrough: Languages and translations.

Response#

Response fields#

  • tags / subcategories — both arrays of { slug, label }. The slug is the stable identifier you filter by; the label is a human-readable string in the requested ?lang=. Tags are short identifiers (≤4 words, proper nouns and concepts), subcategories are broader topic groupings (≤4 words, organic). A question typically has 3-5 tags and 3-6 subcategories.
  • category — the top-level category (24 in total). id is the legacy OpenTDB id (9-32). name is localized to ?lang= with English fallback if a translation is missing.
  • translationOf / rootQuestionId — non-null when this row is a translation. translationOf is the direct parent (usually English source), rootQuestionId is the canonical source across translation chains.
  • translator — coarse provenance: "machine" (machine-translated from the source language), "human" (manually translated/edited), "native" (imported as-is from a multilingual upstream source), or null (original-language record, no translation involved). The specific upstream system that produced a machine translation is an implementation detail and not exposed.
  • extensions — forward-compatible container for stable per-source extras. Today exposes subcategories: string[] (also surfaced as the typed top-level subcategories: [{slug, label}]). Future stable fields land here additively. Internal pipeline observability (model versions, prompt versions, run IDs) is not exposed here — that’s audit data we keep server-side.

License metadata in attribution#

The 9-field attribution object gives you everything needed to comply with upstream licenses without consulting external docs:

  • license — the effective applied license string (e.g. CC-BY-SA-4.0 — with 3.0 → 4.0 upgrade for sources where applicable).
  • licenseVersion — the original upstream license version ("3.0", "4.0", or null for MIT). Use this when you need the exact version the upstream record came under.
  • licenseUrl — the canonical license URL (creativecommons.org or opensource.org). null for proprietary records.
  • modifications — array of changes we made on top of the upstream record. Possible values: translated_<lang> (per-language), refined_text (we improved phrasing), quizified (we converted a Q&A pair into a quiz item with distractors). An empty array means we kept the upstream record verbatim.
  • lastModified — ISO timestamp of our last modification (updatedAt). Use this for cache invalidation and updated_since syncs.

If you redistribute records covered by share-alike licenses, build your attribution string using author + licenseUrl + the modifications list.

Difficulty grading#

difficulty is graded by an LLM with the distractor set in scope — not the question alone. A boolean “is the sky blue?” rated against three nonsense distractors is trivial; the same question with three plausible-sounding distractors is medium. This is why the same factual content can land on different levels across the catalog.

Five levels:

  • trivial — common knowledge. Primary-school / mainstream culture. More than 80% of adults answer correctly.
  • easy — approachable. Familiar but takes a second of thought.
  • medium — requires thought. Most adults could get there with effort; not memorized.
  • hard — needs domain context. Hobbyists, students, professionals in the field.
  • expert — specialist trivia. Fewer than 10% answer correctly without research.

Pre-rescore records hold an importer-assigned placeholder (mostly medium for factoid sources like NQ, Mintaka, EntityQ). Use ?quality=high together with ?difficulty=... to skip ungraded rows when you need calibrated levels for adaptive gameplay.

Pair with ?quality=high (which keeps only records that passed distractor validation and content grading) for adaptive difficulty pipelines where the level needs to mean something.

Stop condition#

When _links.next is absent, you’ve reached the end. Some pages may return fewer than limit rows even mid-stream — do not use “fewer rows than limit” as a stop condition.

Rate limits#

Pagination lets you burn through your quota fast. Consider:

  • Requesting limit=100 (max) to halve request count
  • Using updated_since for incremental syncs after initial fetch
  • Caching on your side — questions don’t change often

Performance#

  • p50 (warm): ~57ms
  • p95: ~91ms (sustained 50 RPS, 5-min baseline)
  • p99: ~118ms
  • Last measured: 2026-05-30
  • SLO: p95 < 500ms, error rate < 1%
  • Cursor pagination scales to any catalog size — performance stays flat as you walk forward.
  • ?count=none (default) skips the total — page forward with the cursor and _links.next. Add ?count=exact for a precise meta.total (COUNT(*), ~25-90ms). For per-category/tag/topic totals, use the discovery endpoints (/tags, /subcategories, /topics, /regions) — each item carries an exact count.

See also#