# US Economic Calendar: BLS Release Dates (CPI, Jobs, PPI) (`yadroo/bls-release-calendar`) Actor

Scheduled release dates for every U.S. Bureau of Labor Statistics news release: CPI, Employment Situation, PPI, JOLTS, ECI and more. One row per release with reference period, UTC and Eastern time, schedule page and archived release link. Filter by release, date range or next release only.

- **URL**: https://apify.com/yadroo/bls-release-calendar.md
- **Developed by:** [Samat Makatov](https://apify.com/yadroo) (community)
- **Categories:** Developer tools, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 scheduled release returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## US Economic Calendar: BLS Release Dates (CPI, Jobs, PPI)

The official release schedule of the U.S. Bureau of Labor Statistics as flat rows: which news release is coming, for
which reference period, on which date and at exactly which moment in UTC. CPI, the Employment Situation (jobs report),
PPI, JOLTS, the Employment Cost Index and about forty more, each with the BLS short code, the Eastern wall-clock time,
the converted UTC timestamp (daylight saving included), the schedule page and — for dates that have already passed — a
link that opens the published release. No API key, no proxy, no browser.

This is a calendar of **when**, not a feed of **how much**: the schedule carries no actual, forecast or previous values.

### Use cases

- **Trading and model automation**: ask `mode: "nextRelease"` for `cpi` and `empsit` every morning and schedule the
  rerun of a model, or a trading pause, on the exact UTC minute the number lands.
- **Macro dashboards and news desks**: a rolling 30-day event feed with reference periods, weekdays and hours
  remaining, so the calendar widget fills itself.
- **Risk controls**: block the half hour around the jobs report and CPI — both come at 08:30 Eastern, which is 12:30Z
  in summer and 13:30Z in winter, a difference that breaks hard-coded offsets twice a year.
- **Event studies and backtests**: pull past releases with `dateFrom`/`dateTo` and get the publication moment plus the
  archived release page, so a study lines up to the minute data hit the wire.
- **Indicator tables for apps and agents**: `mode: "releaseList"` returns the dictionary — code, printed name, cadence,
  how many dates are scheduled, the next moment — ready to store as a lookup table.
- **Calendar-change monitoring**: `onlyNew` keys on the release plus its scheduled moment, so a scheduled task emits a
  row exactly when BLS moves a date, and nothing on the days it does not.

### Input

Nothing is required. With the defaults (and the prefilled `releases`) a run returns the CPI, jobs report and PPI dates
of the next 90 days.

| Field | Type | Default | Allowed values / notes |
|---|---|---|---|
| `mode` | string | `calendar` | `calendar`, `nextRelease`, `releaseList` — see [Modes](#modes) |
| `releases` | string\[] | all | BLS short codes, e.g. `["cpi","empsit"]` (prefill: `cpi`, `empsit`, `ppi`). Empty = every release on the schedule — see [Release codes](#release-codes) |
| `releaseNameContains` | string | — | Keep rows whose printed name contains this text, case-insensitive, e.g. `price`, `productivity`. ANDed with `releases` |
| `dateFrom` | string | — | Earliest release date, `YYYY-MM-DD`, inclusive. Eastern calendar dates, the ones BLS prints |
| `dateTo` | string | — | Latest release date, `YYYY-MM-DD`, inclusive |
| `withinDays` | integer | `90` | 1–1095. Used **only** when both dates are empty: today up to N days ahead |
| `onlyUpcoming` | boolean | `false` | Drop releases whose moment has passed. `nextRelease` always does this |
| `onlyNew` | boolean | `false` | Emit only entries this input has not delivered before — see [Monitoring with onlyNew](#monitoring-with-onlynew) |
| `sortOrder` | string | `soonest` | `soonest` or `latest`, by the scheduled moment |
| `maxItems` | integer | `50` | 1–2000. A full published year is roughly 165 rows |
| `fields` | string\[] | all | Keep only these output fields, in this order (`releaseName` is always kept) |

Date rules: `dateFrom`/`dateTo` beat `withinDays`; giving only `dateFrom` reads one year from it. `releases` is a
choice field: an unknown code is rejected by the input validation with the list of valid codes, before the run starts
(so a typo like `cpii` never reaches the actor). To match by wording instead of codes, use the free-text
`releaseNameContains`.

### Reference

#### Modes

| Mode | One row is | Date filters | Typical use |
|---|---|---|---|
| `calendar` | one scheduled release | `dateFrom`/`dateTo`, else `withinDays` | the event feed, past or future |
| `nextRelease` | the next date of one release | ignored — always from today | "when is the next CPI" |
| `releaseList` | one release of the dictionary | ignored — last 12 months plus everything published ahead | code → name → cadence lookup |

#### Release codes

The codes are the ones BLS uses in its own URLs. `Schedule page` marks the releases that have their own schedule page
(`scheduleUrl`); every code below links its latest edition (`latestReleaseUrl`) in `releaseList`.

| Code | Release | Schedule page |
|---|---|---|
| `cpi` | Consumer Price Index | yes |
| `empsit` | Employment Situation (the jobs report) | yes |
| `ppi` | Producer Price Index | yes |
| `jolts` | Job Openings and Labor Turnover Survey | yes |
| `eci` | Employment Cost Index | yes |
| `realer` | Real Earnings | yes |
| `ximpim` | U.S. Import and Export Price Indexes | yes |
| `metro` | Metropolitan Area Employment and Unemployment | yes |
| `laus` | State Employment and Unemployment | yes |
| `prod2` | Productivity and Costs (preliminary and revised) | yes |
| `cewbd` | Business Employment Dynamics | yes |
| `ecec` | Employer Costs for Employee Compensation | yes |
| `cew` | County Employment and Wages | yes |
| `wkyeng` | Usual Weekly Earnings of Wage and Salary Workers | yes |
| `jltst` | State Job Openings and Labor Turnover | no |
| `cesan` | Consumer Expenditures | no |
| `osh` | Employer-Reported Workplace Injuries and Illnesses | no |
| `cfoi` | Census of Fatal Occupational Injuries | no |
| `ecopro` | Employment Projections and Occupational Outlook Handbook | no |
| `atus` | American Time Use Survey | no |
| `union2` | Union Membership | no |
| `ocwage` | Occupational Employment and Wages | no |
| `wkstp` | Major Work Stoppages | no |
| `famee` | Employment Characteristics of Families | no |
| `forbrn` | Labor Force Characteristics of Foreign-born Workers | no |
| `disabl` | People with a Disability: Labor Force Characteristics | no |
| `vet` | Employment Situation of Veterans | no |
| `youth` | Summer Youth Labor Force | no |
| `disp` | Worker Displacement | no |
| `ors` | Occupational Requirements in the United States | no |
| `leave` | Access to and Use of Leave | no |
| `work` | Work Experience of the Population | no |
| `prod3` | Total Factor Productivity | no |
| `prin` | Productivity and Costs by Industry: Manufacturing and Mining | no |
| `tenure` | Employee Tenure | no |

The schedule also prints about a dozen smaller releases that have no code of their own (benchmark revisions, one-off
studies). They are returned like everything else, with `releaseCode: null`; filter those with `releaseNameContains`.
Federal holidays are printed on the same pages and are **not** returned — they carry no release time.

#### Reference periods

`referencePeriod` is the period the data describes, `referencePeriodType` groups it:

| Type | Examples | Releases |
|---|---|---|
| `monthly` | `September 2026`, `December 2025` | CPI, PPI, jobs report, JOLTS, state and metro employment |
| `quarterly` | `Third Quarter 2026` | ECI, productivity, weekly earnings, business employment dynamics |
| `annual` | `Annual 2025`, `2025` | injuries, fatalities, union membership, expenditures |
| `other` | `Biennial 2026`, `2024-2025` | occasional studies with an unusual period |

#### Monitoring with onlyNew

`onlyNew` remembers `releaseCode@releaseTimeUtc` in a named key-value store (`bls-release-calendar-state`), one record
per filter set. The first run returns everything it finds; later runs return only entries that were not there before.
Because the key contains the scheduled moment, a **rescheduled** release counts as new — which is the point: a daily
task with `onlyNew: true` stays quiet until BLS actually moves or adds something.

### Examples

**When is the next CPI, jobs report, PPI and JOLTS?**

```json
{ "mode": "nextRelease", "releases": ["cpi", "empsit", "ppi", "jolts"], "sortOrder": "soonest", "maxItems": 10 }
```

**The US inflation block of one quarter**

```json
{ "mode": "calendar", "releases": ["cpi", "ppi", "ximpim", "realer"], "dateFrom": "2026-10-01", "dateTo": "2026-12-31", "maxItems": 30 }
```

**Every jobs report date of a year**

```json
{ "mode": "calendar", "releases": ["empsit"], "dateFrom": "2026-01-01", "dateTo": "2026-12-31", "maxItems": 20 }
```

**Rolling feed of the next 30 days, all releases**

```json
{ "mode": "calendar", "withinDays": 30, "onlyUpcoming": true, "sortOrder": "soonest", "maxItems": 40 }
```

**Past releases with a link to the published text (event studies)**

```json
{ "mode": "calendar", "releases": ["cpi", "empsit", "jolts"], "dateFrom": "2026-01-01", "dateTo": "2026-06-30", "sortOrder": "latest", "maxItems": 25 }
```

**The dictionary: codes, cadence, schedule pages**

```json
{ "mode": "releaseList", "sortOrder": "soonest", "maxItems": 60 }
```

**Only what changed since the last run (scheduled task)**

```json
{ "mode": "calendar", "withinDays": 120, "onlyNew": true, "maxItems": 100 }
```

### Output

One row per scheduled release. A real row from a cloud run on 2026-09-19 (defaults plus the prefilled releases):

```json
{
  "releaseCode": "cpi",
  "releaseName": "Consumer Price Index",
  "referencePeriod": "September 2026",
  "referencePeriodType": "monthly",
  "releaseDate": "2026-10-14",
  "releaseTimeEt": "08:30",
  "releaseTimeUtc": "2026-10-14T12:30:00.000Z",
  "dayOfWeek": "Wednesday",
  "isUpcoming": true,
  "hoursUntilRelease": 591.2,
  "scheduleUrl": "https://www.bls.gov/schedule/news_release/cpi.htm",
  "releaseUrl": null,
  "agency": "U.S. Bureau of Labor Statistics",
  "sourceFormat": "html",
  "url": "https://www.bls.gov/schedule/2026/home.htm",
  "fetchedAt": "2026-09-19T21:18:22.922Z"
}
```

| Field | Type | Meaning |
|---|---|---|
| `releaseCode` | string | null | BLS short code; `null` for the smaller releases that have none |
| `releaseName` | string | Name as the schedule prints it |
| `referencePeriod` | string | null | Period the data describes, e.g. `September 2026` |
| `referencePeriodType` | string | `monthly`, `quarterly`, `annual`, `other` |
| `releaseDate` | string | Release date `YYYY-MM-DD`, Eastern calendar day |
| `releaseTimeEt` | string | Release time `HH:MM` Eastern, as printed (08:30 or 10:00 for almost everything) |
| `releaseTimeUtc` | string | The same moment in UTC, ISO 8601, daylight saving applied |
| `dayOfWeek` | string | Weekday of the release |
| `isUpcoming` | boolean | Whether the moment is still ahead of `fetchedAt` |
| `hoursUntilRelease` | number | null | Hours left; `null` once the release is out |
| `scheduleUrl` | string | null | The release's own BLS schedule page, where it has one |
| `releaseUrl` | string | null | Archived release page; filled for past dates of releases that have an archive |
| `agency` | string | Always `U.S. Bureau of Labor Statistics` |
| `sourceFormat` | string | `html` from the schedule page, `ics` from the calendar feed fallback |
| `url` | string | The schedule page the row was read from |
| `fetchedAt` | string | Fetch time, ISO 8601 UTC |

`mode: "releaseList"` writes a different row: `releaseCode`, `releaseName`, `frequency` (`monthly`, `quarterly`,
`semiannual`, `annual`, `multiple per month`, `irregular`), `scheduledCount`, `nextReleaseUtc`, `referencePeriodType`,
`agency`, `scheduleUrl`, `latestReleaseUrl` (the newest edition of that release), `url`, `fetchedAt`.

Every run also writes a `SUMMARY` record to the key-value store: the window actually read, the pages fetched, how many
entries were parsed, how many rows were dropped by filters, whether the window had to be trimmed, and the covered
years.

Dataset views: **Release calendar** (everything), **Upcoming releases** (what is still ahead, with hours remaining),
**Releases on the schedule** (the `releaseList` dictionary).

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~bls-release-calendar/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"nextRelease","releases":["cpi","empsit"],"fields":["releaseName","referencePeriod","releaseTimeUtc"],"maxItems":5}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("yadroo/bls-release-calendar").call(run_input={
    "mode": "calendar",
    "releases": ["cpi", "empsit"],
    "withinDays": 45,
    "maxItems": 20,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["releaseTimeUtc"], row["releaseName"], row["referencePeriod"])
```

For an agent, `mode: "nextRelease"` with `fields` is the cheapest call there is: one row per indicator, three fields,
a couple of seconds. The run finishes in about five seconds because it reads one or two static pages.

### Pricing

Pay per result: **$0.001** to start a run plus **$0.002** per row written. Typical runs:

| Run | Rows | Cost |
|---|---|---|
| Next date of four indicators (`nextRelease`) | 4 | $0.009 |
| Default run: CPI, jobs report, PPI for 90 days | 9 | $0.019 |
| Rolling 30-day feed, all releases | ~10 | $0.021 |
| The full dictionary (`releaseList`) | ~47 | $0.095 |

Compute is included in those numbers in practice: the actor runs at 256 MB without a browser and finishes in seconds.

### Limits & FAQ

- **No values, ever.** The schedule says when a release comes out, not what the number is. Actual, forecast and
  previous values are not in this source and are not returned. BLS publishes the numbers themselves elsewhere.
- **US and BLS only.** Releases from other US statistical agencies (Commerce, the Census Bureau, the Federal Reserve)
  and from other countries are not on this schedule, so they are not in the output.
- **How far the calendar reaches.** BLS publishes one year at a time; the next year appears only when BLS sets it.
  Earlier years stay readable in the archive, so past windows work. A window that only *reaches into* an unpublished
  year is trimmed to the last scheduled date and the run says so (`trimmed: true` in `SUMMARY`, and in the status
  message); a window entirely outside the published range ends with a message naming the range instead of returning
  an empty success.
- **Times are Eastern, converted.** BLS schedules in Eastern wall-clock time. `releaseTimeUtc` applies the real
  daylight-saving rules, so the same 08:30 release is `12:30Z` in October and `13:30Z` in November. Both are in the
  row — never rebuild the UTC time with a fixed offset.
- **Dates move.** BLS reschedules releases, and during a lapse in funding it can stop publishing altogether. Run with
  `onlyNew: true` on a schedule to be told when something changed.
- **`releaseUrl` only exists after publication.** It is left `null` for future dates and for the few releases whose
  archive uses a different naming pattern; `scheduleUrl` is filled only for the releases that have their own schedule
  page (14 of the 35 codes).
- **If the schedule page changes shape**, the actor falls back to the calendar feed BLS publishes for subscribing, and
  marks those rows `sourceFormat: "ics"`. That feed has no reference periods, so `referencePeriod` is `null` there —
  a missing period is better than an invented one.
- **Rate and politeness.** A run makes one to three requests to `www.bls.gov` (one page per covered year) and reads
  nothing else. There is no login, no captcha and no anti-bot wall to work around.
- **Licence and credit.** BLS material is in the public domain and free to use; BLS asks to be cited as the source.
  Every row carries `agency` and the source `url` for that reason.

***

Made by **Yadroo**. Sibling actors: [us-treasury-yields](https://apify.com/yadroo/us-treasury-yields),
[world-bank-indicators](https://apify.com/yadroo/world-bank-indicators),
[sec-edgar-filings](https://apify.com/yadroo/sec-edgar-filings),
[fx-rates](https://apify.com/yadroo/fx-rates),
[public-holidays](https://apify.com/yadroo/public-holidays).

# Actor input Schema

## `mode` (type: `string`):

`calendar` returns one row per scheduled release in the selected window. `nextRelease` returns the next upcoming date of each selected release (one row per release) — the answer to "when is the next CPI". `releaseList` returns the dictionary of releases the schedule contains (code, name, cadence, schedule page) and ignores the date filters.

## `releases` (type: `array`):

Keep only these releases, by their BLS short code. Empty = every release on the schedule, including the smaller ones that have no code (they come back with `releaseCode: null`). See README → Reference dictionaries for the full list.

## `releaseNameContains` (type: `string`):

Keep only releases whose printed name contains this text, case-insensitive, e.g. "price", "employment", "productivity". Use it for releases that are not in the code list above. Combined with `releases` it widens nothing: a row must match both.

## `dateFrom` (type: `string`):

Earliest release date, YYYY-MM-DD. Set `dateFrom` and `dateTo` to read past releases; leave both empty to use `withinDays` from today.

## `dateTo` (type: `string`):

Latest release date, YYYY-MM-DD. BLS publishes the schedule one year at a time; earlier years stay readable in the archive. A range that reaches past the last published year is trimmed to what exists and the run says so; a range entirely outside it ends with a clear message naming the covered range.

## `withinDays` (type: `integer`):

Used only when `dateFrom` and `dateTo` are both empty: releases scheduled from today up to N days ahead. 30 for a monthly planning run, 365 for the whole year ahead. A window that reaches into a year BLS has not published yet is trimmed to the last scheduled date instead of failing.

## `onlyUpcoming` (type: `boolean`):

Drop releases whose scheduled moment has already passed, also inside an explicit date range. `nextRelease` always applies this.

## `onlyNew` (type: `boolean`):

Remember release code plus scheduled UTC timestamp in this actor's key-value store and output only entries that were not there on the previous run. A rescheduled release counts as new, so a scheduled task emits exactly the calendar changes. The first run outputs everything it finds.

## `sortOrder` (type: `string`):

Order rows by the scheduled release moment.

## `maxItems` (type: `integer`):

Stop after this many rows. A full year of the schedule is about 165 rows (166 in 2026).

## `fields` (type: `array`):

Keep only these output fields, in this order, e.g. \["releaseName", "referencePeriod", "releaseTimeUtc"]. Empty = all fields of the mode.

## Actor input object example

```json
{
  "mode": "calendar",
  "releases": [
    "cpi",
    "empsit",
    "ppi"
  ],
  "withinDays": 90,
  "onlyUpcoming": false,
  "onlyNew": false,
  "sortOrder": "soonest",
  "maxItems": 50
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "releases": [
        "cpi",
        "empsit",
        "ppi"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/bls-release-calendar").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "releases": [
        "cpi",
        "empsit",
        "ppi",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("yadroo/bls-release-calendar").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "releases": [
    "cpi",
    "empsit",
    "ppi"
  ]
}' |
apify call yadroo/bls-release-calendar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yadroo/bls-release-calendar"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7aYZVYqkUGapq8Fjk/builds/3h01AvASfD89PUqgl/openapi.json
