# Data Breach Notification Monitor — 7 Sources, No Login (`outstanding_vegetable/data-breach-notice-monitor`) Actor

Watch state attorney general breach lists (CA, WA, OR, VT, DE, IA) and the HHS breach portal and get only NEW data breach notices since the last run: organization, breach and report dates, residents affected, data types, notice letter PDF. Daily schedule. No login. MCP-ready. $20 per 1,000 alerts.

- **URL**: https://apify.com/outstanding\_vegetable/data-breach-notice-monitor.md
- **Developed by:** [Peter Skotte](https://apify.com/outstanding_vegetable) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 breach 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

## Data Breach Notification Monitor — state AG registries + HHS, daily alerts

Get a **daily feed of only the new data breach notifications** filed with US state attorneys general and the
HHS Office for Civil Rights: which organization was breached, when, how many people were affected, what data
was exposed, and a link to the sample consumer letter. The monitor remembers every notice it has already
reported, so a scheduled run emits just the delta (`new`, or `updated` when a source revises the affected
count), posts a summary to your webhook, and costs you only for what actually changed. No login, no API key.

Pairs with an SEC 8-K Item 1.05 (cybersecurity incident) watch: the state registries catch the thousands of
breaches at private companies, hospitals, school districts and law firms that never file with the SEC.

### Sources

| Key | Registry | What it publishes | Affected count | Posting lag |
|---|---|---|---|---|
| `ca` | California AG — Data Security Breach list | Organization, breach date(s), date reported, sample letter PDF | none published | days |
| `wa` | Washington AG — Data Breach Notifications | Organization, breach start date, Washingtonians affected, information compromised, letter PDF | state residents | days |
| `hhs` | HHS OCR — HIPAA breach portal (cases under investigation, 500+ individuals) | Covered entity, state, entity type, individuals affected, breach type, location, submission date | total | days |
| `or` | Oregon DOJ — Data Breach search | Organization, dates of breach, discovery date, notice-sent date, number affected | total (as published) | days |
| `vt` | Vermont AG — Security Breach Notices | Organization, organization type, Vermont residents affected, categories of data breached | state residents | days |
| `de` | Delaware AG — Data Security Breach Database (open-data API) | Organization, breach / discovered / notice dates, Delaware residents, total affected, data types, letter PDF, supplemental revisions | total + state | posted in batches, typically 4–8 weeks |
| `ia` | Iowa AG — Security Breach Notifications | Organization, industry, date reported, letter PDF | none published | 2–4 weeks |

Default sources are `ca`, `wa` and `hhs` (the three fastest-posting). Pass `["all"]` for every registry.

Not covered, and why: **Maine** took its public breach database offline after abuse of its reporting system;
**Texas** moved its list into a Salesforce Lightning app with no guest data endpoint; **Massachusetts**,
**New Hampshire** and **Montana** block automated access; **Hawaii** last posted in 2024; **Indiana** publishes
an annual PDF only; **Maryland**, **New Jersey**, **North Dakota** removed their public lists.

### How it works

1. Loads each selected registry and keeps the notices **reported within the last `lookbackDays`**.
2. Applies your `companyKeywords` and `minAffected` filters.
3. Compares each notice against the monitor's saved state (`source:noticeKey → affectedCount`).
4. Emits a record when the notice is unseen (`changeType: "new"`) or its published affected count changed
   (`changeType: "updated"`), enriched with the sample-letter PDF for California when `includeDetails` is on.
5. Saves the state, then POSTs a run summary to `webhookUrl` if set.

State lives in a named key-value store `breach-monitor-<hash of monitorId>` in your Apify account, capped at
50,000 notices (oldest dropped). Delete the store to reset a monitor.

### Input

| Field | Default | Notes |
|---|---|---|
| `sources` | `["ca","wa","hhs"]` | Any of `ca`, `wa`, `hhs`, `or`, `vt`, `de`, `ia`, or `all` |
| `companyKeywords` | `[]` | Case-insensitive substrings matched against the organization name, OR-ed. Empty = all |
| `minAffected` | `0` | Minimum published affected count (total or state residents, whichever is larger). California and Iowa publish no counts and are excluded when this is above 0 |
| `lookbackDays` | `30` | Notices reported within N days. Registries post days to weeks after the filing, so keep this generous; the seen-state prevents duplicates |
| `maxNewPerSource` | `5` | Cap per source per run; anything beyond stays unseen and comes out next run |
| `maxItems` | `10` | Overall cap per run |
| `includeDetails` | `true` | Fetch the California detail page for the sample letter PDF |
| `firstRunMode` | `emitAll` | `emitAll` reports every current notice on the first run; `baseline` records them silently |
| `webhookUrl` | `""` | Optional POST target for the run summary |
| `monitorId` | `default` | One state store per ID — run several watchlists side by side |
| `proxyConfiguration` | none | Every source is reachable from Apify's cloud without a proxy |

### Output

One record per new or updated notice:

```json
{
  "source": "wa",
  "state": "WA",
  "organization": "zHealth, Inc.",
  "organizationType": null,
  "breachStartDate": "2026-01-20",
  "breachEndDate": null,
  "discoveredDate": null,
  "reportedDate": "2026-09-11",
  "affectedTotal": null,
  "affectedStateResidents": 1332,
  "dataTypes": ["Name", "Health Insurance Policy or ID Number", "Medical Information"],
  "noticeUrl": "https://agportal-s3bucket.s3.amazonaws.com/databreach/BreachA42768.pdf",
  "letterUrl": "https://agportal-s3bucket.s3.amazonaws.com/databreach/BreachA42768.pdf",
  "description": null,
  "matchedKeyword": null,
  "changeType": "new",
  "firstSeenAt": "2026-09-29T00:17:29.434Z",
  "monitorId": "default"
}
```

Fields a registry does not publish are `null` (or `[]` for `dataTypes`). `noticeUrl` is the registry's detail
page when one exists (California), otherwise the letter PDF or the listing page. `description` carries the
HHS breach type and location, the Oregon consumer-notice date, or the Delaware supplemental-filing date.

### Recommended setup for a daily feed

1. Create a task, pick your `sources`, and set `monitorId` to something meaningful (`healthcare-10k`).
2. **First run: set `firstRunMode` to `baseline`** with `lookbackDays` 60. This records everything currently
   listed, emits nothing, and stops day one from being a backlog dump.
3. Switch `firstRunMode` back to `emitAll` (it only matters when the state is empty), keep `lookbackDays`
   at 30–45 and raise `maxNewPerSource` / `maxItems` to 200+.
4. **Schedule the task daily at 07:00 America/Los\_Angeles.** California, Washington and Oregon post during
   the Pacific business day; a morning run catches the previous day's postings across all time zones.
5. Point `webhookUrl` at Slack (incoming webhook), Zapier, Make, or your own endpoint. The payload is
   `{monitorId, runAt, sources, newCount, updatedCount, scanned, seenTotal, notices[first 50]}`.

The default settings (`{}`) run in `emitAll` mode and return the latest 10 notices so you see real output on
the first try.

### Use cases

- **Cyber insurers and brokers**: flag insureds and applicants the day their notice lands; `updated` records
  catch upward revisions of the affected count.
- **Security vendors**: outbound trigger lists — every breached organization with the data types exposed.
- **Class-action and privacy law firms**: first sight of breaches above a threshold (`minAffected: 10000`).
- **Journalists and researchers**: one normalized feed across seven registries instead of seven web pages.
- **Third-party risk teams**: watch your vendors by name with `companyKeywords`.

### Examples

Healthcare breaches over 10,000 individuals, all registries:

```json
{ "sources": ["all"], "companyKeywords": ["health", "medical", "hospital", "clinic", "dental", "pharmacy"], "minAffected": 10000, "maxNewPerSource": 100, "maxItems": 500 }
```

Watch a vendor list:

```json
{ "sources": ["all"], "companyKeywords": ["Quatrro", "zHealth", "DentaQuest"], "lookbackDays": 90, "firstRunMode": "baseline" }
```

### Notes

- Counts are exactly as published: `affectedStateResidents` for Washington and Vermont, `affectedTotal` for
  HHS and Oregon, both for Delaware. California and Iowa publish no counts.
- Notices are deduplicated per source, not across sources: a company that files in California, Washington
  and Oregon produces one record per registry, each with that state's figures.
- Pricing: $0.005 per run plus $0.02 per emitted notice. A daily schedule that finds nothing new costs the
  start fee only.

# Actor input Schema

## `sources` (type: `array`):

Which breach registries to watch: ca (California AG), wa (Washington AG), de (Delaware AG open-data), or (Oregon DOJ), vt (Vermont AG), ia (Iowa AG), hhs (HHS OCR HIPAA breach portal, 500+ individuals). Use "all" for every source.

## `companyKeywords` (type: `array`):

Case-insensitive substrings matched against the organization name, e.g. \["health", "bank", "acme corp"]. A notice matches if it contains any of them. Empty = every organization.

## `minAffected` (type: `integer`):

Only emit notices whose published affected count (total or state residents, whichever is larger) is at least this. 0 = no filter. Sources that publish no count (California) are excluded when this is above 0.

## `lookbackDays` (type: `integer`):

Only consider notices reported to the regulator within this many days. State postings lag the filing by days to weeks, so 30 is a safe default for a daily schedule; the seen-state prevents duplicates.

## `maxNewPerSource` (type: `integer`):

Stop after emitting this many new or updated notices from each source. Notices beyond the cap stay unseen and are emitted on the next run.

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

Overall cap on notices emitted in one run, across all sources.

## `includeDetails` (type: `boolean`):

Open each emitted California notice's detail page to pick up the sample consumer letter PDF (one extra request per California notice). Other sources already carry every published field in their listing.

## `firstRunMode` (type: `string`):

What to do when the monitor has no saved state yet. emitAll: treat every current notice in the window as new and emit it (good for a one-off pull or a first test). baseline: silently record every current notice as seen and emit nothing, so the next scheduled run reports only what is new since.

## `webhookUrl` (type: `string`):

Optional. After each run a JSON summary {monitorId, runAt, newCount, updatedCount, notices\[first 50]} is POSTed here (Slack/Zapier/Make/your API).

## `monitorId` (type: `string`):

Name of this watchlist. Each monitor ID keeps its own seen-state in a key-value store named breach-monitor-<hash>, so you can run several profiles (e.g. "healthcare-10k", "my-portfolio") side by side.

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

All sources are reachable from Apify's cloud without a proxy. Enable one only if a source starts blocking.

## Actor input object example

```json
{
  "sources": [
    "ca",
    "wa",
    "hhs"
  ],
  "companyKeywords": [],
  "minAffected": 0,
  "lookbackDays": 30,
  "maxNewPerSource": 5,
  "maxItems": 10,
  "includeDetails": true,
  "firstRunMode": "emitAll",
  "webhookUrl": "",
  "monitorId": "default",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset of new or updated data breach notifications found in this run (JSON).

# 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 = {
    "sources": [
        "ca",
        "wa",
        "hhs"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("outstanding_vegetable/data-breach-notice-monitor").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 = { "sources": [
        "ca",
        "wa",
        "hhs",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("outstanding_vegetable/data-breach-notice-monitor").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 '{
  "sources": [
    "ca",
    "wa",
    "hhs"
  ]
}' |
apify call outstanding_vegetable/data-breach-notice-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,outstanding_vegetable/data-breach-notice-monitor"
        }
    }
}
```

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/EG0peBlSZdG2zKyyo/builds/L32fQt9Af5IKSupVF/openapi.json
