Public API

Every number on this site is public JSON: which providers are down right now, ninety days of uptime, every incident their own status pages published, and the crowd's click counter. It is the same data the pages are drawn from, read from the same cache.

Using it

Every endpoint

Method and pathResponseCache-Control
GET /api/statesame object as hello minus t/tickHzpublic, max-age=1, stale-while-revalidate=2 + ETag: "ver-<ver>-<floor(all/1000)>", honours If-None-Match
POST /api/clickack objectno-store; 429 with Retry-After when the per-IP limiter (10 req/s) trips
GET /api/history?provider=&limit=50&before=<iso>{"items":[{"id","provider","startedAt","endedAt","durationS","clicks","spawns","breaches","peakZombies","wallsDown","wallTimes","zombiesV2","synthetic","incidents":[{"id","title","impact"}]}],"nextBef…public, max-age=60
GET /api/sieges/{id}siege as above + "events":[{"kind","at","value","payload"}] + "incidents":[{…,"updates":[{"status","body","createdAt"}]}]max-age=300 if ended, max-age=5 if live
GET /api/incidents?provider=&open=true&limit=50{"items":[{"id","provider","title","impact","status","startedAt","resolvedAt","url","updates":[…last 3…]}]}public, max-age=30
GET /api/incidents/{provider}/{sourceId}one incident's whole record (B29): {"id","provider","sourceId","title","impact","status","startedAt","resolvedAt","durationS","sourceUpdatedAt","lastSeenAt","url","statusUrl","updates":[{"status","bod…public, max-age=30 while open, public, max-age=300 once resolved
GET /api/resets?limit=50&before=<iso>{"current":{"clicks":98765,"sinceAt":"<when the latest applied reset landed: effectiveAt when known, else announcedAt; appliedAt only when neither…public, max-age=60
GET /api/stats{"allTimeClicks","resetClicks","perProvider":{"<p>":{"sieges","totalClicks","totalBreaches","totalSiegeSeconds","currentPeaceStreakS","longestSiege":{"id","durationS"}}},"records":{"longestSiege":{"id…public, max-age=60
GET /api/minutes?target=all|openai|anthropic|google|codex-reset&hours=24[{"minute":"<iso>","clicks","spawns","breaches"}] gap-filled with zerospublic, max-age=60
GET /api/status/{provider}?days=90one provider's own status page — {"provider","name","statusUrl","asOf","current":{"state":"ok|degraded|down","down","sinceS","openIncidents"},"windows":[{"window":"24h|7d|30d|<days>d","uptimePct","dow…public, max-age=300
GET /api/uptime?days=90{"days":[{"date":"2026-09-03","openai":"ok|minor|major|critical","anthropic":"…","google":"…"}]} worst impact per UTC daypublic, max-age=300
GET /feed.xmlAtom feed, 50 newest entries: siege started / siege ended / codex reset / incident openedpublic, max-age=300
GET /og.png?p=all|openai|anthropic|google1200×630 PNG of the live state; ?v= is an ignored cache-busterpublic, max-age=60
GET /badge.svg?provider=all|openai|anthropic|google|chatgpt|codex|claude|geminishields-style SVG, e.g. OpenAI · under siege · 7 zombies. The four product slugs (F31) label with the product and take their state from that product's own component subset, by the same filter and the…public, max-age=60
GET /embed?p=all|openai|anthropic|googleembed.html (second Vite entry), header Content-Security-Policy: frame-ancestors *no-cache
GET /index.html with injected <title>, meta description, og:, twitter: (og:image=/og.png?p=all&v=<ver>); the built index.html contains a single marker <!--mha:meta--> in <head> (no <title> of its own)…no-cache
GET /status/{chatgpt|codex|claude|gemini}a product page: the parent provider's response narrowed to the components that product publishes, with its own title, h1, FAQ, four-item breadcrumb and a "Part of {Parent} status" line. It exists…no-cache
GET /status/{provider}/incidents/{sourceId}an incident page (B29, R-521/R-522): breadcrumbs, <h1>{Provider} incident: {title}</h1>, an answer-first summary sentence, the timeline, the affected components, the siege the crowd fought, and the…no-cache
GET /v2the same shell, with <meta name="robots" content="noindex, follow"> and canonical = {base}/. It is a rendering variant of /, not a second pageno-cache
GET /api-docsthe public API as a page (F33): the REST table and the WebSocket frames rendered from this file at build time, with a curl line per endpoint, the CORS and rate-limit statement, and the commit and…no-cache
GET /ads/termsthe advertising programme's three reader-facing pages (F35, R-525): the advertiser terms, the page PayPal returns an approving buyer to, and the page it returns a cancelling one to. Ordinary shell…no-cache
GET /robots.txtserved by the host, not a static file: Sitemap: line on the configured origin, /og.png explicitly allowed, /api/, /embed, /badge, /lab.html, /v2 disallowed, and an allow group for OAI-SearchBot,…public, max-age=3600
GET /sitemap.xml<urlset> of every indexable route with <lastmod> derived from data (newest incident, reset or siege for that page; omitted when the page has no data yet), <changefreq> and <priority>. Never lists…public, max-age=300
GET /{indexnow-key}.txtthe IndexNow ownership proof: the key as text/plain. 404 when Mha__Seo__IndexNowKey is unset or the path is not the configured keypublic, max-age=3600
GET /llms.txttext/markdown: what the site is, every status and product page as a linked list, the public API behind them, and how to cite. The map an answer engine reads before it reads the pagespublic, max-age=3600
GET /llms-full.txttext/markdown: the same server-composed bodies as the pages, with the markup taken off (headings become ##, <dt>/<dd> merge onto one line, the breadcrumb <nav> is dropped as navigation rather than…public, max-age=300
GET /humans.txttext/plain: what the site is made of and who runs it. Names a maintainer only when Mha__Seo__MaintainerName is set; never an email, never anything identifying by defaultpublic, max-age=3600
GET /.well-known/security.txttext/plain per RFC 9116, with Contact, Expires (build stamp + 1 year, floored at now + 1 year) and Preferred-Languages. 404 when Mha__Seo__SecurityContact is unset — a security.txt with nowhere to…public, max-age=3600
GET /feed.xml?p=openai|anthropic|googlethe same Atom feed filtered to one provider; absent or all is the unfiltered feedpublic, max-age=300
GET /assets/*Vite hashed assetspublic, max-age=31536000, immutable
GET /healthzsee docs/architecture/deployment.mdno-store

Built from docs/api.md 9bcacc78 on 2026-09-08.

Last checked .

Must Have AI is a parody and is not affiliated with, endorsed by or connected to OpenAI, Anthropic or Google. Every outage shown here is read from the provider's own public status page.