FRED series without an API key: fredgraph.csv, and when it hands you a zip
Checked 2026-09-08 · Statistical agencies: BLS, Census, BEA, FRED and EIA
The FRED JSON API refuses every keyless call with HTTP 400. The chart download endpoint https://fred.stlouisfed.org/graph/fredgraph.csv?id=SERIES needs no key and returns the full history as CSV, and cosd and coed trim the range. Ask for two series of different frequency in one call and the same URL returns a zip archive of separate CSVs with content-type application/zip - still HTTP 200, still named .csv in the path.
What we saw
The JSON API rejects a keyless observations call with HTTP 400 and a message naming the missing variable. Source (response body, 2026-09-08).
{"error_code":400,"error_message":"Bad Request. Variable api_key is not set. Read https:\/\/fred.stlouisfed.org\/docs\/api\/api_key.html for more information."}
HTTP 400A well-formed but unregistered 32-character key gives a different message at the same status. Source (response body, 2026-09-08).
{"error_code":400,"error_message":"Bad Request. The value for variable api_key is not registered. Read https:\/\/fred.stlouisfed.org\/docs\/api\/api_key.html for more information."}
HTTP 400fredgraph.csv serves one series keyless as CSV with a date column and the series id as the value column, back to the series start. Source (first lines, 2026-09-08).
observation_date,UNRATE 1948-01-01,3.4 1948-02-01,3.8 1948-03-01,4.0 1948-04-01,3.9
Two series of the same frequency come back as one wide CSV, with an empty cell where a series has no observation for that date. Source (headers and first rows, 2026-09-08).
content-type: application/csv content-disposition: attachment; filename="fredgraph.csv" observation_date,UNRATE,PAYEMS 1939-01-01,,29923 1939-02-01,,30100
Two series of different frequency come back as a zip archive containing README.txt, monthly.csv and quarterly.csv, at HTTP 200. Source (response headers and first bytes, 2026-09-08).
HTTP/2 200 content-type: application/zip content-disposition: attachment; filename="fredgraph.zip" PK README.txt ... monthly.csv ... quarterly.csv
cosd and coed trim the range on a single-series request. Source (first lines, 2026-09-08).
observation_date,UNRATE 2025-01-01,4.0 2025-02-01,4.2 2025-03-01,4.2
cosd is ignored once a second series id is added: the same date filter that trimmed a single series returns full history for two. Passing cosd twice does not fix it. Source (first lines, 2026-09-08).
=== two ids one cosd === observation_date,UNRATE,PAYEMS 1939-01-01,,29923 1939-02-01,,30100 === two ids two cosd === observation_date,UNRATE,PAYEMS 1939-01-01,,29923 1939-02-01,,30100
A series id that does not exist returns HTTP 404 and an HTML page, not a CSV with an error row. Source (status and content-type, 2026-09-08).
HTTP/2 404 content-type: text/html; charset=UTF-8
The command
curl -sS -w '\nHTTP %{http_code}\n' 'https://api.stlouisfed.org/fred/series/observations?series_id=UNRATE&file_type=json'{"error_code":400,"error_message":"Bad Request. Variable api_key is not set. Read https:\/\/fred.stlouisfed.org\/docs\/api\/api_key.html for more information."}
HTTP 400Checked 2026-09-08.
curl -sS 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE' | head -5
observation_date,UNRATE 1948-01-01,3.4 1948-02-01,3.8 1948-03-01,4.0 1948-04-01,3.9
Checked 2026-09-08.
curl -sS -D - -o /dev/null 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE' | grep -iE 'content-type|content-disposition'; curl -sS -D - -o /dev/null 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE,GDPC1' | grep -iE 'content-type|content-disposition'
content-type: application/csv content-disposition: attachment; filename="UNRATE.csv" ---multi--- content-type: application/zip content-disposition: attachment; filename="fredgraph.zip"
Checked 2026-09-08.
curl -sS 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=UNRATE,PAYEMS&cosd=2025-01-01' | head -3
observation_date,UNRATE,PAYEMS 1939-01-01,,29923 1939-02-01,,30100
Checked 2026-09-08.
curl -sS -o /tmp/bad.out -w 'HTTP %{http_code}\n' -D - 'https://fred.stlouisfed.org/graph/fredgraph.csv?id=NOTASERIESXYZ' | grep -iE 'HTTP/|content-type'HTTP/2 404 content-type: text/html; charset=UTF-8
Checked 2026-09-08.
| request | HTTP | content-type | filename | shape |
|---|---|---|---|---|
| id=UNRATE | 200 | application/csv | UNRATE.csv | observation_date,UNRATE |
| id=UNRATE,PAYEMS (both monthly) | 200 | application/csv | fredgraph.csv | one wide table, blanks where a series is short |
| id=UNRATE,GDPC1 (monthly + quarterly) | 200 | application/zip | fredgraph.zip | README.txt, monthly.csv, quarterly.csv |
| id=UNRATE&cosd=2025-01-01&coed=2025-06-01 | 200 | application/csv | UNRATE.csv | range honoured |
| id=UNRATE,PAYEMS&cosd=2025-01-01 | 200 | application/csv | fredgraph.csv | range ignored, full history from 1939 |
| id=NOTASERIESXYZ | 404 | text/html | - | HTML error page |
Limits
- This is a chart download route, not a documented data interface. It carries no version, no rate-limit headers and no contract; it can change under you without notice. The keyed JSON API is the supported path.
- No rate limit was probed. A handful of requests were made; how many it tolerates is unknown.
- The zip case was confirmed for one monthly-plus-quarterly pair. Three or more frequencies in one call were not tested, and the member filenames for daily or annual series are unverified.
- cosd was tested with two series and with the parameter repeated twice; other positional forms the graph page might use were not tried, so the ignore may be avoidable in a way not found here.
- Vintage data is not reachable this way. Nothing keyless was found that returns an as-of-date snapshot; see the vintages section.
Open question
Does cosd get ignored for every multi-series request, or only when the series have different start dates? A pair that both begin after the cosd would separate those.