Phase 1 — no login required Phase 2 — API key required
This is a practice API built for the "Building Practical API Workflows for Public Health" workshops. It behaves like a real public-health data API — you'll ask it questions and it will hand back data — but everything here is safe to experiment on and won't break anything.
https://test-api.seminars.page
Everything you do starts by adding something to the end of this address.
There are two "resources" — think of each as a table you can ask questions of:
| Resource | What it contains | Address |
|---|---|---|
| Cases | Weekly reported counts of a few notifiable conditions, by state and county | /v1/cases |
| Immunizations | Monthly doses administered for a few vaccines, by state and county | /v1/immunizations |
Both resources now require you to prove you're allowed to ask for data. This happens in two steps, and it mirrors how most real public-health APIs work:
POST https://test-api.seminars.page/v1/token Header: X-API-Key: YOUR-CLASSROOM-KEY
Response:
{
"token": "3F82ac9K...",
"token_type": "Bearer",
"expires_in": 900
}
expires_in is in seconds — this token stops working after 15 minutes, and you'll
need to repeat this step to get a new one. That's deliberate: it's the whole point of the
"temporary credential" concept.
Add it as a header on /v1/cases and /v1/immunizations requests:
GET https://test-api.seminars.page/v1/cases?state=CA&limit=25 Header: Authorization: Bearer 3F82ac9K...
https://test-api.seminars.page/v1/token, and under "HTTP request header parameters" add X-API-Key with your classroom key. You'll need to switch the request to POST — Power Query's "From Web" dialog exposes this under its advanced options.token value out of the response/v1/cases or /v1/immunizations as before, and add a header named Authorization with the value Bearer followed by your token (note the space after "Bearer")https://test-api.seminars.page/v1/cases?state=CA&limit=25
This asks for up to 25 case records from California. Here's what comes back:
{
"data": [
{
"id": 1,
"state": "CA",
"county": "Los Angeles",
"report_date": "2026-06-01",
"case_type": "influenza-like illness",
"case_count": 12
}
],
"next_cursor": "MjU=",
"count": 25
}
| Parameter | Required? | What it does |
|---|---|---|
state | No | Two-letter state code, e.g. CA, TX, NY, FL, WA. Leave it out to get every state. |
limit | No | How many rows per page, from 1–50. Defaults to 25. The API will never hand back more than 50 rows in one response — see "Pagination" below. |
cursor | No | Where to pick up from. You get this value from the previous response's next_cursor — you never make it up yourself. |
https://test-api.seminars.page/v1/immunizations?county=King&limit=25
| Parameter | Required? | What it does |
|---|---|---|
county | No | County name, e.g. King, Harris, Erie. Leave it out to get every county. |
limit | No | Same as above — 1 to 50, default 25. |
cursor | No | Same as above. |
This API deliberately never hands you an entire dataset in one response, even if the whole thing would technically fit. Real public-health APIs work the same way, both to protect their servers and because most tools (Excel included) work better with data in manageable chunks.
Every response includes a next_cursor value. If it's a piece of text, there's more
data waiting — take that value and add it to your address as &cursor=THAT_VALUE to get
the next page. If next_cursor is null, you've reached the end.
https://test-api.seminars.page/v1/cases?state=CA&limit=25&cursor=MjU=
Each token can make up to 20 requests per minute. If you go over that, you'll get an error back instead of data, with a hint about how long to wait:
HTTP 429 Too Many Requests
Retry-After: 37
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Try again in 37 seconds."
}
}
This is normal, and it's there on purpose — real APIs do this to stay reliable for everyone
using them. Retry-After tells you how many seconds to wait before trying again.
| Status | Meaning |
|---|---|
| 400 | Something about your request doesn't make sense to the API — most often an invalid cursor. Try the request again without a cursor to start over. |
| 401 | Missing or invalid credentials. For /v1/token, check your X-API-Key header. For /v1/cases or /v1/immunizations, your token may be missing, mistyped, or expired — get a new one from /v1/token. |
| 404 | The address doesn't match a resource this API knows about. Double check /v1/cases or /v1/immunizations. |
| 429 | You've hit the rate limit — see above. Wait the number of seconds in Retry-After and try again. |