In March 2020, developer Kyle Redelinghuys wired Johns Hopkins' daily case counts into a free, keyless REST API at covid19api.com β and accidentally created a tutorial genre. For two years, "build a COVID-19 dashboard" was the portfolio project: every React course, bootcamp cohort, and YouTube series had one, most of them opening with fetch('https://api.covid19api.com/summary'). By its own farewell note the API served over a billion requests β dashboards, mobile apps, travel tools. Genuine respect: it was fast, documented in Postman, and free through the entire pandemic.
It was discontinued on May 15, 2023. The farewell page still stands at www.covid19api.com, but the API host itself is gone at the DNS level (verified September 2026):
curl "https://api.covid19api.com/summary"
# curl: (6) Could not resolve host: api.covid19api.com
In a browser that's TypeError: Failed to fetch before any HTTP happens β no status code, no CORS hint, nothing for a tutorial-follower to google. Every COVID-dashboard tutorial written between 2020 and 2023 now dies on its first fetch.
The community successor most tutorials migrated to, disease.sh (GPL-3.0, ~2.5k stars, still maintained), is everything you want from a free API: keyless, CORS-open (access-control-allow-origin: *), ~1s responses, 231 countries with rich per-country fields, flag images on a CDN it hosts itself, and it even kept its legacy /v2 paths answering so old integrations don't break. All verified September 2026:
curl "https://disease.sh/v3/covid-19/all" # global totals
curl "https://disease.sh/v3/covid-19/countries/usa" # one country, flag URL included
curl "https://disease.sh/v3/covid-19/countries/notacountry"
# β 404 {"message":"Country not found or doesn't have any cases"} β a real 404, kindly
Point an old tutorial at it and things mostly just work. But there's a trap no host can fix, because it's in the data itself:
todayCases is 0 in all 231 countries β the upstream sources stopped daily collection years ago. The totals you fetch today are a historical record, not a feed.updated says "just now." The updated timestamp refreshes every few minutes (it's the cache's clock, not the data's), so your "last updated" widget confidently displays today's date over 2023 numbers. The freshest-looking frozen data on the internet.?lastdays=30 doesn't return the last 30 days; it silently returns February 8 β March 9, 2023. Your x-axis time-travels and nothing errors.recovered flatlines at zero. JHU stopped tracking recoveries long before the end β the USA recovered timeline is literally all zeros. The green line in the classic three-line chart tutorial renders flat on the floor./vaccine/coverage?lastdays=5 returns the same number five times.None of this is disease.sh's fault β it faithfully serves what the world stopped producing. But it means every "live dashboard" tutorial is now, in truth, a historical-snapshot dashboard. Which is fine! It just changes what you need from the API: not liveness β queryability.
Capture the country table once, flatten the nested countryInfo, and host your own copy β no signup:
curl -s "https://disease.sh/v3/covid-19/countries" | jq '{countries: [.[] |
{country, iso3: .countryInfo.iso3, flag: .countryInfo.flag,
cases, deaths, recovered, active, critical,
casesPerOneMillion, deathsPerOneMillion, tests, population, continent}]}' > db.json
curl -s -X POST "https://mockbird.mockbird.workers.dev/api/projects/import?name=covid" \
-H 'content-type: application/json' --data-binary @db.json
# β {"id":"YOUR_ID","adminKey":"YOUR_ADMIN_KEY",
# "resources":[{"name":"countries","records":231,β¦}]} β 0 warnings
The flag URLs survive the import β hotlink them and the flags still render in your table rows.
Every command below ran against a live import while writing this guide:
BASE=https://mockbird.mockbird.workers.dev/m/YOUR_ID
curl "$BASE/countries?country=USA" # exact match
curl "$BASE/countries?country_like=stan" # 6: Afghanistanβ¦Uzbekistan
curl "$BASE/countries?cases_gte=10000000" # X-Total-Count: 16
curl "$BASE/countries?continent=Europe" # X-Total-Count: 48
curl "$BASE/countries?sortBy=deaths&order=desc&limit=3&select=country,deaths"
# β USA (1,219,487), Brazil (711,380), India (533,570)
# β the "top N" table, one request, no client-side sort
That last one matters: the leaderboard table at the top of every COVID dashboard was always a client-side .sort() over the full 231-row payload, because neither the original API nor disease.sh sorts for you. And writes are real, which the originals never offered:
curl -X PATCH "$BASE/countries/1" \
-H 'content-type: application/json' -d '{"watched":true}'
# β persists β the "pin a country" exercise has a backend now
curl -X POST "$BASE/countries" -H 'content-type: application/json' \
-d '{"country":"Atlantis","cases":0}' # β id 232
/m/YOUR_ID/types.ts generates a TypeScript Country interface from your records (?format=zod for Zod), and the outage story that brought you here is reproducible on demand for error-state tests: ?mock_delay=3000, ?mock_status=503, ?mock_ratelimit=5.
| covid19api.com | disease.sh | Your import | |
|---|---|---|---|
| Reachable | β NXDOMAIN since 2023 | β keyless, CORS * | β keyless, CORS * |
| Data | β (gone) | β full historical record, 231 countries | β the same snapshot, yours |
| Still updating | β | β frozen (todayCases: 0 everywhere) | β frozen β but honestly labeled yours |
| Historical charts | β | β β¦ending 3/9/23; lastdays time-travels | β import any window as records |
| Search / substring | β | β exact country names only | β ?country_like=, ?q= full-text |
| Ranges w/ counts | β | β | β ?cases_gte= + X-Total-Count |
| Sorting | β | β ?sort= on some list routes | β ?sortBy= any field, both orders |
| Writes persist | β | β read-only | β POST/PATCH/DELETE are real |
| Flags | β | β own CDN | β hotlink theirs (verified 200) |
The fair split: for reading the historical record β or the per-continent, per-US-state, and vaccine slices we didn't import here β use disease.sh directly and star the repo; it's a model citizen of the free-API world. For serious epidemiological analysis, skip REST APIs entirely and use Our World in Data's maintained datasets. The moment you're building or testing a dashboard β sorting leaderboards server-side, filtering with counts, wiring a pin button, keeping CI green and deterministic β spend the one curl and own the snapshot. Successor status: /status/disease-sh. Related dead-API rescues: Studio Ghibli API, PunkAPI, GitHub Jobs, deterministic test data.