CDC WONDER query CLI — explore, build, and refine public health data queries
Project description
pulse
CDC WONDER public health query CLI — explore datasets, run bundled queries, and use Claude to build and refine custom XML queries for public health data that Americans should care about.
What is this?
CDC WONDER (Wide-ranging ONline Data for Epidemiologic Research) is the government's primary interface for public health statistics: drug overdose deaths, maternal mortality, birth rates, COVID deaths by race, suicide trends, vaccine adverse events, and much more. Its XML API is powerful but opaque.
pulse makes it usable:
- Explore all datasets with clear descriptions of what they cover and when
- Search by topic to find the right dataset or a working example query
- Run bundled, validated XML queries directly against the CDC API
- Build new queries from natural language using Claude
- Refine existing queries with conversational feedback
Setup
# Install (requires Python 3.12+)
uv sync
# For build/query/refine/compare/chat commands, set your Anthropic API key:
export ANTHROPIC_API_KEY=sk-ant-...
# or put it in a .env file:
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env
LLM provider
pulse defaults to Anthropic Claude but can also run against an Azure
OpenAI Foundry deployment (e.g. GPT-5.4). Select the provider with
LLM_PROVIDER (defaults to anthropic):
# Anthropic (default) — needs ANTHROPIC_API_KEY as above
# Azure OpenAI Foundry
export LLM_PROVIDER=azure_openai
export AZURE_OPENAI_API_KEY=...
export AZURE_OPENAI_ENDPOINT=https://<your-resource>.openai.azure.com
export AZURE_OPENAI_DEPLOYMENT=<your-gpt-5.4-deployment-name>
export AZURE_OPENAI_API_VERSION=<api-version-your-resource-supports>
All four AZURE_OPENAI_* variables are required when LLM_PROVIDER=azure_openai;
pulse will tell you which ones are missing. These can also go in a .env
file alongside ANTHROPIC_API_KEY.
If the LLM endpoint isn't directly reachable — e.g. an Azure OpenAI resource
with public network access disabled, requiring a private endpoint — bridge
the connection through a proxy with LLM_HTTP_PROXY. Applies to both
providers, and supports http://, https://, socks5://, and socks5h://
(DNS resolved through the proxy):
export LLM_HTTP_PROXY=socks5h://user:pass@host:port
Commands
pulse datasets — what's available
pulse datasets # all datasets
pulse datasets --topic Mortality # filter by topic
pulse datasets --json # JSON output
Shows all 26+ CDC WONDER datasets with: topic, year range, what the data covers, number of bundled example queries, and whether age-adjusted rates are available.
Topics: Mortality · Infant Mortality · Natality · Environment · Vaccine Safety · Infectious Disease
pulse info <ID> — deep dive on a dataset
pulse info D176 # Provisional Mortality (2018–present)
pulse info D66 # Natality / birth data
pulse info D8 # VAERS vaccine adverse events
Shows: subject description, available measures, key grouping dimensions, and all bundled example queries for that dataset.
pulse search "<topic>" — find what you need
pulse search "opioid overdose deaths by state"
pulse search "maternal mortality by race"
pulse search "birth rates 2010 to 2020"
pulse search "tick-borne disease cases" --queries # queries only
pulse search "recent COVID deaths" --datasets # datasets only
pulse list-queries — all bundled example queries
pulse list-queries
pulse list-queries --dataset D176 # filter by dataset
23 working XML queries covering: drug/opioid/fentanyl deaths, maternal mortality, births, COVID deaths by race, suicide, tick-borne diseases, racial mortality gap, infant mortality, heart disease vs. cancer, and more.
pulse run <query> — execute a query
# Run a bundled query by filename (no path needed)
pulse run drug-deaths-by-year-2018-2024-req.xml
# Output formats
pulse run opioid-overdose-deaths-2018-2024-req.xml -f csv
pulse run mortality-by-year-cause-2021-2024-req.xml -f json
pulse run births-by-year-2007-2024-req.xml -f table -o births.csv
# Run your own query file
pulse run /path/to/my-query.xml
Hits the live CDC WONDER API. No login required; CDC requires a ~2-minute cooldown between queries.
pulse build "<description>" — build a query with Claude
# Requires ANTHROPIC_API_KEY
pulse build "drug overdose deaths by state and year 2018-2023"
pulse build "maternal mortality by race, 2018-2023" -o maternal-race.xml
pulse build "birth rates by age of mother 2010 to 2024" --no-suggest
Suggests closest existing queries first, then calls Claude to build a new XML query. The LLM selects the right dataset and generates overrides merged onto a validated base template.
pulse query "<description>" — build and run in one step
pulse query "fentanyl deaths by state 2020-2024" -f csv
pulse query "infant mortality by race 2018-2023" --save-xml infant-race.xml
pulse refine <file> "<feedback>" — iterate on a query
pulse refine opioid-overdose-deaths-2018-2024-req.xml "break it down by state"
pulse refine drug-deaths-by-year-2018-2024-req.xml "add sex breakdown" -o drug-sex.xml
pulse refine drug-deaths-by-year-2018-2024-req.xml "show monthly not yearly" --run -f csv
Testing
uv run pytest # unit tests only — fast, no network (default)
uv run pytest -m integration # + integration tests (see below)
Unit tests cover catalog/matcher lookups, XML template merging (including the CDC WONDER radio-button-trap regression), AAR constraints, provider selection, and the offline-network-free CLI commands.
Integration tests (tests/integration/) are excluded by default and split
into two kinds:
test_socks_proxy_integration.py— always runs. Spins up a local SOCKS5 relay and a local mock LLM HTTP server, so it genuinely exercisesLLM_HTTP_PROXYend-to-end (real SOCKS handshake, real HTTP request/response) without needing real Azure/Anthropic credentials.test_llm_provider_live.py— hits whateverANTHROPIC_API_KEY/LLM_PROVIDER=azure_openai+AZURE_OPENAI_*/LLM_HTTP_PROXYyou actually have configured. Skips if credentials aren't set; also skips (rather than fails) if the provider is reachable but blocked at the network layer (e.g. an Azure OpenAI resource with public access disabled and no working proxy) — that's an environment gap, not a code defect.
Bundled Datasets (with base templates)
| ID | Subject | Years |
|---|---|---|
| D176 | Provisional mortality — opioids, COVID, suicide, heart disease | 2018–present |
| D157 | Final mortality, single race (MCD+UCD) | 2018–2023 |
| D158 | Underlying cause of death, single race — maternal mortality | 2018–2023 |
| D77 | Multiple cause of death — drug deaths (historical) | 1999–2020 |
| D76 | Underlying cause of death — suicide, cancer (historical) | 1999–2020 |
| D141 | MCD with US-Mexico border regions | 1999–2020 |
| D140 | Compressed mortality ICD-10 | 1999–2016 |
| D16 | Compressed mortality ICD-9 | 1979–1998 |
| D74 | Compressed mortality ICD-8 | 1968–1978 |
| D69 | Linked birth/infant death records | 2007–2023 |
| D159 | Linked birth/infant death, expanded race | 2017–2023 |
| D31/D18/D23 | Linked birth/infant death (historical) | 1995–2006 |
| D66 | Natality — birth rates, birth outcomes | 2007–2024 |
| D149 | Natality, expanded race detail | 2016–2024 |
| D192 | Provisional natality (monthly) | 2023–present |
| D27/D10 | Natality (historical) | 1995–2006 |
| D8 | VAERS vaccine adverse events | 1990–present |
| D104 | Heat wave days by county | 1981–2010 |
| D60/D80/D81 | NLDAS temperature, sunlight, precipitation | 1979–2011 |
| D73 | PM2.5 fine particulate matter | 2003–2011 |
| D61 | MODIS land surface temperature | 2003–2008 |
Public Health Questions You Can Answer
- How did opioid overdose deaths trend from 1999 to today, broken down by drug type?
- What is the racial gap in COVID-19 mortality?
- How does maternal mortality differ by race and state?
- Which states have the highest suicide rates by sex?
- How have birth rates changed by age of mother since 1995?
- Are tick-borne disease cases increasing?
- How do PM2.5 air quality levels correlate with where people live?
- What are the most common adverse events reported after COVID vaccines?
Releasing
Releases are cut by pushing a tag. publish.yml (single workflow, one run
per tag) handles the rest as three sequential jobs:
- Bump
versioninpyproject.toml, commit it. git tag vX.Y.Z && git push origin vX.Y.Zbuildbuilds the sdist/wheel, failing fast if the tag doesn't matchpyproject.toml's version.release(needsbuild) creates the GitHub Release with the built artifacts attached — the source of truth for what shipped.publish(needsrelease) publishes those same artifacts to PyPI (pulse-code) via trusted publishing (OIDC) against theprodenvironment — no API tokens stored in the repo.
The needs: chain means a failure at any step blocks everything after it —
e.g. a PyPI hiccup can't leave a GitHub Release around for a package that
isn't actually installable. If the publish job fails after release
succeeds, use "Re-run failed jobs" on that workflow run rather than
re-tagging. PyPI publishing is immutable: once a version is published it
can't be re-uploaded, so a bad release means bumping to a new version.
Based On
Built using fartbagxp/health as reference — a comprehensive collection of CDC data pipelines and the CDC WONDER XML API client and LLM query builder this tool builds on.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pulse_code-1.0.1.tar.gz.
File metadata
- Download URL: pulse_code-1.0.1.tar.gz
- Upload date:
- Size: 74.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af252854a8abfc81c9491899b4496913f811c59b5f39ff8088d2b12577b451dc
|
|
| MD5 |
010e0222e6aa8e8bbb9036ff48277062
|
|
| BLAKE2b-256 |
9c5ed36ec672517e09e066ceccc2be7e6f5b711d6a0e7be0a4a6577b1c3ffc27
|
Provenance
The following attestation bundles were made for pulse_code-1.0.1.tar.gz:
Publisher:
publish.yml on fartbagxp/pulse-code
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pulse_code-1.0.1.tar.gz -
Subject digest:
af252854a8abfc81c9491899b4496913f811c59b5f39ff8088d2b12577b451dc - Sigstore transparency entry: 2047108321
- Sigstore integration time:
-
Permalink:
fartbagxp/pulse-code@3922fd6ca151929c0de7b6155962c80c09437973 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/fartbagxp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3922fd6ca151929c0de7b6155962c80c09437973 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pulse_code-1.0.1-py3-none-any.whl.
File metadata
- Download URL: pulse_code-1.0.1-py3-none-any.whl
- Upload date:
- Size: 111.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
863e9672eac609a4a6092b126035bdf940f9faa21769e20b1c0d396af101bf6e
|
|
| MD5 |
2d48099eb694a8d6f2cd51b0c15606da
|
|
| BLAKE2b-256 |
2ad38894ffef2c0a9fe6f0929a58e2947bf98aa09a21b8bfa92dac7a6a7939f6
|
Provenance
The following attestation bundles were made for pulse_code-1.0.1-py3-none-any.whl:
Publisher:
publish.yml on fartbagxp/pulse-code
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pulse_code-1.0.1-py3-none-any.whl -
Subject digest:
863e9672eac609a4a6092b126035bdf940f9faa21769e20b1c0d396af101bf6e - Sigstore transparency entry: 2047108526
- Sigstore integration time:
-
Permalink:
fartbagxp/pulse-code@3922fd6ca151929c0de7b6155962c80c09437973 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/fartbagxp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3922fd6ca151929c0de7b6155962c80c09437973 -
Trigger Event:
push
-
Statement type: