Skip to contents

The epidatr package provides access to all the endpoints of the Delphi Epidata API, and can be used to make requests for specific signals on specific dates and in select geographic regions. It is widely used in epidemiological research, real-time forecasting models, and public health dashboards.

Setup

Installation

You can install the stable version of this package from CRAN:

# Install from CRAN
install.packages("epidatr")
# or using pak
pak::pkg_install("epidatr")
# or using renv
renv::install("epidatr")

Or if you want the development version, install from GitHub:

# Install the dev version using pak
pak::pkg_install("cmu-delphi/epidatr@dev")
# or using remotes
remotes::install_github("cmu-delphi/epidatr", ref = "dev")
# or using renv
renv::install("cmu-delphi/epidatr@dev")

API Keys

The Delphi API requires a (free) API key for full functionality. While most endpoints are available without one, there are limits on API usage for anonymous users, including a rate limit.

To generate your key, register for a pseudo-anonymous account. See the save_api_key() function documentation for details on how to set up epidatr to use your API key.

The Delphi V5 API

epidatr allows three categories of data access to the Delphi V5 API:

  • epidata_snapshot() provides a specific view of how a dataset looked at a point in time.
  • epidata_archive() fetches all versions of a dataset across time, representing the full revision history.
  • epidata_aux() accesses source-specific auxiliary tables containing metadata, laboratory protocols, or additional static keys (such as NWSS wastewater facility descriptions).

Additionally, epidata_meta() provides access to system metadata to list available sources, signals, geographic granularities, and date ranges.

Basic Usage

To make a request of a particular data source at a specific point in time, we’ll use epidata_snapshot(). This function needs the source name, signal name, and a geographic level in order to complete a query.

Suppose we are interested in the nssp source, which provides access to a wide range of emergency department visits data:

library(epidatr)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

# Obtain the latest snapshot of the influenza ED-visit percentage
# from the NSSP source for the US
epidata <- epidata_snapshot(
  source = "nssp",
  signals = "pct_ed_visits_influenza",
  geo_type = "nation"
)
knitr::kable(head(epidata))
signal report_time geo_type geo_value fill_method reference_time value
pct_ed_visits_influenza 2024-04-18 12:00:00 nation us source 2022-10-01 0.48
pct_ed_visits_influenza 2024-04-18 12:00:00 nation us source 2022-10-15 0.90
pct_ed_visits_influenza 2024-04-18 12:00:00 nation us source 2023-01-28 0.55
pct_ed_visits_influenza 2024-04-18 12:00:00 nation us source 2023-02-04 0.47
pct_ed_visits_influenza 2024-04-18 12:00:00 nation us source 2023-02-18 0.36
pct_ed_visits_influenza 2024-04-18 12:00:00 nation us source 2023-02-25 0.34

epidata_snapshot() returns a tibble (a modern reimagining of R’s standard data frame that prints cleanly and preserves typed columns). (Here we’re using knitr::kable() to make it more readable.) Each row represents one observation for the US on one date. The location is given in the geo_value column, the date it describes in the reference_time column, the value of the requested signal in value, and the publication date in report_time.

The Delphi V5 API makes signals available at different geographic levels, depending on the source. Use epidata_meta("nssp") for a given source to see which geo types it supports.

To request signals for all states instead of the entire US, we use the geo_type argument. This automatically returns all available data for that geo type:

# Obtain the latest snapshot of the influenza ED-visit percentage
# from the NSSP source across all available dates and states
epidata_snapshot(
  source = "nssp",
  signals = "pct_ed_visits_influenza",
  geo_type = "state"
)
#> # A tibble: 10,905 × 7
#>   signal report_time         geo_type geo_value fill_method reference_time value
#>   <chr>  <dttm>              <chr>    <chr>     <chr>       <date>         <dbl>
#> 1 pct_e… 2024-05-03 12:00:00 state    de        source      2024-03-02      4.82
#> 2 pct_e… 2024-05-03 12:00:00 state    ks        source      2024-03-23      2.09
#> 3 pct_e… 2024-05-03 12:00:00 state    mn        source      2022-10-29      1.14
#> 4 pct_e… 2024-05-03 12:00:00 state    mn        source      2024-03-30      2.89
#> # ℹ 10,901 more rows

You can also query multiple signals in a single request by passing a vector to signals:

# Obtain both influenza and COVID-19 ED-visit percentages in a single query
epidata_snapshot(
  source = "nssp",
  signals = c("pct_ed_visits_influenza", "pct_ed_visits_covid"),
  geo_type = "state",
  geo_values = "pa",
  reference_time = epirange("2024-12-01", "2024-12-15")
)
#> # A tibble: 4 × 7
#>   signal report_time         geo_type geo_value fill_method reference_time value
#>   <chr>  <dttm>              <chr>    <chr>     <chr>       <date>         <dbl>
#> 1 pct_e… 2025-01-17 12:00:00 state    pa        source      2024-12-07     0.570
#> 2 pct_e… 2025-01-24 12:00:00 state    pa        source      2024-12-07     0.75 
#> 3 pct_e… 2025-01-24 12:00:00 state    pa        source      2024-12-14     0.820
#> 4 pct_e… 2025-01-24 12:00:00 state    pa        source      2024-12-14     0.870

Alternatively, we can fetch the time series for a subset of states and reference dates by listing out the desired locations in the geo_values argument and using a range in the reference_time argument:

# Obtain the data from January 1st, 2024 to January 1st, 2025
# of the influenza ED-visit percentage from the NSSP source for 
# Pennsylvania, California, and Florida
epidata_snapshot(
  source = "nssp",
  signals = "pct_ed_visits_influenza",
  geo_type = "state",
  geo_values = c("pa", "ca", "fl"),
  reference_time = epirange("2024-01-01", "2025-01-01")
)
#> # A tibble: 156 × 7
#>   signal report_time         geo_type geo_value fill_method reference_time value
#>   <chr>  <dttm>              <chr>    <chr>     <chr>       <date>         <dbl>
#> 1 pct_e… 2024-05-03 12:00:00 state    pa        source      2024-03-23     1.76 
#> 2 pct_e… 2024-05-17 12:00:00 state    pa        source      2024-04-13     0.830
#> 3 pct_e… 2024-04-18 12:00:00 state    fl        source      2024-01-06     4.83 
#> 4 pct_e… 2024-04-18 12:00:00 state    fl        source      2024-01-13     3.69 
#> # ℹ 152 more rows

Getting versioned data

The Delphi V5 API stores a historical record of all data, including corrections and updates, which is particularly useful for accurately backtesting forecasting models. To retrieve versioned data in epidata_snapshot(), we can use the snapshot_date argument, which fetches the data as it was known on a specific date.

# Obtain the influenza ED-visit percentage from NSSP for Pennsylvania
# as it was known on 2025-01-01
epidata_snapshot(
  source = "nssp",
  signals = "pct_ed_visits_influenza",
  geo_type = "state",
  geo_values = "pa",
  snapshot_date = "2025-01-01"
)
#> # A tibble: 117 × 7
#>   signal report_time         geo_type geo_value fill_method reference_time value
#>   <chr>  <dttm>              <chr>    <chr>     <chr>       <date>         <dbl>
#> 1 pct_e… 2024-04-18 12:00:00 state    pa        source      2022-10-01     0.120
#> 2 pct_e… 2024-04-18 12:00:00 state    pa        source      2022-10-08     0.100
#> 3 pct_e… 2024-04-18 12:00:00 state    pa        source      2022-10-15     0.210
#> 4 pct_e… 2024-04-18 12:00:00 state    pa        source      2022-10-22     0.330
#> # ℹ 113 more rows

To request all versions of the data issued within a specific time range, we use epidata_archive() with the report_time argument. This parameter allows us to fetch versions using comparison operators (e.g., "<2025-01-15") or an epirange().

# See how the estimate for a SINGLE reference date (2024-12-07) evolved
# by fetching all reports issued in December 2024 and early January 2025
epidata_archive(
  source = "nssp",
  signals = "pct_ed_visits_influenza",
  geo_type = "state",
  geo_values = "pa",
  reference_time = "2024-12-07",
  report_time = epirange("2024-12-01", "2025-01-15")
)
#> # A tibble: 2 × 7
#>   signal report_time         geo_type geo_value fill_method reference_time value
#>   <chr>  <dttm>              <chr>    <chr>     <chr>       <date>         <dbl>
#> 1 pct_e… 2024-12-20 12:00:00 state    pa        source      2024-12-07     0.560
#> 2 pct_e… 2024-12-13 12:00:00 state    pa        source      2024-12-07     0.550

See vignette("versioned-data") for details and more ways to specify versioned data.

Auxiliary data

Some sources include extra columns connected to the signal data, such as the population served by each NWSS sewershed or site metadata. epidata_aux() retrieves this auxiliary data, either on its own or merged onto a signal pull.

You can pull auxiliary data directly by source. To see what key columns you can filter on for a given source, consult that source’s page in the V5 signals documentation. Named filters on these key columns can be passed through ... to limit the returned rows, and columns can be used to select specific fields:

aux_data <- epidata_aux(
  source = "nwss",
  pcr_target = "sars-cov-2",
  sample_index = c("92012", "92013")
)
knitr::kable(head(aux_data))
report_time geo_value reference_time nwss_source sample_index pcr_target report_ts_nominal_end state_territory county_fips counties_served population_served sample_type sample_matrix sample_location flow_rate concentration_method pasteurized pcr_type extraction_method major_lab_method inhibition_detect inhibition_adjust ntc_amplify pcr_gene_target_agg pcr_target_units lod_sewage hum_frac_target_mic hum_frac_mic_conc hum_frac_mic_unit rec_eff_percent rec_eff_target_name rec_eff_spike_matrix rec_eff_spike_conc pipeline_run_id report_ts_actual comments
2026-11-06 16:19:20 162 2026-01-27 CDC_Verily 92012 sars-cov-2 NA ca 06079 San Luis Obispo 14465 24-hr time-weighted composite post grit removal wwtp 0.45 ceres nanotrap f ddpcr thermo magmax viral/pathogen nucleic acid isolation kit 2 f f f n copies/l wastewater 1500 pepper mild mottle virus 153424325.78461 copies/l wastewater 40.7 bcov vaccine clarified sample 5 31694 2026-11-06 16:19:20 NA
2026-06-26 12:00:00 162 2026-01-27 CDC_Verily 92012 sars-cov-2 2026-11-06 16:19:20 ca 06079 San Luis Obispo 14465 24-hr time-weighted composite post grit removal wwtp 0.45 ceres nanotrap f ddpcr thermo magmax viral/pathogen nucleic acid isolation kit 2 f f f n copies/l wastewater 1500 pepper mild mottle virus 153424325.78462 copies/l wastewater 40.7 bcov vaccine clarified sample 5 31694 2026-11-06 16:19:20 NA
2026-06-19 12:00:00 162 2026-01-27 CDC_Verily 92012 sars-cov-2 2026-06-26 00:00:00 ca 06079 San Luis Obispo 14465 24-hr time-weighted composite post grit removal wwtp 0.45 ceres nanotrap f ddpcr thermo magmax viral/pathogen nucleic acid isolation kit 2 f f f n copies/l wastewater 1500 pepper mild mottle virus 153424325.78462 copies/l wastewater 40.7 bcov vaccine clarified sample 5 7895 2026-06-26 21:02:14 NA
2026-06-12 12:00:00 162 2026-01-27 CDC_Verily 92012 sars-cov-2 2026-06-19 00:00:00 ca 06079 San Luis Obispo 14465 24-hr time-weighted composite post grit removal wwtp 0.45 ceres nanotrap f ddpcr thermo magmax viral/pathogen nucleic acid isolation kit 2 f f f n copies/l wastewater 1500 pepper mild mottle virus 153424325.78462 copies/l wastewater 40.7 bcov vaccine clarified sample 5 7879 2026-06-26 21:00:39 NA
2026-06-05 12:00:00 162 2026-01-27 CDC_Verily 92012 sars-cov-2 2026-06-12 00:00:00 ca 06079 San Luis Obispo 14465 24-hr time-weighted composite post grit removal wwtp 0.45 ceres nanotrap f ddpcr thermo magmax viral/pathogen nucleic acid isolation kit 2 f f f n copies/l wastewater 1500 pepper mild mottle virus 153424325.78462 copies/l wastewater 40.7 bcov vaccine clarified sample 5 7868 2026-06-26 20:59:33 NA
2026-05-30 12:00:00 162 2026-01-27 CDC_Verily 92012 sars-cov-2 2026-06-05 00:00:00 ca 06079 San Luis Obispo 14465 24-hr time-weighted composite post grit removal wwtp 0.45 ceres nanotrap f ddpcr thermo magmax viral/pathogen nucleic acid isolation kit 2 f f f n copies/l wastewater 1500 pepper mild mottle virus 153424325.78462 copies/l wastewater 40.7 bcov vaccine clarified sample 5 7858 2026-06-26 20:58:46 NA

You can also attach auxiliary columns directly to a signal pull by passing the output of epidata_snapshot() or epidata_archive() directly to epidata_aux(). In this workflow, epidata_aux() fetches the matching auxiliary data and left-joins it onto the shared key columns:

# Fetch signal data for a specific sewershed
nwss_data <- epidata_snapshot(
  source = "nwss",
  signals = "covid_avg_conc",
  geo_type = "sewershed",
  geo_values = "128",
  reference_time = epirange("2024-12-01", "2025-01-01")
)
head(nwss_data)
#> # A tibble: 6 × 10
#>   signal       report_time         geo_type geo_value fill_method reference_time
#>   <chr>        <dttm>              <chr>    <chr>     <chr>       <date>        
#> 1 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-19    
#> 2 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-03    
#> 3 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-17    
#> 4 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-12    
#> # ℹ 2 more rows
#> # ℹ 4 more variables: nwss_source <chr>, sample_index <chr>, pcr_target <chr>,
#> #   value <dbl>

# Attach auxiliary metadata
nwss_merged <- nwss_data %>%
  epidata_aux()
head(nwss_merged)
#> # A tibble: 6 × 40
#>   signal       report_time         geo_type geo_value fill_method reference_time
#>   <chr>        <dttm>              <chr>    <chr>     <chr>       <date>        
#> 1 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-19    
#> 2 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-03    
#> 3 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-17    
#> 4 covid_avg_c… 2026-06-26 12:00:00 sewersh… 128       source      2024-12-12    
#> # ℹ 2 more rows
#> # ℹ 34 more variables: nwss_source <chr>, sample_index <chr>, pcr_target <chr>,
#> #   value <dbl>, report_ts_nominal_end <chr>, state_territory <chr>,
#> #   county_fips <chr>, counties_served <chr>, population_served <chr>,
#> #   sample_type <chr>, sample_matrix <chr>, sample_location <chr>,
#> #   flow_rate <chr>, concentration_method <chr>, pasteurized <chr>,
#> #   pcr_type <chr>, extraction_method <chr>, major_lab_method <chr>, …

If you don’t pass explicit key filters, epidata_aux() automatically infers them from the base dataset.

Advanced queries

Server-side key filtering

Beyond standard arguments (source, signals, geo_type), some sources include extra key dimensions that categorize the data (for example, nwss categorizes by pcr_target, and pophive categorizes by age_group). To see what key columns are available per source, consult that source’s page in the V5 signals documentation. You can pass these extra dimensions directly as named parameters to filter the query server-side:

epidata_snapshot(
  source = "nwss",
  signals = "pcr_conc_smoothed",
  geo_type = "county",
  pcr_target = c("sars-cov-2", "influenza")
)

Dry runs

If you want to inspect the generated API request URL underlying each function query without actually fetching data, you can pass dry_run = TRUE via fetch_args_list(). This works with epidata_snapshot(), epidata_archive(), and epidata_aux():

dry_run_call <- epidata_snapshot(
  source = "nssp",
  signals = "pct_ed_visits_influenza",
  geo_type = "state",
  fetch_args = fetch_args_list(dry_run = TRUE)
)
dry_run_call
#> 
#> ── <epidata_call> object: ──────────────────────────────────────────────────────
#> • Pipe this object into `fetch()` to actually fetch the data
#> • Request URL:
#>   https://delphi.cmu.edu/epidata/v5/snapshot/?source=nssp&signal=pct_ed_visits_influenza&geo_type=state

Plotting

Because the output data is in a standard tibble format, we can easily plot it using ggplot2:

library(ggplot2)

# Plot the influenza ED-visit time series fetched earlier with epidata_snapshot()
ggplot(epidata, aes(x = reference_time, y = value)) +
  geom_line() +
  labs(
    title = "Influenza ED visits from NSSP",
    subtitle = paste0(
      "US, ", min(epidata$reference_time), " to ", max(epidata$reference_time)
    ),
    x = "Date",
    y = "% of ED visits"
  )

Line chart of the weekly percentage of US emergency department visits due to influenza, from NSSP.

ggplot2 can also be used with epidatr and maps to create choropleths:

library(epidatr)
library(dplyr)
library(ggplot2)
library(maps)

# Obtain the latest snapshot of confirmed flu hospital admissions
# from NHSN for all states on a single reference date
nhsn_states <- epidata_snapshot(
  source = "nhsn",
  signals = "confirmed_admissions_flu_ew",
  geo_type = "state",
  geo_values = "*",
  reference_time = "2025-01-04"
)

# Get a mapping of states to longitude/latitude coordinates
states_map <- map_data("state")

# Convert state abbreviations into state names
nhsn_states <- mutate(
  nhsn_states,
  state = ifelse(
    geo_value == "dc",
    "district of columbia",
    state.name[match(geo_value, tolower(state.abb))] %>% tolower()
  )
)

# Add coordinates for each state
nhsn_states <- left_join(states_map, nhsn_states, by = c("region" = "state"))

# Plot
ggplot(nhsn_states, aes(x = long, y = lat, group = group, fill = value)) +
  geom_polygon(colour = "black", linewidth = 0.2) +
  coord_map("polyconic") +
  labs(
    title = "Confirmed flu hospital admissions from NHSN",
    subtitle = "All states, 2025-01-04",
    x = "Longitude",
    y = "Latitude"
  )

US map of confirmed influenza hospital admissions by state for the week ending 2025-01-04, from NHSN.

Plotting revision histories

We can also visualize revision histories from epidata_archive(). The archive lists a row only when a value is first reported or revised, so to see the whole time series as it looked on each release date, keep the latest value of each reference date reported up to then:

# Fetch every version of Pennsylvania influenza ED visits reported before 2025
pa_revisions <- epidata_archive(
  source = "nssp",
  signals = "pct_ed_visits_influenza",
  geo_type = "state",
  geo_values = "pa",
  reference_time = epirange("2024-10-01", "2024-12-01"),
  report_time = "<2025-01-01"
)

# Rebuild the series as known on each release date
release_dates <- sort(unique(pa_revisions$report_time))
pa_as_of <- bind_rows(lapply(release_dates, function(release) {
  pa_revisions %>%
    filter(report_time <= release) %>%
    group_by(reference_time) %>%
    slice_max(report_time, n = 1) %>%
    ungroup() %>%
    mutate(as_of = as.Date(release))
}))

ggplot(pa_as_of, aes(x = reference_time, y = value, color = as.factor(as_of))) +
  geom_line() +
  geom_point(size = 1) +
  labs(
    title = "Revisions of NSSP influenza ED visits in Pennsylvania",
    subtitle = "October-November 2024 estimates as known on each release date",
    x = "Observation Date",
    y = "% of ED visits",
    color = "As of"
  )

Line chart of Pennsylvania influenza ED-visit percentages for October-November 2024, with one line per release date showing the series as it was known then; later releases extend the series and revise recent values.

Available data sources and endpoints

epidatr provides access to a broad collection of epidemiological data streams:

See vignette("signal-discovery") for an in-depth guide to discovering signals, browsing metadata, and querying datasets across these categories. If you have existing pub_covidcast() code, see vignette("migration-guide") for the argument and column mapping to the V5 functions.