U.S. Bureau of Labor Statistics (BLS) Public Data API
U.S. Bureau of Labor Statistics (BLS) Public Data API
Official REST API from the U.S. Bureau of Labor Statistics for inflation, employment, and wage data. Public domain.
The U.S. Bureau of Labor Statistics publishes official economic data through free REST APIs at api.bls.gov. No registration required for v1.0 (25 queries/day); free registration for v2.0 (email and organization name, renewed yearly) unlocks 500 queries/day, 50 series and 20 years of data per query. BLS material is public domain, except previously copyrighted photographs and illustrations; BLS asks that you cite it as the source.
Required Attribution:
Cite BLS (as stated in this row's license note)
Coverage
CPI, PPI, unemployment, payrolls, productivity, wages; US national and regional data; historical coverage varies by series (CPI from 1913, employment from 1939)
Good for
- Tracking inflation and employment data for macro trading signals
- Building economic calendars and release monitors
- Training macro forecasting models on official government statistics
Verdict
Tracking official economic releases for macro trading signals. CPI-U (CUUR0000SA0), unemployment rate (LNS14000000), nonfarm payrolls (CES0000000001), and PPI (WPUFD4) updated monthly. V2.0 free registration unlocks higher limits and calculated changes.
Endpoints
| Endpoint | Returns |
|---|---|
POST /publicAPI/v2/timeseries/data/ | Retrieve time series data for one or more series IDs |
Get the free weekly email
Free weekly email of newly verified free data. Unsubscribe by emailing us.
By entering your email and clicking Subscribe, you agree to receive the Signal Lab weekly email from Quantum Data Technologies Ltd., 3200-650 West Georgia Street, Vancouver, BC V6B 4P7, Canada (signal-lab@qdt.ai). You can unsubscribe at any time: reply "unsubscribe" to any email or write to signal-lab@qdt.ai. See our Privacy Policy.
Use in your agent
Load BLS time series into pandas and Parquet. Checked against the provider's documentation on Oct 29, 2026.
Access
- Version 1.0: no registration or key.
- Version 2.0: register at https://data.bls.gov/registrationEngine/ with your email and organization name; the key arrives by email and must be renewed at least once a year.
Request
curl -X POST -H 'Content-Type: application/json' -d '{"seriesid":["CUUR0000SA0"],"startyear":"2017","endyear":"2026"}' https://api.bls.gov/publicAPI/v1/timeseries/data/Format
JSON: status, message and Results.series, each with seriesID and data points {year, period (M01 to M12), periodName, value, footnotes}. Values arrive as strings. You need to know the series IDs; BLS has no catalogue of them.
Load into pandas and Parquet
Needs Python 3 with requests, pandas and pyarrow.
"""BLS Public Data API v1 (CPI-U and unemployment rate) -> pandas -> Parquet. No key for v1."""
import requests
import pandas as pd
payload = {"seriesid": ["CUUR0000SA0", "LNS14000000"], # CPI-U all items (NSA); unemployment rate (SA)
"startyear": "2017", "endyear": "2026"} # v1 allows up to 10 years per request
r = requests.post("https://api.bls.gov/publicAPI/v1/timeseries/data/", json=payload, timeout=60)
r.raise_for_status()
body = r.json()
if body["status"] != "REQUEST_SUCCEEDED":
raise RuntimeError(body["message"])
for m in body["message"]: # some problems arrive as messages on a "successful" reply
print("BLS message:", m)
empty = [s["seriesID"] for s in body["Results"]["series"] if not s["data"]]
if empty:
raise RuntimeError(f"no data for {empty}: {body['message']}")
rows = []
for s in body["Results"]["series"]:
for p in s["data"]:
rows.append({"series_id": s["seriesID"], "year": int(p["year"]), "period": p["period"],
"value": p["value"],
"footnotes": "; ".join(f.get("text", "") for f in p["footnotes"] if f)})
df = pd.DataFrame(rows)
df = df[df["period"].str.match(r"M(0[1-9]|1[0-2])$")] # drop M13 (annual average) if present
df["date"] = pd.to_datetime(df["year"].astype(str) + "-" + df["period"].str[1:] + "-01")
df["value"] = pd.to_numeric(df["value"], errors="coerce") # strings; "-" marks a missing value
df = df.sort_values(["series_id", "date"])
df.to_parquet("bls_cpi_unemployment.parquet", index=False)
print(len(df), "rows;", df.groupby("series_id")["date"].agg(["min", "max"]).astype(str).to_dict("index"))Run by us on 2026-10-29 (python 3.13.5 | pandas 3.1.0 | pyarrow 26.0.0 | requests 2.34.2). Result: 234 monthly rows (CPI-U and the unemployment rate), 2017-01 to 2026-09.
Limits
- Version 1.0: 25 queries a day, 25 series and 10 years per query.
- Version 2.0 (free registration): 500 queries a day, 50 series and 20 years per query.
- Both: 50 requests per 10 seconds; going over returns HTTP 429.
Quirks
- Some problems come back as REQUEST_SUCCEEDED with a message and an empty data list (BLS's own example is an invalid series ID). The snippet prints messages and stops on empty series.
- Observed 2026-10-29: a version 1.0 request for 2010 to 2026 returned REQUEST_SUCCEEDED with the message "Year range has been reduced to the system-allowed limit of 10 years." and only 2010 to 2019. Split long ranges.
- Missing values are "-" with a footnote; October 2025 CPI and unemployment read "Data unavailable due to the 2025 lapse in appropriations". The snippet turns them into NaN.
- Series IDs must be upper case.
Terms and attribution
- BLS material is in the public domain; BLS asks you to cite it as the source.
Sources
- https://www.bls.gov/developers/api_faqs.htm: “Unregistered users may request up to 25 queries daily.”
- https://www.bls.gov/developers/api_faqs.htm: “Registered users may request up to 20 years per query.”
- https://www.bls.gov/developers/api_faqs.htm: “Users must renew registration with the BLS Public Data API at least once a year.”
- https://www.bls.gov/developers/api_faqs.htm: “We do not currently have a catalogue of series IDs”
- https://www.bls.gov/developers/api_signature.htm: “You can include up to 25 series IDs, each separated with a comma, in the body of your request.”
- https://www.bls.gov/opub/copyright-information.htm: “You are free to use our public domain material without specific permission, although we do ask that you cite the Bureau of Labor Statistics as the source.”
Information and education only. Not financial, investment, legal or tax advice. Listings can be inaccurate, incomplete or out of date and are provided "as is". Free tiers, limits and licenses belong to each provider and can change, so check the provider's own terms before you rely on them. Terms & Disclaimer