Authentication#
Every /api/v1/* request — except /api/v1/report (anonymous bug submission) — requires an API key. Quota is per user account, shared across all your keys (up to 20 active).
Key prefixes#
| Prefix | Purpose | Browser-safe (CORS) |
|---|---|---|
qb_pk_* | Publishable — safe to embed in client bundles (browsers, mobile apps). | Yes — sends Access-Control-Allow-Origin: * |
qb_sk_* | Secret — backend only. Don’t ship in client bundles. | No — browsers will block cross-origin use |
Passing the key#
Send your key in the X-API-Key header. Authorization: Bearer <key> also works.
Rotation#
Rotate keys from the dashboard. When you rotate:
- Create the new key first.
- Deploy the new key alongside the old one in your environment.
- Delete the old key after your traffic has drained to the new key (check dashboard usage).
The rotated old key keeps working for 24h as a grace period — no downtime, no broken requests.
Storage#
- Server: environment variables (
QUIZBASE_KEY), never committed files. - Client (browser/mobile): only
qb_pk_*. Consider a server-side proxy if you want to hide the key entirely — it costs you a hop but stops key extraction. - CI: encrypted secrets (GitHub Actions, GitLab CI, Railway variables).
Public endpoint (no key needed)#
One endpoint is public — anonymous-by-design with per-IP rate limit:
POST /v1/report— translation / factual / attribution / inappropriate problem reports. Rate-limited 5/min per IP.
Every other endpoint requires an API key.
CORS — when browser fetch works#
/v1/report: open CORS — fetch from any origin works without a key.- Authenticated endpoints with
qb_pk_*: CORS header set. Browser fetch from any origin works — that’s what publishable keys are for. - Authenticated endpoints with
qb_sk_*: no CORS header. Browsers block these requests by design. Secret keys aren’t meant to ship in browser bundles.
OPTIONS preflight returns 204 with Access-Control-Allow-Methods: GET, POST, OPTIONS and Allow-Headers: Authorization, Content-Type, X-API-Key, X-Request-Id.
Per-key origin allowlists (Stripe-style allowed_domains) are on the post-launch roadmap — currently any origin can use a pk_* key.
Next#
- Errors and retries — what a 401 looks like and how to handle it
- GET /v1/me — first authenticated endpoint to smoke-test your key
- GET /v1/questions/random — fetch a quiz round