# WARN Layoff Notices Scraper - Layoffs & Closures, 22 US States (`wakachobi/warn-layoff-notices`) Actor

Collect official WARN Act layoff and plant-closure notices from 22 US state labor departments in one normalized dataset: company, location, employees affected, notice and layoff dates, closure or layoff, industry.

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

## Pricing

$5.00 / 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.

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 Notice Scraper: US layoff and plant closure notices from 22 states

Collect **official WARN Act layoff and closure notices** straight from 22 US state labor departments and get them
back in **one normalized dataset**: company, city, county, employees affected, notice date, layoff date, closure or
layoff, reason and industry. Every state publishes WARN data in a different format (Excel files, CSVs, Google
Sheets, HTML tables, dashboards); this actor reads them all and gives you the same clean fields for each. No login
and no API key.

Built for **sales and account intelligence, recruiting, journalists, researchers and investors** who want to know
which companies are cutting jobs, where, and how many, usually 60 days before it happens.

### What data you get

- State, company name as filed, and a **normalized company name** (legal suffixes, "Updated" markers and DBA parts removed) for matching and CRM enrichment
- Location: street address, city, county and ZIP where the state publishes them
- **Employees affected** as a number
- **Notice date** (when the employer notified the state), received date and the **layoff / closure start date**, all ISO 8601
- Closure, layoff, relocation or reduced hours, plus temporary or permanent when the state says so
- Reason, industry and NAICS code where given
- Link to the official source and, for some states, to the notice PDF
- `companyDomain`: no state publishes the employer's website, so this is `null` for almost every notice. It is only
  filled when the company name itself contains a domain (e.g. "Noble.com, LLC"). It is never guessed and no paid
  lookup is used. Match on `companyNameNormalized` instead
- `raw`: the original source row, so nothing the state publishes is lost (personal contact columns are removed)

#### Sample result

```json
{
  "id": "093ca49209c67f23",
  "state": "NY",
  "companyName": "SYPartners, LLC",
  "companyNameNormalized": "SYPartners",
  "companyDomain": null,
  "address": "395 Hudson Street 8th Floor New York, NY, 10014",
  "city": "New York",
  "county": "New York",
  "zip": "10014",
  "employeesAffected": 31,
  "noticeDate": "2026-10-02",
  "noticeDatePrecision": "day",
  "receivedDate": "2026-10-06",
  "effectiveDate": "2026-12-31",
  "closureOrLayoff": "layoff",
  "permanence": "permanent",
  "noticeType": "Layoff Permanent",
  "reason": "Economic",
  "industry": null,
  "naicsCode": null,
  "sourceUrl": "https://dol.ny.gov/warn-dashboard",
  "noticeDateIsFuture": false,
  "raw": { "Business Legal Name": "SYPartners, LLC", "Date of WARN Notice": "2026-10-02", "Number of Affected Workers": "31", "...": "..." },
  "scrapedAt": "2026-10-23T01:16:54.206Z"
}
```

#### All output fields

| Field | Description |
|---|---|
| `id` | Stable ID of the notice (hash of state, company, date, place and headcount) |
| `state` | Two-letter state code |
| `companyName` / `companyNameNormalized` | Name as filed / cleaned name for matching |
| `companyDomain` | Domain only when it is literally part of the company name, so `null` on almost every notice. Never guessed |
| `address`, `city`, `county`, `zip` | Location of the affected site, where published |
| `employeesAffected` | Number of workers affected (number) |
| `noticeDate` | Date of the notice (falls back to the date the state received or posted it) |
| `noticeDatePrecision` | `day`, or `month` when the state only publishes the month (New Jersey, some Pennsylvania rows): the date is then the 1st of that month, and the notice is kept when that month overlaps your window, so its `noticeDate` can be earlier than `noticeDateFrom` |
| `noticeDateIsFuture` | `true` when the state lists a notice date later than the day of the run (a typo or a post-dated filing). The row is kept exactly as published |
| `receivedDate` | Date the state received or processed the notice, if different |
| `effectiveDate` | First layoff or closure date. `effectiveDateText` keeps the original text when it is a range or phased schedule, and is `null` when the state gives one plain date |
| `closureOrLayoff` | `closure`, `layoff`, `relocation`, `reduced hours` or `null` when the state does not say |
| `permanence` | `permanent`, `temporary` or `null` |
| `noticeType` | The state's own type text or code |
| `reason`, `industry`, `naicsCode` | When the state publishes them |
| `sourceUrl`, `noticeUrl` | Official source and, where available, the notice document |
| `raw` | Original row from the source |

### States covered

| State | Official source | Format | History available |
|---|---|---|---|
| California | [EDD WARN](https://edd.ca.gov/en/Jobs_and_Training/Layoff_Services_WARN) | XLSX | Current fiscal year (from July 1) |
| Colorado | [CDLE WARN list](https://cdle.colorado.gov/employers/layoff-separations/layoff-warn-list) | Google Sheet (CSV) | Current year |
| Connecticut | [CT DOL document library](https://dolpublicdocumentlibrary.ct.gov/CsblrCategory?prefix=%2Frapid_response%2Fwarn_documents) | JSON metadata | Latest 500 notices |
| Florida | [FloridaCommerce WARN](https://floridajobs.org/workforce-resources/worker-adjustment-and-retraining-notification-\(warn\)) | HTML table, per year | Multiple years |
| Georgia | [TCSG WARN public view](https://www.tcsg.edu/warn-public-view/) | JSON + HTML entry pages | Since 2023 |
| Illinois | [Illinois workNet](https://www.illinoisworknet.com/LayoffRecovery/Pages/ArchivedWARNReports.aspx) | XLSX export | Full IEBS history |
| Indiana | [DWD current WARN notices](https://www.in.gov/dwd/warn-notices/current-warn-notices/) | HTML table | Since 2008 |
| Iowa | [IWD WARN notices](https://workforce.iowa.gov/employers/resources/warn/notices) | XLSX event log | Since Oct 2021 |
| Maryland | [Maryland Labor WARN](https://www.dllr.state.md.us/employment/warn.shtml) | HTML table, per year | Multiple years |
| Massachusetts | [MassHire WARN updates](https://www.mass.gov/info-details/worker-adjustment-and-retraining-notification-act-warn-layoff-and-closure-updates) | CSV + XLSX | Current and previous fiscal year |
| Michigan | [LEO WARN notices](https://www.michigan.gov/leo/bureaus-agencies/wd/data-public-notices/warn-notices) | JSON search feed | Latest 2,000 notices |
| New Jersey | [NJ DOL WARN](https://www.nj.gov/labor/employer-services/warn/) | XLSX, sheet per year | Multiple years (month-level dates) |
| New York | [NY DOL WARN dashboard](https://dol.ny.gov/warn-dashboard) | CSV (Tableau Public) | Current year |
| North Carolina | [NC Commerce WARN summary](https://www.commerce.nc.gov/data-tools-reports/labor-market-data-tools/workforce-warn-reports) | HTML table | Current year |
| Ohio | [ODJFS current notices](https://jfs.ohio.gov/job-workforce-services/job-programs-and-services/submit-a-warn-notice/current-public-notices-of-layoffs-and-closures) | CSV | Current year |
| Oregon | [HECC WARN](https://ccwd.hecc.oregon.gov/Layoff/WARN) | XLSX download | About ten years |
| Pennsylvania | [PA L\&I WARN notices](https://www.pa.gov/agencies/dli/programs-services/workforce-development-home/warn-requirements/warn-notices) | HTML | Since 2023 |
| Texas | [TWC WARN notices](https://www.twc.texas.gov/data-reports/warn-notice) | XLSX, per year | Multiple years |
| Utah | [DWS WARN notices](https://jobs.utah.gov/employer/business/warnnotices.html) | HTML tables, per year | Multiple years |
| Virginia | [Virginia Works WARN](https://virginiaworks.gov/im-an-employer/retain-and-grow/warn-notices/) | CSV | Full list |
| Washington | [ESD WARN](https://esd.wa.gov/about-employees/WARN) | HTML table (paginated) | About 900 latest notices |
| Wisconsin | [DWD WARN](https://dwd.wisconsin.gov/dislocatedworker/warn/) | Google Sheet (CSV) | Full list |

**Coming soon:** Alabama, Alaska, Arizona, Arkansas, Delaware, District of Columbia, Hawaii, Idaho, Kansas, Kentucky,
Louisiana, Maine, Minnesota, Mississippi, Missouri, Montana, Nebraska, Nevada, New Hampshire, New Mexico, North
Dakota, Oklahoma, Rhode Island, South Carolina, South Dakota, Tennessee, Vermont, West Virginia and Wyoming. Many of
these publish only PDFs or scanned letters; they will be added as reliable sources are confirmed.

### How to use

| Field | What it does |
|---|---|
| `states` | States to include. Empty = all 22 supported states |
| `noticeDateFrom` | Only notices on or after this date. `YYYY-MM-DD` or relative: `30 days`, `6 months`, `1 year`. Default: last 30 days. Month-precision notices (NJ, some PA) are kept when their month overlaps the window |
| `companyNameContains` | Keep only companies whose name contains this text, e.g. `Amazon` |
| `minEmployees` | Keep only notices affecting at least this many workers |
| `maxItems` | Stop after this many notices (newest first, default 100) |
| `proxyConfiguration` | Datacenter proxy by default. State sites are public; if one state fails, try without proxy |

Example inputs:

```json
{ "noticeDateFrom": "30 days", "maxItems": 500 }
```

```json
{ "states": ["CA", "TX", "NY", "WA"], "noticeDateFrom": "90 days", "minEmployees": 100 }
```

```json
{ "noticeDateFrom": "2026-01-01", "companyNameContains": "Starbucks", "maxItems": 50 }
```

Results are sorted newest first and **deduplicated**: when a state lists the same notice twice (for example in a
weekly file and a yearly archive, or once per repeated row), you get it once. Amended notices that change the
headcount or date are kept as separate rows. A per-state run report (rows parsed, rows in your window, failures) is
saved in the key-value store as `STATE_SUMMARY`. If one state's website is down, the run continues with the others.

### Pricing

**$5 per 1,000 notices**, charged only for notices saved to your dataset. A typical "last 30 days, all states" run
returns 150 to 300 notices and costs about $0.75 to $1.50. Use `maxItems` to cap the cost.

### Use cases

- **Sales and account intelligence**: spot companies restructuring, closing sites or relocating, and time outreach for outplacement, staffing, real estate, equipment or advisory services
- **Recruiting**: find pools of experienced workers in a city or industry before the layoff date
- **Journalism and research**: track layoffs by state, industry or employer over time
- **Investors and analysts**: an early signal of cost cutting and plant closures at public and private companies
- **Clay, n8n, Make and Zapier workflows**: run daily with `noticeDateFrom: "2 days"` and push new notices into your CRM, a spreadsheet or Slack

### FAQ

**Is it legal?** Yes. WARN notices are public records that states publish for anyone to read. The actor only reads
those official public pages and files, without logging in, and drops personal contact details (names, phones,
emails of employer contacts) from the output. You are responsible for how you use the data.

**Why does the log say a state was skipped?** Some state sites sometimes refuse requests from cloud servers
(Ohio and Texas have done this). The actor then skips that state, finishes the rest, and lists the skipped
states at the end of the log. You are only charged for notices actually saved. Run it again later to pick
up the skipped state.

**Why do some fields come back empty?** Each state publishes different columns. Texas, for example, gives no layoff
type and Massachusetts no county. The actor never invents values: empty means the state did not publish it. The
original row is always in `raw`.

**Why is a New Jersey date always the 1st of the month?** New Jersey only publishes the month a notice was posted.
Those rows have `noticeDatePrecision: "month"` and are included whenever that month overlaps your date window. For
example, with `noticeDateFrom` 2026-09-23, a notice posted in September 2026 is included with `noticeDate`
2026-09-01. Filter on `noticeDatePrecision` if you only want exact days.

**How fresh is the data?** As fresh as the state's own page; most states update weekly or several times a week. Some
states post notices with a delay, and occasionally a state lists a notice with a future date exactly as filed. Those rows are kept and
flagged with `noticeDateIsFuture: true`.

**Can I run it on a schedule?** Yes. Use Apify Schedules for a daily or weekly run and connect the dataset to Google
Sheets, Slack, a webhook, Clay or n8n through Apify integrations.

### Integrations / API

#### 1. Weekly layoff alert for your sales territory (n8n → Slack)

Get a Slack message every Monday listing companies in your states that filed WARN notices for 50+ workers.

1. **Schedule Trigger:** every Monday at 08:00.
2. **Apify node:** *Run an Actor and get dataset items* (or an HTTP Request node, see below). Actor `wakachobi~warn-layoff-notices`, input:
   ```json
   { "states": ["CA", "WA", "OR"], "noticeDateFrom": "7 days", "maxItems": 500 }
   ```
3. **Filter:** `{{ $json.employeesAffected }}` ≥ 50.
4. **Slack:** message text
   `{{ $json.companyNameNormalized }} ({{ $json.city }}, {{ $json.state }}): {{ $json.employeesAffected }} workers, {{ $json.closureOrLayoff }} from {{ $json.effectiveDate }}. {{ $json.sourceUrl }}`

**Cost:** a week of notices for 3 states is usually a few dozen results, i.e. cents per run.

#### 2. Flag target accounts that are cutting jobs (Clay)

Use this to mark accounts in a Clay table that recently filed a WARN notice, for recruiting (talent becoming available) or for sales (a churn or vendor-change signal).

1. In a new Clay table: **Import → Pull Data from Apify Actor**. Pick *WARN Notice Scraper* with input `{ "states": ["NY","NJ","MA"], "noticeDateFrom": "90 days" }`. Set it to refresh weekly.
2. In your accounts table, add a **Lookup** column that matches your company name against the WARN table's `companyNameNormalized`. That field drops legal suffixes ("Inc.", "LLC") and "Updated" markers, so "Acme Holdings, Inc." matches "Acme Holdings".
3. Bring back `employeesAffected`, `noticeDate` and `effectiveDate`, and filter the table to rows that matched.

#### 3. Plain API call (any tool, script or spreadsheet)

```bash
curl -X POST "https://api.apify.com/v2/acts/wakachobi~warn-layoff-notices/run-sync-get-dataset-items?format=csv" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d '{ "states": ["TX"], "noticeDateFrom": "30 days", "maxItems": 1000 }' -o texas-warn.csv
```

***

### Credits

Source discovery for several states was informed by the open-source
[biglocalnews/warn-scraper](https://github.com/biglocalnews/warn-scraper) project by Big Local News at Stanford.
This actor is an independent implementation.

# Actor input Schema

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

US states to collect WARN notices from. Leave empty for all supported states.

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

Only notices dated on or after this day. Use YYYY-MM-DD or a relative period like '90 days', '6 months' or '1 year'. Empty = last 30 days. States that only publish the posting month (New Jersey, some Pennsylvania rows) are included when that month overlaps the window.

## `companyNameContains` (type: `string`):

Keep only notices whose company name contains this text (case-insensitive), e.g. 'Amazon'.

## `minEmployees` (type: `integer`):

Keep only notices affecting at least this many employees, e.g. 100.

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

Stop after this many notices in total (newest first).

## `proxyConfiguration` (type: `object`):

State government sites are public. Datacenter proxy works for most; if one state keeps failing, try without proxy or with residential.

## Actor input object example

```json
{
  "states": [
    "CA",
    "CO",
    "CT",
    "FL",
    "GA",
    "IA",
    "IL",
    "IN",
    "MA",
    "MD",
    "MI",
    "NC",
    "NJ",
    "NY",
    "OH",
    "OR",
    "PA",
    "TX",
    "UT",
    "VA",
    "WA",
    "WI"
  ],
  "noticeDateFrom": "30 days",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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",
        "CO",
        "CT",
        "FL",
        "GA",
        "IA",
        "IL",
        "IN",
        "MA",
        "MD",
        "MI",
        "NC",
        "NJ",
        "NY",
        "OH",
        "OR",
        "PA",
        "TX",
        "UT",
        "VA",
        "WA",
        "WI"
    ],
    "noticeDateFrom": "30 days",
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("wakachobi/warn-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",
        "CO",
        "CT",
        "FL",
        "GA",
        "IA",
        "IL",
        "IN",
        "MA",
        "MD",
        "MI",
        "NC",
        "NJ",
        "NY",
        "OH",
        "OR",
        "PA",
        "TX",
        "UT",
        "VA",
        "WA",
        "WI",
    ],
    "noticeDateFrom": "30 days",
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("wakachobi/warn-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",
    "CO",
    "CT",
    "FL",
    "GA",
    "IA",
    "IL",
    "IN",
    "MA",
    "MD",
    "MI",
    "NC",
    "NJ",
    "NY",
    "OH",
    "OR",
    "PA",
    "TX",
    "UT",
    "VA",
    "WA",
    "WI"
  ],
  "noticeDateFrom": "30 days",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call wakachobi/warn-layoff-notices --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,wakachobi/warn-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/5LrcbdM0kbeT1CIFa/builds/DwtfxbyhaMZ5uUYXB/openapi.json
