API

Who's Bluffing runs on a small JSON API that the web game, the Slack and Discord apps and the classroom dashboard all use. This page starts with what anyone may read, then gives the full contracts: rounds (the game today) and the retired daily game.

Public API

The public player count is GET /api/players, returning {total_players}. It is cached for 60 seconds and carries Access-Control-Allow-Origin: *, so another site can read it with a simple GET. No key or account is needed.

Total Players counts distinct anonymous IDs with a completed round or full assessment, or an in-channel daily chat answer. Page views and unfinished rounds do not count. The same ID is deduplicated across sources; a person using several IDs can count several times.

Activity windows are private. /api/kpi requires the owner header x-kpi-key and returns the total, DAU, MAU (rolling 30 days), and annual active users (rolling 365 days), plus a UTC date. Legacy statistics endpoints require the same header. Missing or wrong keys return 401; private responses are not publicly cacheable and have no CORS header.

The other endpoints (starting a round, answers, flags, the chat apps' calls) exist for the Who's Bluffing apps. Building on them is welcome, but expect them to change without notice, keep traffic low, and never send personal data.

Rate limits

Requests to /api/* are rate-limited per IP address (about 120 a minute); beyond that the API answers 429 for a minute. Cache the public count rather than polling more than once a minute.

Attribution

If you show these numbers, please credit Who's Bluffing with a link to whosbluffing.com. Keep the note that anonymous IDs are not a precise count of individual people. Historical research definitions are preserved on the research page.

Open data

The public player-count endpoint contains no answers or player IDs. The release plan is fixed in the pre-registration: anonymous row-level data for both studies, with every exclusion flag kept, will be published on OSF, Hugging Face and Kaggle under CC BY-NC 4.0, each release with a data card that describes how it was collected, its sample bias and what it should not be used for. Classroom sessions are pooled without class codes. Details: Open data on the research page and the pre-registration.

Rounds API contract (replaces the daily-item loop for the viral game; the full assessment at /test keeps intervals)

Current product contract (2026-10-07): use mode=quick; mode=ranked returns 410. Old rk-<date> challenge links remain readable. History and Languages each have 80 authored questions with mixed formats, subtopic caps, and subject reuse avoidance; History also balances eras and regions. These packs do not fall back to generated comparisons.

The original contract below documents stored data and legacy replay behaviour; the access and creation rules above supersede its public-read and ranked-start descriptions.

Authored quick questions (2026-10-05): new all and ai quick rounds prefer items/quick_curated.json, with generated comparisons as fallback. IDs are q plus five digits; existing comparisons keep p plus five digits. IDs and answers are immutable after deployment; flawed questions are retired, never rewritten under an existing ID. Ranked schedules, daily chat questions, dares and the full assessment are unchanged.

All JSON. Dates are UTC YYYY-MM-DD. surface ∈ {web, slack, discord, room}. community is slack:<team hash>, discord:<guild hash> or room:<code>. A play = one completed round (10 answered) on web/room/discord/slack, or one in-channel Slack or Discord answer to the daily question.

Daily-item endpoints (/api/daily/*) remain for the full assessment and existing data; the home page no longer uses them.

Clarifications (2026-10-03, after the Slack build):

Additions (2026-10-04, from the web build; all backward compatible):

Familiarity (2026-10-04): every item carries views_month (average monthly English Wikipedia pageviews by users over the last 3 full months); a pair's fame = the lesser of its two items'.

Lab claims (2026-10-04): after a quick round in the AI pack a player can say which lab they are with. Self-reported and anonymous.

Party claims (2026-10-05): the same board for the Politics pack (quick-round pack=politics: categories pol_elected, pol_timeline, pol_numbers, pol_money, pol_drama, drawn by tier like the AI pack; never in ranked rounds or the chat question). After a quick round in the Politics pack a player can say which side they are on. Self-reported and anonymous; stored in the lab tables with board = parties (lab claims carry board = labs).

Daily API contract (web implements; Slack consumes)

Access amendment (2026-10-06): /api/daily/stats and /api/kpi are owner-only (x-kpi-key, 401 without it, private/no-store). /api/kpi now returns {as_of,total_players,dau,mau,yau}. Only /api/players exposes a public product count, {total_players}. The retired daily range game's plays are not part of the current product counts.

Base: the web deployment (https://<host>/api). All JSON. Dates are UTC YYYY-MM-DD. surface ∈ {web, slack, classroom}.

Anonymous ids: web = random id in localStorage; Slack = hex sha256(team_id + ":" + user_id + ":" + SALT) computed in the Slack worker (SALT is a Worker secret that must never change); never the raw Slack ids. Server stores players(anon_id, surface, first_seen, last_seen, plays) and plays(anon_id, date, surface, hits, completed_at).

Surface notes: for surface=slack, rt_ms is the modal open-to-submit time divided by 5 (Slack gives no per-question timing) and is approximate; response-time exclusions in PREREG apply to the web surface only.

Source: web/docs/public-api.md, docs/api-rounds.md, docs/api-daily.md on GitHub.