# WARN Act Layoff Notices API – Layoffs & Closures (`groundplanes/warn-act-layoff-notices`) Actor

Official WARN Act layoff & closure notices from CA, NY, OR, WA: company, city, employees affected, effective dates. For outplacement, recruiting & research.
Free sample (100 rows, real output): https://drive.google.com/uc?export=download\&id=1iFosSJ_GujXCxDirpibrB3Bet6pt2cY8

- **URL**: https://apify.com/groundplanes/warn-act-layoff-notices.md
- **Developed by:** [Groundplane Labs](https://apify.com/groundplanes) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 warn notices

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

## WARN Act Layoff & Plant Closure Notices Scraper (Multi-State)

Collect and normalize official Worker Adjustment and Retraining Notification (WARN) Act mass layoff and plant closure notices from four state workforce agencies (**California, New York, Oregon and Washington**) into a single structured feed.

### Free sample

Download a free sample (100 rows, real actor output, CSV): https://drive.google.com/uc?export=download\&id=1iFosSJ_GujXCxDirpibrB3Bet6pt2cY8 — check the columns and data quality before you run anything.

### What it does

Under the federal WARN Act (and stricter state "mini-WARN" statutes in states like California and New York), employers must file formal advance notice with state workforce agencies before conducting mass layoffs, plant closures, or relocations. However, each state publishes its WARN notices in a different format, ranging from multi-sheet Excel workbooks to HTML tables.

This Actor:

- Dynamically discovers and downloads the current fiscal-year California EDD WARN `.xlsx` workbook and parses the detailed report sheet.
- Collects and normalizes the WARN lists from Washington ESD, Oregon HECC Rapid Response and the New York State DOL WARN Dashboard (its official CSV export).
- Standardizes dates (`noticeDate`, `effectiveDate`, `daysUntilEffective`), affected worker counts (`employeesAffected`), locations (`address`, `city`, `county`), and event categories (`CLOSURE`, `LAYOFF`, `RELOCATION`, `UNKNOWN`).
- Isolates per-state failures: if one state portal is temporarily unavailable, the Actor logs a warning, returns all notices from the remaining states, and reports the failed state in the final status message.

### Who it's for

- **Technical and executive recruiting agencies** identifying talent pools from companies undergoing workforce reductions.
- **Outplacement, career transition, and HR services firms** offering rapid-response transition support to affected employers.
- **Macroeconomic, equity, and credit analysts** tracking corporate downsizing, facility closures, and sector-level headcount cuts.
- **Commercial real estate and industrial liquidators** monitoring plant and warehouse closures.

### Input

- **`states`** *(array of strings)* — Supported states: `"CA"`, `"NY"`, `"OR"`, `"WA"`. Default: all four, `["CA", "NY", "OR", "WA"]`. No other states are supported.
- **`noticeDateFrom`** *(string, YYYY-MM-DD)* — Earliest `noticeDate` to include. Defaults to today minus 60 days.
- **`minEmployeesAffected`** *(integer)* — Minimum `employeesAffected` count (`0` = no minimum). Default: `0`.
- **`eventType`** *(string enum)* — `"ALL"` (default), `"CLOSURE"`, `"LAYOFF"`, `"RELOCATION"`, or `"UNKNOWN"`.
- **`companyKeyword`** *(string)* — Optional case-insensitive keyword matched against `companyName`. Default: `""`.
- **`maxItems`** *(integer)* — Maximum rows to return. Default: `200`.

#### Example input JSON

```json
{
  "states": ["CA", "NY", "OR", "WA"],
  "noticeDateFrom": "2026-08-25",
  "minEmployeesAffected": 0,
  "eventType": "ALL",
  "companyKeyword": "",
  "maxItems": 200
}
```

#### Example run options (default max charge $50)

Set a default maximum charge of **$50** (`"maxTotalChargeUsd": 50`) in your run options:

```json
{
  "maxTotalChargeUsd": 50
}
```

### Output

Results are deduplicated by `recordId` (`<state>-<hash of company + noticeDate + site + employeesAffected + effectiveDate + eventType>`) and sorted newest first by `noticeDate` descending. The hash covers every field that distinguishes one filing from another, so two distinct notices from the same company on the same day (for example an original and an amended filing with different headcounts) both appear; only true duplicates collapse. Fields not published by a given state (such as `naicsOrIndustry` outside California) are returned as `null` rather than invented.

| Field | Type | Description |
|---|---|---|
| `recordId` | `string` | Deterministic ID (`<state>-<12-char sha256 hash>`). Changed in v0.3: IDs are not comparable with v0.1/v0.2 output. |
| `state` | `string` | Two-letter state code (`CA`, `NY`, `OR`, `WA`). |
| `companyName` | `string` | Employer name listed on the WARN filing. |
| `address` | `string \| null` | Street/facility address if published by the state. |
| `city` | `string \| null` | Facility city or region. |
| `county` | `string \| null` | County or parish if published by the state. |
| `employeesAffected` | `integer \| null` | Number of affected employees. |
| `noticeDate` | `string` | WARN notice date (`YYYY-MM-DD`). |
| `effectiveDate` | `string \| null` | Layoff/closure effective start date (`YYYY-MM-DD`) if published. |
| `daysUntilEffective` | `integer \| null` | Calendar days from today until `effectiveDate`. |
| `eventType` | `string` | Normalized category (`CLOSURE`, `LAYOFF`, `RELOCATION`, `UNKNOWN`). |
| `eventTypeRaw` | `string \| null` | Raw event description published by the state agency. |
| `naicsOrIndustry` | `string \| null` | NAICS sector / industry label (published by California EDD; `null` for others). |
| `sourceUrl` | `string` | Direct URL to the official state WARN file or page. |
| `scrapedAt` | `string` | UTC ISO-8601 timestamp when the row was collected. |

#### Example output row (illustrative)

This row is a **made-up example** that shows the real output shape. The company and values are not a real notice. Run the Actor to get live rows.

```json
{
  "recordId": "CA-9f8e7d6c5b4a",
  "state": "CA",
  "companyName": "Example Cloud Systems, Inc.",
  "address": "500 Innovation Way  San Jose CA 95134",
  "city": "San Jose",
  "county": "Santa Clara County",
  "employeesAffected": 145,
  "noticeDate": "2026-10-18",
  "effectiveDate": "2026-12-18",
  "daysUntilEffective": 55,
  "eventType": "LAYOFF",
  "eventTypeRaw": "Layoff Permanent",
  "naicsOrIndustry": "54 Professional Scientific and Technical Services",
  "sourceUrl": "https://edd.ca.gov/siteassets/files/jobs_and_training/warn/warn_report1.xlsx",
  "scrapedAt": "2026-10-23T19:00:00Z"
}
```

### How pricing works

Pay per result: $0.0025 per notice returned ($2.50 per 1,000), plus a tiny Apify actor-start fee. You only pay for rows returned. Set `"maxTotalChargeUsd": 50` in your run options to cap spend automatically.

### Data source and terms

#### Supported states and update schedules

- **California (`CA`):** [California Employment Development Department (EDD) WARN Report](https://edd.ca.gov/en/jobs_and_training/Layoff_Services_WARN) — Updated every Tuesday and Thursday via an official fiscal-year `.xlsx` workbook.
- **Washington (`WA`):** [Washington Employment Security Department (ESD) WARN Search](https://fortress.wa.gov/esd/file/warn/Public/SearchWARN.aspx) — Updated continuously on business days as WARN notices are received.
- **Oregon (`OR`):** [Oregon Higher Education Coordinating Commission (HECC) Rapid Response WARN](https://ccwd.hecc.oregon.gov/Layoff/WARN) — Updated continuously as notices are logged.
- **New York (`NY`):** [New York State DOL WARN Dashboard](https://dol.ny.gov/warn-dashboard). The Actor reads the dashboard's official Tableau CSV export, which covers notices posted from April 1, 2025 onward. The old dol.ny.gov/warn-notices page is a retired archive and is no longer used. **Fragility note:** the NY feed depends on a Tableau dashboard CSV export, not a documented API. If NYSDOL changes the dashboard, NY can fail while CA, OR and WA still return rows (the failed state is named in the run status).

#### Privacy & Affiliation Disclaimer

- WARN Act filings are public corporate workforce notices and contain **no personal employee data**.
- This Actor is an independent tool and is **not affiliated with or endorsed by the California EDD, New York State DOL, Washington ESD, Oregon HECC, or the U.S. Department of Labor**.

### Limits and FAQ

- Found a problem or need a field? Open an issue on the Issues tab; we respond quickly.
- **What does the run status tell me?**
  Every run ends `Complete:` or `PARTIAL:` and names the newest source activity date for each state, measured before your filters apply (for New York this is the newest **posted** date, because NY notice dates routinely lag posting by weeks). A state whose source layout changes, fails, or shows no new notices for over 14 days makes the run `PARTIAL` with the reason stated, instead of quietly returning less data. A run that matches nothing against healthy sources succeeds with `Complete: 0 notices matched your filters`, with the scanned row count stated.
- **What happens if one state's website is down for maintenance?**
  Each state runs in its own isolated adapter throttled to 1 request/second. If one state fails, the Actor logs a warning, outputs all matching rows from the other states, and notes the failed state in the final run status message.
- **Which fields are null for each state?**
  Each state publishes different fields, and the Actor leaves anything a state doesn't publish as `null` rather than guessing. Checked on 2026-10-23:

  | Field | CA | NY | OR | WA |
  |---|---|---|---|---|
  | `address` | filled | filled (full site address) | null | null |
  | `city` | filled | null (city is inside `address`) | filled (can be the employer's out-of-state HQ city) | filled |
  | `county` | filled | filled | null | null |
  | `employeesAffected` | filled | filled | filled | filled |
  | `effectiveDate` | filled | filled | null | filled |
  | `eventType` | LAYOFF or CLOSURE | LAYOFF or CLOSURE | LAYOFF (OR labels most notices "Large Layoff") | LAYOFF or CLOSURE |
  | `naicsOrIndustry` | filled | null | null | null |

  NY's "Date of WARN Notice" is often weeks before NYSDOL posts the notice, so recently posted NY notices can fall outside a short `noticeDateFrom` window.

### Changelog

- **v0.3 fix round 2 (2026-10-28):** California `city` extraction now reads the raw workbook cell. In the first v0.3 build the pipeline cleaned the address (collapsing whitespace) before extraction ran, which destroyed the double-space street/city separator the extractor primarily relies on; on the 2026-10-27 workbook that shipped 3 wrong cities and 8 avoidable `null`s. After the fix, a full census of all 397 CA rows through the adapter gives 390/390 separator-based cities correct, 0 wrong, 0 avoidable nulls. The free sample above was regenerated from this build. HTTP retries now honor `Retry-After` and no longer retry non-retryable 4xx responses. Text corrections: the dataset schema's `recordId` description now states the full hash inputs, and a TEST_NOTES note about New York's duplicated column was corrected (DictReader keeps the last duplicate; the columns agreed on every live row, so no data impact).

- **v0.3 (2026-10-27):** honesty and drift hardening. Per-state structure sentinels: a renamed source column or table now fails that state loudly (PARTIAL with the reason) instead of silently returning nulls or zero rows. California `city` extraction fixed (street names no longer leak into `city`; unparseable cities are `null` instead of wrong). `recordId` now hashes company + noticeDate + site + employeesAffected + effectiveDate + eventType, so distinct same-day filings no longer collapse into one row (IDs differ from v0.1/v0.2). Status contract: `Complete:`/`PARTIAL:` on every run, newest source activity named per state, zero matches succeed as `Complete: 0`, and the Issues-tab note appears on source-caused PARTIALs only.

- **v0.2 (2026-10-27):** docs: free-sample section + store refresh; no code changes.

- **v0.1 (2026-10-23):** Initial release supporting CA, NY, WA, and OR official WARN portals.

# Actor input Schema

## `states` (type: `array`):

State WARN sources to collect from: CA (California EDD), NY (New York State DOL WARN Dashboard), OR (Oregon HECC), WA (Washington ESD).

## `noticeDateFrom` (type: `string`):

Earliest WARN notice date to include (YYYY-MM-DD). Leave empty for today minus 60 days.

## `minEmployeesAffected` (type: `integer`):

Minimum number of workers affected by the layoff or closure notice.

## `eventType` (type: `string`):

Filter by normalized WARN event type.

## `companyKeyword` (type: `string`):

Optional case-insensitive substring matched against companyName.

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

Maximum number of WARN notices to return.

## Actor input object example

```json
{
  "states": [
    "CA",
    "NY",
    "OR",
    "WA"
  ],
  "minEmployeesAffected": 0,
  "eventType": "ALL",
  "companyKeyword": "",
  "maxItems": 200
}
```

# 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 = {
    "states": [
        "CA",
        "NY",
        "OR",
        "WA"
    ],
    "minEmployeesAffected": 0,
    "eventType": "ALL",
    "companyKeyword": "",
    "maxItems": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("groundplanes/warn-act-layoff-notices").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 = {
    "states": [
        "CA",
        "NY",
        "OR",
        "WA",
    ],
    "minEmployeesAffected": 0,
    "eventType": "ALL",
    "companyKeyword": "",
    "maxItems": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("groundplanes/warn-act-layoff-notices").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 '{
  "states": [
    "CA",
    "NY",
    "OR",
    "WA"
  ],
  "minEmployeesAffected": 0,
  "eventType": "ALL",
  "companyKeyword": "",
  "maxItems": 200
}' |
apify call groundplanes/warn-act-layoff-notices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,groundplanes/warn-act-layoff-notices"
        }
    }
}
```

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/4seGLdjon7x0PnGOU/builds/wZzus3sQjGnrIa7EE/openapi.json
