L logiover
data · Jul 9, 2026 · 7 min read

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 & geographycountry (or US state name), continent, iso2 and iso3 country codes, and a flag image URL.
  • Case countscases (cumulative confirmed), todayCases, deaths, todayDeaths, recovered, and active (cases minus deaths minus recovered).
  • Severity & testingcritical (ICU patients), tests (total performed).
  • Population-adjusted ratioscasesPerMillion, deathsPerMillion, testsPerMillion, and population — the fields you need for fair cross-country comparison.
  • Vaccinationvaccinated (total doses) and vaccinatedPerHundred, populated in vaccine mode.
  • Time & provenancedate (for historical/vaccine rows), updated (date of last data update), and a mode tag 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. historical mode returns only cases, deaths, and recovered — not tests, population, or per-million ratios. vaccine mode returns vaccination figures. Fields not available in a given mode come back as null. 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 historical with lastDays: 0 across 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 usStates mode 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 historical and wonder where population and tests went, 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, TR resolve cleanly; “United States” vs “USA” may not. To confirm valid names, run an unfiltered countries pass first and read the country column.
  • lastDays: 0 can 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 countries unfiltered and group on the continent field 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