How to Scrape Global Health & COVID-19 Data in 2026
Pull COVID-19 cases, deaths, vaccinations and historical time series for 200+ countries and US states from the open disease.sh API. Keyless and export-ready.
Getting clean, structured epidemiological data is harder than it should be. Official dashboards give you a chart, not a download; the underlying figures are scattered across Johns Hopkins, Worldometer, and national health agencies, each with its own format. If you’re building a trend dashboard, training a model, or comparing country responses, you don’t want to scrape HTML tables or reconcile a dozen CSVs by hand — you want one structured feed. The open disease.sh API aggregates these sources into a single keyless JSON API, and this guide covers how to pull country stats, historical time series, vaccination data, and US-state breakdowns from it at scale.
What’s worth extracting
The scraper returns structured records with 20+ fields, and which fields populate depends on the mode. The full field set:
- Identity & geography —
country(or US state name),continent,iso2andiso3country codes, and aflagimage URL. - Case counts —
cases(cumulative confirmed),todayCases,deaths,todayDeaths,recovered, andactive(cases minus deaths minus recovered). - Severity & testing —
critical(ICU patients),tests(total performed). - Population-adjusted ratios —
casesPerMillion,deathsPerMillion,testsPerMillion, andpopulation— the fields you need for fair cross-country comparison. - Vaccination —
vaccinated(total doses) andvaccinatedPerHundred, populated in vaccine mode. - Time & provenance —
date(for historical/vaccine rows),updated(date of last data update), and amodetag on every row so you can mix modes in one dataset and filter later.
How the source works
The scraper queries the disease.sh open-source disease-data API at https://disease.sh/v3/. There’s no HTML scraping, no captcha, and no login wall — it’s a stable JSON API, updated multiple times a day from Johns Hopkins, Worldometer, and other authoritative sources. It runs in four modes:
countries— live COVID-19 stats for all 200+ countries, or a filtered subset. Returns cases, deaths, recoveries, active, critical, tests, population, and per-million ratios.historical— time-series data, one row per country per date, with cumulative case/death/recovered counts going back to early 2020.vaccine— vaccination coverage timelines: doses administered per country per day across the rollout.usStates— state-level breakdowns for all 50+ US states, in the same shape as country rows.
An all-countries snapshot needs almost no configuration:
{
"mode": "countries"
}
A historical pull for a specific set of countries over a time window:
{
"mode": "historical",
"countries": ["USA", "Germany", "Turkey"],
"lastDays": 90
}
The countries filter accepts either full country names or ISO codes; leaving it empty returns everything. lastDays controls how far back historical and vaccine modes reach — set it to 0 for the entire available timeline.
▶ Run the Global Health Stats Scraper on Apify — one run pulls live stats for 200+ countries, or tens of thousands of historical time-series rows, from the open disease.sh API. No API key, no login. Export to CSV, JSON, or Excel.
The access reality
disease.sh is an open project built for public consumption, so the friction isn’t access — it’s data shape and volume:
- Fields are mode-specific. Not every endpoint returns every field.
historicalmode returns only cases, deaths, and recovered — not tests, population, or per-million ratios.vaccinemode returns vaccination figures. Fields not available in a given mode come back asnull. This is by design, not a bug, and it’s the single most common source of confusion. - Historical mode is high-volume. For a popular country like the USA, the full timeline is 1,000+ daily data points. Run
historicalwithlastDays: 0across all countries and you can get 50,000+ rows in one run — expect it to take 2–5 minutes. Countries mode, by contrast, finishes in 10–30 seconds. - Country-name matching is strict. Query a name or code the API doesn’t recognize and you get zero rows for it (the actor logs a warning and exits cleanly). ISO codes (
US,DE,TR) are more reliable than full names, which can mismatch on spelling or formatting. - Update cadence varies by data type. Live data refreshes roughly every 15–30 minutes; historical snapshots update about once a day. Schedule runs accordingly — twice-daily for live data captures both cycles.
- It’s an aggregator, so it inherits upstream quirks. disease.sh pulls from Johns Hopkins, Worldometer, CDC, and others. Data availability and revisions follow those upstream sources; a field being sparse for a given country usually reflects the source, not the scraper.
Example output
A countries-mode record for Germany:
{
"mode": "countries",
"country": "Germany",
"continent": "Europe",
"iso2": "DE",
"iso3": "DEU",
"flag": "https://disease.sh/assets/img/flags/de.png",
"cases": "38437756",
"deaths": "174979",
"recovered": "37900000",
"active": "362777",
"critical": "605",
"tests": "122332384",
"population": "84270625",
"casesPerMillion": "456216",
"deathsPerMillion": "2077",
"updated": "2024-03-15"
}
And a historical row, where the mode-specific nulls are visible — only cases, deaths, and recovered are populated:
{
"mode": "historical",
"country": "USA",
"date": "2022-01-15",
"cases": "64984610",
"deaths": "848877",
"recovered": "40547832",
"tests": null,
"population": null,
"casesPerMillion": null
}
Use cases
- Global comparison dashboards — download country-by-country stats for 200+ nations in one run and chart cases, deaths, and per-million ratios side by side.
- Trend analysis — pull historical case time series for a set of countries into Google Sheets or a BI tool and visualize the shape of each wave.
- Vaccination monitoring — track daily dose counts per country across the rollout period for policy or NGO reporting.
- US state dashboards — feed the
usStatesmode into a real-time dashboard or alerting system covering all 50+ states. - ML training data — build datasets for models predicting disease spread, healthcare-system strain, or the effect of policy interventions.
- AI-agent research — the actor is available as an MCP tool, so an LLM can pull live stats and summarize them (e.g. “which of these countries has the highest active case rate per million?”).
Cost and effort: build vs. managed
disease.sh is free and keyless, so the trade-off is engineering time and orchestration, not API bills:
- Building from scratch — you’d learn the four endpoint families, write the per-mode field mapping, handle the high-volume historical pagination, normalize the mixed null shapes into a consistent schema, and manage country-name-to-ISO matching. None of it is hard, but the mode-specific null handling and the historical volume are fiddly.
- Using the managed actor — pick a mode, optionally filter countries, run. Pricing is pay-per-result: a fraction of a cent per record. A countries snapshot is 200-ish rows for a fraction of a cent; a full historical pull is tens of thousands of rows but still cheap because it’s per-row.
Where the managed actor earns its keep is scheduling: set it to run daily and it builds a growing historical archive automatically, which is tedious to orchestrate by hand.
Common pitfalls
Specifics of disease.sh data that catch people out:
- Nulls are mode-dependent, not missing data. The number one gotcha. If you run
historicaland wonder wherepopulationandtestswent, they were never in that endpoint. Check which fields your chosen mode actually returns before you build downstream logic that expects them. - Use ISO codes, not names, for filtering. Full country names mismatch easily.
US,DE,TRresolve cleanly; “United States” vs “USA” may not. To confirm valid names, run an unfilteredcountriespass first and read thecountrycolumn. lastDays: 0can return a huge dataset. For all countries, the full timeline is 50,000+ rows. That’s fine, but budget the run time (2–5 minutes) and the row cost. Narrow to specific countries or a shorter window if you don’t need the whole history.- All values are strings. Cases, deaths, and ratios come back as strings, not numbers. Cast them before you do arithmetic in a spreadsheet or notebook.
- Data is aggregated and revised. disease.sh reflects its upstream sources, which occasionally revise figures. Two runs days apart can show adjusted historical numbers — that’s the upstream data being corrected, not the scraper being inconsistent.
- Continent filtering isn’t a mode. There’s no continent mode here; the scraper focuses on country and state granularity. To group by continent, run
countriesunfiltered and group on thecontinentfield afterward.
Wrapping up
disease.sh turns a scattered mess of pandemic data into one keyless API, which makes global health stats genuinely accessible — the work is handling the mode-specific fields and the sheer volume of historical rows. If you need a one-off snapshot, hit the API directly. If you want a repeatable, scheduled feed — a live snapshot plus a growing historical archive across countries or US states — a managed actor handles the modes, nulls, and orchestration.
▶ Open the Global Health Stats Scraper on Apify — one run yields live stats for 200+ countries or tens of thousands of historical time-series rows, keyless and login-free. Export to JSON, CSV, or Excel, and start on Apify’s free monthly credit.
Related guides
How to Build a Deep-Research Retrieval Layer for AI Agents
Give an agent a topic and get ranked web sources, full-page Markdown, and recent news with citations — keyless multi-source retrieval for RAG and grounding.
How to Extract Structured Data from Any URL in 2026
Turn any web page into clean JSON without an LLM: parse schema.org JSON-LD, OpenGraph, tables, prices and contacts deterministically — no API key, no browser.
How to Add Live Web Search to AI Agents (No API Key)
Give your LLM fresh, citable search results without a Tavily or SerpAPI key. How live SERP extraction works: ranked results plus Markdown page content for RAG.