V3/V4 to V5 migration guide¶
The legacy Epidata APIs, including the V4 main endpoint (pub_covidcast())
and the V3 endpoints (pub_fluview(), pub_flusurv(), pub_wiki(), and so
on), are transitioning to the V5 API, served by epidata_snapshot(),
epidata_archive(), and epidata_meta(). The transition is happening source
by source. All V3 and V4 sources keep working until the migration is complete
(tentatively October 2026), and endpoints that are no longer updated remain
accessible on V3/V4. For new work, start on V5 and fall back to a legacy
method only for a source that is not yet available there.
Starting in October 2026, the V4 methods are tentatively deprecated in favor
of the V5 API, and calling them raises a UserWarning pointing back to this
guide.
For the current list of sources and indicators available on the new API, see the V5 signals documentation.
This guide walks through the transition from pub_covidcast() and the other
legacy methods. pub_covidcast() is the most widely used, but the V3 methods
differ in their names and argument conventions, so the tables below compare
both V4 (pub_covidcast()) and V3 (using pub_fluview() as the example) to
their V5 equivalents.
from epidatpy import EpiDataContext, EpiRange
epidata = EpiDataContext()
Endpoint mapping¶
The legacy endpoints split into several purpose-built V5 routes determined by
the kind of query. The “V3 (other endpoints)” column gives examples
(pub_fluview(), pub_flusurv(), pub_wiki()) to illustrate how much the
legacy endpoints differ from each other; check each method’s reference page
for its own behavior.
Task |
V4 ( |
V3 (other endpoints) |
V5 equivalent |
|---|---|---|---|
Fetch the latest data, or a snapshot as of a past date |
|
Endpoint-specific ( |
|
Fetch the full revision history of a signal |
|
Supported by some ( |
|
Discover sources, signals, geo types, and date ranges |
|
Shared meta for some ( |
|
Filter by publication lag |
|
Supported by some ( |
none (compute |
epidata() is a convenience wrapper that routes to epidata_archive() if you
pass report_time, or to epidata_snapshot() if you pass snapshot_date (or
neither).
Argument changes¶
Most pub_covidcast() arguments carry over to V5 under the same name, but
some have been renamed, dropped, or added. The V3 methods do not share
argument names with pub_covidcast(); pub_fluview() is shown as an example.
V4 argument ( |
V3 ( |
V5 argument |
Notes |
|---|---|---|---|
|
not exposed (implied by the method name) |
|
Identifies the source dataset in V5. |
|
none (implied by the endpoint) |
|
One or more signal names within the source, sent in a single request. |
|
not exposed ( |
|
Geographic resolution (e.g. |
|
|
|
The server returns all locations for the requested |
|
not exposed ( |
none |
Dropped. All V5 endpoints use calendar dates. |
|
|
|
Accepts dates or an |
|
none |
|
|
|
|
|
|
|
|
none |
Compute it yourself from |
none |
none |
|
New in V5. Selects the imputation variant ( |
none |
none |
|
New in V5. Caps the rows returned, for previewing a query. The query has no stable sort order, so it is not a filter. |
The new methods also add fill_method, which has no covidcast equivalent.
Some sources publish several variants of the same signal that differ in how
nulls were handled during geographic aggregation:
"source"is the raw source data, with no imputation"fill_ave"has null values filled with the average of neighboring values"fill_zero"has null values filled with zero
The default None returns all variants, so filter on this column (or pass a
value to the argument) if you want exactly one time series per location.
Column changes¶
Response fields follow a similar pattern. pub_fluview() again serves as the
V3 example; column names vary across legacy endpoints (pub_wiki(), for
instance, returns article, count, and hour).
V4 column ( |
V3 ( |
V5 column |
Notes |
|---|---|---|---|
|
not returned |
dropped |
You queried by source; add it back with |
|
none (implied by the endpoint) |
|
The signal name. |
|
endpoint-specific columns (e.g. |
|
One standardized value column across all V5 sources. |
not returned |
not returned |
|
Included in V5 responses. |
|
|
|
Standardized location identifier. |
|
|
|
The date the value describes. Always a date. |
|
|
|
When the value was published. A UTC timestamp ( |
|
|
dropped |
Compute as |
|
none |
dropped |
Was already deprecated in the covidcast API. |
|
none |
|
Uncertainty is now expressed as confidence-interval bounds on |
|
none |
dropped |
Missingness is now expressed through |
none |
none |
|
Which null-handling variant of the signal this row belongs to. See above. |
Some sources also carry extra columns in the new API, for example age_group
(pophive)
and nwss_source, sample_index, pcr_target
(nwss).
Each source’s documentation page lists its extra columns.
Uncertainty columns¶
The covidcast columns stderr and sample_size have no fixed replacement.
The shared schema carries only value; a source that quantifies uncertainty
adds its own columns, such as ci_lower and ci_upper. Use the metadata or
the documentation
to see which value columns a source returns:
meta_sleepcycle = epidata.epidata_meta(source="sleepcycle")
meta_sleepcycle["value_columns"]
['ci_lower', 'ci_upper', 'value']
A query, before and after¶
V4 example: NSSP through covidcast¶
Fetching NSSP influenza ED visit percentages for two states, as the data looked on January 1, 2025:
old = epidata.pub_covidcast(
data_source="nssp",
signals="pct_ed_visits_influenza",
geo_type="state",
time_type="week",
geo_values=["pa", "ca"],
time_values=EpiRange(202440, 202501),
as_of=20250101,
).df()
old.head()
/home/runner/work/epidatpy/epidatpy/epidatpy/_endpoints.py:786: UserWarning: `pub_covidcast` uses the V4 Epidata API. Starting in October 2026, V4 is tentatively deprecated in favor of the V5 API. See the migration guide (https://cmu-delphi.github.io/epidatpy/migration_guide.html) for the V5 endpoints and how to move to them.
_warn_v4_sunset("pub_covidcast")
/home/runner/work/epidatpy/epidatpy/epidatpy/_call.py:540: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
df[info.name] = to_datetime(df[info.name])
| source | signal | geo_type | geo_value | time_type | time_value | issue | lag | value | stderr | sample_size | direction | missing_value | missing_stderr | missing_sample_size | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | nssp | pct_ed_visits_influenza | state | ca | week | 202440 | 2026-03-08 | 103 | 0.14 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 1 | nssp | pct_ed_visits_influenza | state | pa | week | 202440 | 2026-03-08 | 103 | 0.05 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 2 | nssp | pct_ed_visits_influenza | state | ca | week | 202441 | 2026-03-08 | 102 | 0.14 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 3 | nssp | pct_ed_visits_influenza | state | pa | week | 202441 | 2026-03-08 | 102 | 0.07 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 4 | nssp | pct_ed_visits_influenza | state | ca | week | 202442 | 2026-03-08 | 101 | 0.16 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
new = epidata.epidata_snapshot(
source="nssp",
signals="pct_ed_visits_influenza",
geo_type="state",
geo_values=["pa", "ca"],
reference_time=EpiRange("2024-10-01", "2025-01-01"),
snapshot_date="2025-01-01",
).df()
new.head()
| signal | report_time | geo_type | geo_value | fill_method | reference_time | value | |
|---|---|---|---|---|---|---|---|
| 510 | pct_ed_visits_influenza | 2024-12-27 00:00:00+00:00 | state | ca | source | 2024-10-05 | 0.14 |
| 511 | pct_ed_visits_influenza | 2024-12-27 00:00:00+00:00 | state | ca | source | 2024-10-12 | 0.14 |
| 512 | pct_ed_visits_influenza | 2024-12-27 00:00:00+00:00 | state | ca | source | 2024-10-19 | 0.16 |
| 513 | pct_ed_visits_influenza | 2024-12-27 00:00:00+00:00 | state | ca | source | 2024-10-26 | 0.2 |
| 514 | pct_ed_visits_influenza | 2024-12-27 00:00:00+00:00 | state | ca | source | 2024-11-02 | 0.25 |
Both queries return the same signal, just with renamed and reshaped columns:
print(list(old.columns))
print(list(new.columns))
['source', 'signal', 'geo_type', 'geo_value', 'time_type', 'time_value', 'issue', 'lag', 'value', 'stderr', 'sample_size', 'direction', 'missing_value', 'missing_stderr', 'missing_sample_size']
['signal', 'report_time', 'geo_type', 'geo_value', 'fill_method', 'reference_time', 'value']
V3 example: FluView¶
For V3 endpoints like pub_fluview(), metrics that used to be separate
columns (num_ili, ili, wili, …) become individual signal names that
you request through signals, and the results land in the single value
column:
old_flu = epidata.pub_fluview(
regions="nat",
epiweeks=EpiRange(202440, 202445),
).df()
old_flu[["release_date", "region", "epiweek", "wili", "ili"]].head()
/home/runner/work/epidatpy/epidatpy/epidatpy/_endpoints.py:1205: UserWarning: `pub_fluview` uses the V4 Epidata API. Starting in October 2026, V4 is tentatively deprecated in favor of the V5 API. See the migration guide (https://cmu-delphi.github.io/epidatpy/migration_guide.html) for the V5 endpoints and how to move to them.
_warn_v4_sunset("pub_fluview")
| release_date | region | epiweek | wili | ili | |
|---|---|---|---|---|---|
| 0 | 2026-09-18 | nat | 202440 | 1.90509 | 1.8485 |
| 1 | 2026-09-18 | nat | 202441 | 2.02014 | 1.94416 |
| 2 | 2026-09-18 | nat | 202442 | 2.07037 | 2.00964 |
| 3 | 2026-09-18 | nat | 202443 | 2.22044 | 2.15518 |
| 4 | 2026-09-18 | nat | 202444 | 2.31725 | 2.23339 |
new_flu = epidata.epidata_snapshot(
source="fluview_ilinet",
signals="wili",
geo_type="nation",
geo_values="us",
reference_time=EpiRange("2024-10-01", "2024-11-15"),
).df()
new_flu.head()
| signal | report_time | geo_type | geo_value | fill_method | reference_time | value | age_group | |
|---|---|---|---|---|---|---|---|---|
| 1314 | wili | 2025-09-12 00:00:00+00:00 | nation | us | source | 2024-10-12 | 2.02014 | all |
| 1315 | wili | 2025-09-12 00:00:00+00:00 | nation | us | source | 2024-10-19 | 2.07037 | all |
| 1316 | wili | 2025-09-12 00:00:00+00:00 | nation | us | source | 2024-10-26 | 2.22044 | all |
| 1317 | wili | 2025-09-12 00:00:00+00:00 | nation | us | source | 2024-11-02 | 2.31725 | all |
| 1318 | wili | 2025-09-12 00:00:00+00:00 | nation | us | source | 2024-11-09 | 2.4719 | all |
Revision history queries¶
Where you passed issues to pub_covidcast(), use epidata_archive() with
report_time:
old_revisions = epidata.pub_covidcast(
data_source="nssp",
signals="pct_ed_visits_influenza",
geo_type="state",
time_type="week",
geo_values="pa",
time_values=EpiRange(202440, 202501),
issues=EpiRange(202440, 202522),
).df()
old_revisions.head()
/home/runner/work/epidatpy/epidatpy/epidatpy/_endpoints.py:786: UserWarning: `pub_covidcast` uses the V4 Epidata API. Starting in October 2026, V4 is tentatively deprecated in favor of the V5 API. See the migration guide (https://cmu-delphi.github.io/epidatpy/migration_guide.html) for the V5 endpoints and how to move to them.
_warn_v4_sunset("pub_covidcast")
/home/runner/work/epidatpy/epidatpy/epidatpy/_call.py:540: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
df[info.name] = to_datetime(df[info.name])
/home/runner/work/epidatpy/epidatpy/epidatpy/_call.py:540: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
df[info.name] = to_datetime(df[info.name])
| source | signal | geo_type | geo_value | time_type | time_value | issue | lag | value | stderr | sample_size | direction | missing_value | missing_stderr | missing_sample_size | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | nssp | pct_ed_visits_influenza | state | pa | week | 202440 | 202445 | 5 | 0.05 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 1 | nssp | pct_ed_visits_influenza | state | pa | week | 202440 | 202446 | 6 | 0.05 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 2 | nssp | pct_ed_visits_influenza | state | pa | week | 202440 | 202447 | 7 | 0.05 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 3 | nssp | pct_ed_visits_influenza | state | pa | week | 202440 | 202448 | 8 | 0.05 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
| 4 | nssp | pct_ed_visits_influenza | state | pa | week | 202440 | 202449 | 9 | 0.05 | <NA> | <NA> | <NA> | 0 | 1 | 1 |
revisions = epidata.epidata_archive(
source="nssp",
signals="pct_ed_visits_influenza",
geo_type="state",
geo_values="pa",
reference_time=EpiRange("2024-10-01", "2025-01-01"),
report_time="<2025-06-01",
).df()
revisions.head()
| signal | report_time | geo_type | geo_value | fill_method | reference_time | value | |
|---|---|---|---|---|---|---|---|
| 82644 | pct_ed_visits_influenza | 2024-11-08 00:00:00+00:00 | state | pa | source | 2024-10-05 | 0.05 |
| 82645 | pct_ed_visits_influenza | 2024-11-08 00:00:00+00:00 | state | pa | source | 2024-10-12 | 0.07 |
| 82646 | pct_ed_visits_influenza | 2024-11-08 00:00:00+00:00 | state | pa | source | 2024-10-19 | 0.08 |
| 82647 | pct_ed_visits_influenza | 2024-11-08 00:00:00+00:00 | state | pa | source | 2024-10-26 | 0.13 |
| 82648 | pct_ed_visits_influenza | 2024-11-08 00:00:00+00:00 | state | pa | source | 2024-11-02 | 0.14 |
If you filtered by lag, fetch the archive and filter afterwards. Because
report_time is a timezone-aware timestamp and reference_time a naive date,
drop the timezone before subtracting:
lag_days = (revisions["report_time"].dt.tz_localize(None) - revisions["reference_time"]).dt.days
# For an exact lag (e.g. 7 days):
revisions[lag_days == 7].head()
| signal | report_time | geo_type | geo_value | fill_method | reference_time | value | |
|---|---|---|---|---|---|---|---|
| 87897 | pct_ed_visits_influenza | 2024-11-23 00:00:00+00:00 | state | pa | source | 2024-11-16 | 0.17 |
| 99213 | pct_ed_visits_influenza | 2024-12-07 00:00:00+00:00 | state | pa | source | 2024-11-30 | 0.35 |
# Or for maximum latency (e.g. at most 7 days of delay):
revisions[lag_days <= 7].head()
| signal | report_time | geo_type | geo_value | fill_method | reference_time | value | |
|---|---|---|---|---|---|---|---|
| 82648 | pct_ed_visits_influenza | 2024-11-08 00:00:00+00:00 | state | pa | source | 2024-11-02 | 0.14 |
| 87897 | pct_ed_visits_influenza | 2024-11-23 00:00:00+00:00 | state | pa | source | 2024-11-16 | 0.17 |
| 99213 | pct_ed_visits_influenza | 2024-12-07 00:00:00+00:00 | state | pa | source | 2024-11-30 | 0.35 |
| 104608 | pct_ed_visits_influenza | 2024-12-13 00:00:00+00:00 | state | pa | source | 2024-12-07 | 0.55 |
| 110556 | pct_ed_visits_influenza | 2024-12-20 00:00:00+00:00 | state | pa | source | 2024-12-14 | 0.85 |
Checking whether a source is available¶
Use epidata_meta() to see what a source offers in the new API. It returns
the signals, geo types, and the available reference_time and report_time
ranges:
meta = epidata.epidata_meta(source="nssp")
# All the fields available for this source
list(meta)
['report_time_range',
'reference_time_range',
'signals',
'geo_types',
'key_columns',
'extra_key_columns',
'value_columns',
'column_types']
meta["signals"] # available signal names
['pct_ed_visits_ari',
'pct_ed_visits_combined',
'pct_ed_visits_covid',
'pct_ed_visits_influenza',
'pct_ed_visits_rsv',
'smoothed_pct_ed_visits_combined',
'smoothed_pct_ed_visits_covid',
'smoothed_pct_ed_visits_influenza',
'smoothed_pct_ed_visits_rsv']
meta["geo_types"] # supported geography levels
['census_division',
'census_region',
'county',
'hhs',
'hrr',
'hsa_nci',
'msa',
'nation',
'state']
meta["reference_time_range"] # earliest/latest reference_time available
{'latest': '2026-09-19', 'first': '2022-10-01'}
meta["report_time_range"] # earliest/latest report_time (publication instant, UTC) available
{'latest': '2026-09-23T00:00:00Z', 'first': '2024-04-18T00:00:00Z'}
Called with no argument, epidata_meta() returns the entries for every
available source, keyed by source name.
If epidata_meta() does not know the source yet, keep using
pub_covidcast() (or the relevant pub_*/pvt_* method) for it and check
back after package updates. The API mailing
list
announces sources as they move.
Endpoints kept for historical reference¶
Not every V4 endpoint is moving to V5. The methods below cover data sources whose collection has already ended (e.g. Google Flu Trends, the HealthTweets signal, the various nowcasts). They are not part of the V4-to-V5 transition, so they are not deprecated and will keep working. The historical data they return is frozen and will remain available. They will just no longer receive new data.
Method |
Data source |
|---|---|
|
CDC total and by-topic webpage visits |
|
COVID hospitalization facility lookup |
|
COVID hospitalizations by facility |
|
COVID hospitalizations by state |
|
Delphi’s ILINet outpatient doctor visits forecasts |
|
Delphi’s PAHO dengue nowcasts (Americas) |
|
PAHO dengue digital surveillance sensors (Americas) |
|
ECDC ILI incidence (Europe) |
|
Google Flu Trends flu search volume |
|
Google Health Trends health topics search volume |
|
KCDC ILI incidence (Korea) |
|
Metadata for the NoroSTAT endpoint |
|
NIDSS dengue cases (Taiwan) |
|
NIDSS flu doctor visits (Taiwan) |
|
CDC NoroSTAT norovirus outbreaks |
|
Delphi’s ILI Nearby nowcasts |
|
PAHO dengue data (Americas) |
|
Influenza and dengue digital surveillance sensors |
|
HealthTweets total and influenza-related tweets |
|
Wikipedia webpage counts by article |