-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathblsapi.html
More file actions
350 lines (332 loc) · 20 KB
/
blsapi.html
File metadata and controls
350 lines (332 loc) · 20 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BLS API Guide | BD Economics</title>
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
<link rel="stylesheet" href="style.css">
<meta name="description" content="Python tutorial: Using the Bureau of Labor Statistics API for employment and inflation data.">
<meta name="keywords" content="BLS API, Economics Dashboard, Trade Network, Trade Networks, Macroeconomics Dashboard, Macroeconomic Dashboard, Markets Dashboard, Market Dashboard, U.S. Economy, U.S. Economy Dashboard, US economy, US economy dashboard, US economy charts, US economy charts pdf, NetworkX trade, international trade networks, network analysis of trade, Bureau of Labor Statistics Python, BLS Application Programming Interface, Bureau of Labor Statistics Application Programming Interface, Bureau of Labor Statistics API Pandas">
<meta name="author" content="Brian Dew">
<link rel="canonical" href="https://bd-econ.com/blsapi.html">
<!-- Open Graph -->
<meta property="og:title" content="BLS API Guide | BD Economics">
<meta property="og:description" content="Python tutorial: Using the Bureau of Labor Statistics API for employment and inflation data.">
<meta property="og:url" content="https://bd-econ.com/blsapi.html">
<meta property="og:type" content="article">
<meta property="og:image" content="https://bd-econ.com/images/01_bdlogo.png">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="BLS API Guide | BD Economics">
<meta name="twitter:description" content="Python tutorial: Using the Bureau of Labor Statistics API for employment and inflation data.">
<meta name="twitter:image" content="https://bd-econ.com/images/01_bdlogo.png">
<link rel="apple-touch-icon" sizes="180x180" href="favicon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon/favicon-16x16.png">
<link rel="manifest" href="favicon/manifest.json">
<meta name="theme-color" content="#ffffff">
<script>
(function() {
const saved = localStorage.getItem('theme');
if (saved) {
document.documentElement.setAttribute('data-theme', saved);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-theme', 'dark');
}
})();
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "BLS API Python Tutorial",
"description": "Python tutorial: Using the Bureau of Labor Statistics API for employment and inflation data.",
"author": {
"@type": "Person",
"name": "Brian Dew"
},
"publisher": {
"@type": "Organization",
"name": "BD Economics",
"url": "https://bd-econ.com"
},
"mainEntityOfPage": "https://bd-econ.com/blsapi.html",
"datePublished": "2017-07-06",
"dateModified": "2026-01-12"
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Guides",
"item": "https://bd-econ.com/python.html"
},
{
"@type": "ListItem",
"position": 2,
"name": "BLS API",
"item": "https://bd-econ.com/blsapi.html"
}
]
}
</script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-PGVF5S620Y"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-PGVF5S620Y');
</script>
</head>
<body>
<a href="#main" class="skip-link">Skip to main content</a>
<nav>
<ul class="ul_nav" id="menu">
<li class="nav_main"> <a href="index.html"><svg class="nav-logo" viewBox="0 0 9 5" width="22" height="12" aria-hidden="true"><rect x="0" y="0" width="1" height="5"/><rect x="0" y="4" width="4" height="1"/><rect x="2" y="2" width="2" height="1"/><rect x="3" y="2" width="1" height="2"/><rect x="8" y="0" width="1" height="5"/><rect x="5" y="4" width="4" height="1"/><rect x="5" y="2" width="2" height="1"/><rect x="5" y="2" width="1" height="2"/></svg>Economics</a> </li>
<li><a href="about.html">About</a> </li>
<li><a href="https://briandew.wordpress.com" target="_blank" rel="noopener">Blog <svg class="icon icon-external" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 3h6v6M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg></a> </li>
<li><a href="python.html" class="active" aria-current="page">Guides <span class="nav-arrow">↓</span></a>
<ul class="hidden">
<li><a href="getstarted.html">Getting Started</a></li>
<li><a href="imfapi1.html">IMF API</a></li>
<li><a href="blsapi.html">BLS API</a></li>
<li><a href="beaapi.html">BEA API</a></li>
<li><a href="censusapi.html">Census API</a></li>
<li><a href="treasuryapi.html">Treasury API</a></li>
<li><a href="cps.html">CPS Microdata</a></li>
</ul>
</li>
<li>
<a href="reports.html">Reports <span class="nav-arrow">↓</span></a>
<ul class="hidden">
<li><a href="chartbook.html">US Chartbook</a></li>
<li><a href="indicators.html">Economic Indicators</a></li>
<li><a href="gdpm.html">Monthly GDP</a></li>
<li><a href="imfweo.html">IMF WEO</a></li>
<li><a href="calendar.html">Release Calendar</a></li>
</ul>
</li>
<li><button class="theme-toggle" onclick="toggleTheme()" aria-label="Toggle dark mode"><span id="theme-icon"><svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg></span></button></li>
<li class="icon">
<button type="button" onclick="responsiveNav()" aria-label="Toggle navigation menu" aria-expanded="false">☰</button>
</li>
</ul>
</nav>
<div class="page-strip accent-purple">
<picture><source srcset="images/bls_strip.webp" type="image/webp"><img decoding="async" fetchpriority="high" src="images/bls_strip.jpg" alt="" aria-hidden="true" class="page-strip-img"></picture>
</div>
<header>
<h1>BLS API</h1>
</header>
<main id="main">
<section>
<article class="prose">
<div class="tutorial-meta">
<span>Updated <time datetime="2026-01-12">Jan 2026</time></span>
<span class="meta-sep">·</span>
<span class="trail-badge trail-intermediate">◆ Intermediate</span>
<span class="meta-sep">·</span>
<button class="tutorial-share" title="Copy link" aria-label="Copy link"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg><span class="share-label">Link</span></button>
</div>
<h2>Bureau of Labor Statistics (BLS) API with Python</h2>
<p>The <a href="https://www.bls.gov/developers/">BLS Public Data API</a> allows machine access to an enormous and incredibly useful set of U.S. economic data. With python, it is easy to integrate BLS data into projects, research, and graphics without the need to manually find, download, and combine files.</p>
<p>This notebook offers two examples of using python to request economic data from the BLS API. The first example requests the labor force participation rate from the BLS v1 API. The second example requests unemployment rates by race/origin, and shows a method for requesting multiple series over a longer period of time using the v2 API.</p>
<hr class="section-bar accent-purple">
<h3>Background</h3>
<h4>BLS</h4>
<p>The U.S. Bureau of Labor Statistics is part of the Department of Labor. You can read more about BLS <a href="https://www.bls.gov/bls/infohome.htm">here</a>.</p>
<blockquote class="blockquote-accent accent-purple">The Bureau of Labor Statistics of the U.S. Department of Labor is the principal Federal agency responsible for measuring labor market activity, working conditions, and price changes in the economy.</blockquote>
<h4>API</h4>
<p>BLS-data-related tasks that are exploratory, repetitive, or need to be well documented, can make use of Application Programming Interface (API) to save time. The API allows users to request data with specific parameters, such as the BLS series ID, dates, or preferred format. Data returned can be fed into pandas or other programs for further calculations and analysis.</p>
<h4>Python</h4>
<p>The examples use Python 3.x with the requests and pandas packages.</p>
<hr class="section-bar accent-purple">
<h3>Example 1: Single series from API v1</h3>
<span class="step-label accent-purple">Import Libraries and Request Data</span>
<p>The version 1 API does not require registration. The series ID is appended directly to the URL path. You can find series IDs using the <a href="https://www.bls.gov/data/">BLS data site</a> search tools. Here we request the civilian labor force participation rate (<code>LNS11300000</code>).</p>
<p>In[1]:</p>
<pre><code class="python">import requests
import pandas as pd
# BLS API v1: series ID goes in the URL path
url = 'https://api.bls.gov/publicAPI/v1/timeseries/data/LNS11300000'
r = requests.get(url).json()</code></pre>
<span class="step-label accent-purple">Explore the Response</span>
<p>The API returns a nested dictionary. The actual data lives at <code>r['Results']['series'][0]['data']</code>—a list of dictionaries, one per month, with the most recent month first.</p>
<p>In[2]:</p>
<pre><code class="python">data = r['Results']['series'][0]['data']
print(data[0])</code></pre>
<pre>{'year': '2025', 'period': 'M12', 'periodName': 'December', 'latest': 'true', 'value': '62.4', 'footnotes': [{}]}</pre>
<span class="step-label accent-purple">Read into Pandas</span>
<p>We create a DataFrame directly from the list of dictionaries, then construct a proper datetime index from the year and period fields.</p>
<p>In[3]:</p>
<pre><code class="python">df = pd.DataFrame(data)</code></pre>
<p>The period field uses the format <code>M01</code> through <code>M12</code>. We strip the <code>M</code> prefix to build a date string. BLS uses <code>-</code> for missing values, so we replace those with <code>None</code> before converting to float.</p>
<p>In[4]:</p>
<pre><code class="python"># Combine year and period to create datetime column
df['date'] = pd.to_datetime(df['year'] + '-' + df.period.str[1:] + '-01')
# Extract footnote text
df['note'] = [i[0].get('text', '') for i in df['footnotes']]
# Reorganize data; BLS uses '-' for missing values
df = df.set_index('date')[['value', 'note']].sort_index().replace('-', None)
df['value'] = df.value.astype(float)
df.tail(3)</code></pre>
<p>Out[4]:</p>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>value</th>
<th>note</th>
</tr>
<tr>
<th>date</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>2025-10-01</th>
<td>NaN</td>
<td>Data unavailable due to the 2025 lapse in appropriations.</td>
</tr>
<tr>
<th>2025-11-01</th>
<td>62.5</td>
<td></td>
</tr>
<tr>
<th>2025-12-01</th>
<td>62.4</td>
<td></td>
</tr>
</tbody>
</table>
<span class="step-label accent-purple">Plot the Results</span>
<p>Plot the monthly values with a horizontal line at the period mean to show the overall trend.</p>
<p>In[5]:</p>
<pre><code class="python">ax = df['value'].plot(title='Labor Force Participation Rate')
ax.axhline(df['value'].mean(), color='orange', label='mean')
ax.legend();</code></pre>
<p>Out[5]:</p>
<picture><source srcset="images/ex1_update.webp" type="image/webp"><img decoding="async" src="images/ex1_update.png" alt="Pandas Plot Output" loading="lazy" width="401" height="327"/></picture>
<p>The v1 API is simple but limited in the number of requests and years returned. For more complex requests, the v2 API shown in example 2 is preferable.</p>
<hr class="section-bar accent-purple">
<h3>Example 2: Requesting multiple series and specific dates</h3>
<p>The second example uses the BLS API v2 (which requires free <a href = "https://data.bls.gov/registrationEngine/">registration</a>) to request more than one series at the same time. The version 2 API has a higher daily query limit, allows more years and series to be returned in each query, and allows some additional options such as requesting data in percent change rather than level. See <a href="https://www.bls.gov/developers/api_faqs.htm#register1">difference between v1 and v2</a>. The API key is stored in a local <code>config.py</code> file; see the <a href="getstarted.html">Getting Started guide</a> for details.</p>
<span class="step-label accent-purple">Parameters</span>
<p>The v2 API uses a POST request instead of GET—we send a JSON body specifying which series, years, and API key we want. The <code>requests</code> library's <code>json=</code> parameter handles the serialization and headers automatically. See: <a href="https://stackoverflow.com/questions/3477333/what-is-the-difference-between-post-and-get">What is the difference between POST and GET?</a></p>
<p>In[6]:</p>
<pre><code class="python">import config # .py file with bls_key = 'API key here'
url = 'https://api.bls.gov/publicAPI/v2/timeseries/data/'
# Series stored as a dictionary
series_dict = {
'LNS14000003': 'White',
'LNS14000006': 'Black',
'LNS14000009': 'Hispanic'
}
# Post request with json= (handles serialization and headers)
results = requests.post(url, json={
"seriesid": list(series_dict.keys()),
"startyear": "2008",
"endyear": "2017",
"registrationkey": config.bls_key
}).json()['Results']['series']</code></pre>
<span class="step-label accent-purple">Process the Data</span>
<p>The API returns each series in the same format, so we loop through them to build a DataFrame. The data arrives in reverse chronological order, so we reverse it with <code>iloc[::-1]</code>.</p>
<p>In[7]:</p>
<pre><code class="python"># Date index from first series
date_list = [f"{i['year']}-{i['period'][1:]}-01" for i in results[0]['data']]
# Build a DataFrame column per series
df = pd.DataFrame()
for s in results:
df[series_dict[s['seriesID']]] = pd.Series(
index=pd.to_datetime(date_list),
data=[i['value'] for i in s['data']]
).astype(float).iloc[::-1]
df.tail()</code></pre>
<p>Out[7]:</p>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>Black</th>
<th>Hispanic</th>
<th>White</th>
</tr>
</thead>
<tbody>
<tr>
<th>2017-03-01</th>
<td>8.0</td>
<td>5.1</td>
<td>3.9</td>
</tr>
<tr>
<th>2017-04-01</th>
<td>7.9</td>
<td>5.2</td>
<td>3.8</td>
</tr>
<tr>
<th>2017-05-01</th>
<td>7.5</td>
<td>5.2</td>
<td>3.7</td>
</tr>
<tr>
<th>2017-06-01</th>
<td>7.1</td>
<td>4.8</td>
<td>3.8</td>
</tr>
<tr>
<th>2017-07-01</th>
<td>7.4</td>
<td>5.1</td>
<td>3.8</td>
</tr>
</tbody>
</table>
<span class="step-label accent-purple">Plot the Results</span>
<p>Plot all three series to compare how unemployment rates moved during and after the Great Recession.</p>
<p>In[8]:</p>
<pre><code class="python">df.plot(title='Unemployment Rates by Race or Origin')</code></pre>
<p>Out[8]:</p>
<picture><source srcset="images/ex2.webp" type="image/webp"><img decoding="async" src="images/ex2.png" alt="Pandas Plot Output" loading="lazy" width="432" height="288"/></picture>
<hr class="section-bar accent-purple">
<h3>Further Resources</h3>
<ul>
<li><a href="https://www.bls.gov/developers/">BLS Public Data API</a> — official documentation and usage limits</li>
<li><a href="https://www.bls.gov/data/">BLS Data Finder</a> — search for series IDs across all BLS programs</li>
<li><a href="https://data.bls.gov/registrationEngine/">Register for API v2</a> — free registration for higher query limits</li>
<li><a href="beaapi.html">BEA API Guide</a> — similar approach for GDP and national accounts data</li>
<li><a href="treasuryapi.html">Treasury API Guide</a> — federal revenue and spending data</li>
</ul>
</article>
<div class="subfooter" data-hub="guides" data-current="blsapi.html" style="--accent-color: var(--color-card-purple)"></div>
</section>
</main>
<footer>
<div class="footer_left">
<p><time datetime="2026">2026</time>, by Brian Dew</p>
</div>
<div class="footer_right">
<a href="https://github.com/bdecon/" aria-label="GitHub"><svg class="icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg></a>
<a href="https://www.linkedin.com/in/brian-dew-5788a386/" aria-label="LinkedIn"><svg class="icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854zm4.943 12.248V6.169H2.542v7.225zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.016-.709-.52-1.248-1.342-1.248S1.4 3.226 1.4 3.934c0 .694.521 1.248 1.327 1.248zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016a.3.3 0 0 1 .016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225z"/></svg></a>
<a href="https://twitter.com/bd_econ" aria-label="Twitter"><svg class="icon" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334q.002-.211-.006-.422A6.7 6.7 0 0 0 16 3.542a6.7 6.7 0 0 1-1.889.518 3.3 3.3 0 0 0 1.447-1.817 6.5 6.5 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.32 9.32 0 0 1-6.767-3.429 3.29 3.29 0 0 0 1.018 4.382A3.3 3.3 0 0 1 .64 6.575v.045a3.29 3.29 0 0 0 2.632 3.218 3.2 3.2 0 0 1-.865.115 3 3 0 0 1-.614-.057 3.28 3.28 0 0 0 3.067 2.277A6.6 6.6 0 0 1 .78 13.58a6 6 0 0 1-.78-.045A9.34 9.34 0 0 0 5.026 15"/></svg></a>
<a href="https://briandew.wordpress.com/" target="_blank" rel="noopener" aria-label="WordPress Blog"><svg class="icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M21.469 6.825c.84 1.537 1.318 3.3 1.318 5.175 0 3.979-2.156 7.456-5.363 9.325l3.295-9.527c.615-1.54.82-2.771.82-3.864 0-.405-.026-.78-.07-1.11m-7.981.105c.647-.03 1.232-.105 1.232-.105.582-.075.514-.93-.067-.899 0 0-1.755.135-2.88.135-1.064 0-2.85-.15-2.85-.15-.585-.03-.661.855-.075.885 0 0 .54.061 1.125.09l1.68 4.605-2.37 7.08L5.354 6.9c.649-.03 1.234-.1 1.234-.1.585-.075.516-.93-.065-.896 0 0-1.746.138-2.874.138-.2 0-.438-.008-.69-.015C4.911 3.15 8.235 1.215 12 1.215c2.809 0 5.365 1.072 7.286 2.833-.046-.003-.091-.009-.141-.009-1.06 0-1.812.923-1.812 1.914 0 .89.513 1.643 1.06 2.531.411.72.89 1.643.89 2.977 0 .915-.354 1.994-.821 3.479l-1.075 3.585-3.9-11.61.001.014zM12 22.784c-1.059 0-2.081-.153-3.048-.437l3.237-9.406 3.315 9.087c.024.053.05.101.078.149-1.12.393-2.325.609-3.582.609M1.211 12c0-1.564.336-3.05.935-4.39L7.29 21.709C3.694 19.96 1.212 16.271 1.211 12M12 0C5.385 0 0 5.385 0 12s5.385 12 12 12 12-5.385 12-12S18.615 0 12 0"/></svg></a>
</div>
</footer>
<script src="scripts/nav.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
</body>
</html>