# US Food Safety Inspections - 7 sources, one result vocabulary (`formal_sadness_xvx/us-food-inspections`) Actor

Restaurant and food-facility inspections from 7 US health departments (~2.3M records) normalised into one schema with a unified pass/fail/conditional result.

- **URL**: https://apify.com/formal\_sadness\_xvx/us-food-inspections.md
- **Developed by:** [Rishi Vinodkumar](https://apify.com/formal_sadness_xvx) (community)
- **Categories:** Lead generation, News, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 inspection records

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## US Food Safety Inspections — 7 sources, one result vocabulary

Restaurant and food-facility inspections from seven US health departments,
normalised into a single schema with one comparable pass/fail outcome.

**~2,275,459 inspection records across 7 actively-maintained sources.**

| Source | State | Records | Last verified |
|---|---|---:|---|
| New York State | NY | ~1,399,201 | 2026-07-17 |
| Cincinnati | OH | ~341,246 | 2026-07-28 |
| Chicago | IL | ~313,437 | 2026-07-27 |
| King County (Seattle) | WA | ~106,077 | 2026-07-28 |
| Marin County | CA | ~49,461 | 2026-07-27 |
| Prince George's County | MD | ~45,073 | 2026-05-16 |
| Austin | TX | ~20,964 | 2026-05-22 |

### Why this exists

The transport is identical everywhere (Socrata). The *semantics* are not. The
inspection result — the one field anyone actually wants — is published six
different ways:

| Jurisdiction | Column | Vocabulary |
|---|---|---|
| Chicago | `results` | `Pass` / `Fail` / `Pass w/ Conditions` |
| King County | `inspection_result` | `Satisfactory` / `Unsatisfactory` + numeric score |
| Cincinnati | `action_status` | `Approved - No Violations` / `Approved - Minor Violations` / `Not In Compliance` |
| Prince George's | `inspection_results` | `Critical Violations observed` |
| Marin County | `inspection_result` | `NOVIOL` / `VIOLMINOR` / `VIOLFOUND` |
| New York State | `critical_violation` | `Critical Violation` / `Not Critical Violation` |
| Austin | `score` | numeric 0–100, no pass/fail at all |

Anyone wanting "failed inspections across metros" has to learn all six. This
actor maps them to one `result_status` of **PASS / FAIL / CONDITIONAL / OTHER /
UNKNOWN**, and always keeps the original string in `raw_result` — a lossy
normalisation nobody can audit is worse than none.

The mapping is substring-based and **order-sensitive**, because these
vocabularies contain each other:

```
"NOVIOL"          contains "VIOL"          -> must be tested first
"Unsatisfactory"  contains "Satisfactory"  -> must be tested first
"Approved - Minor Violations"              -> must precede bare "Approved"
"Not Critical Violation" contains "Critical Violation"  -> must be first
```

Get the order wrong and you silently invert a restaurant's grade.
`test_results.py` locks all of it down against strings observed in live data.

### Three traps worth knowing

**1. King County's score is inverted.** `inspection_score` counts *violation
points* — lower is better. Sorting descending to find the worst offenders would
return the cleanest restaurants. Every record carries `score_direction`
(`lower_better` / `higher_better` / `none`) so the meaning travels with the data.

**2. Cincinnati publishes enforcement actions, not graded inspections.** Its
rows are approvals and compliance findings, so it yields few clean PASS/FAIL
outcomes relative to its size. Filtering `failedOnly` across all sources works,
but do not read Cincinnati's volume as comparable to Chicago's.

**3. New York State rows are violations, not inspections.** One inspection
appears as several rows, so counting rows overstates inspection volume. It is
also statewide *excluding* New York City, and it is the largest source here by
a wide margin — leave it out if you want metro-level comparability.

### Output

```json
{
  "source": "chicago",
  "source_label": "Chicago",
  "state": "IL",
  "facility_name": "MADISON TOBACCO INC.",
  "license_number": "3082417",
  "inspection_id": "2640288",
  "inspection_date": "2026-07-28",
  "inspection_type": "License",
  "result_status": "PASS",
  "raw_result": "Pass",
  "score": null,
  "score_direction": "none",
  "risk_category": "Risk 3 (Low)",
  "violations": "49. NON-FOOD/FOOD CONTACT SURFACES CLEAN - Comments: OBSERVED DEBRIS...",
  "address": "5134 W MADISON ST",
  "city_name": "CHICAGO",
  "zip_code": "60644",
  "latitude": 41.8804954751,
  "longitude": -87.7540313826,
  "source_url": "https://data.cityofchicago.org/resource/4ijn-s7e5.json",
  "source_dataset_id": "4ijn-s7e5"
}
```

`inspection_date`, `result_status`, `address`, `city_name` and `zip_code` are
populated for 100% of records. Coordinates are published by Chicago, Cincinnati,
Marin and New York State only — King County, Prince George's and Austin
publish none.

### Who this is for

- **Food-delivery and restaurant-discovery platforms** surfacing hygiene data
- **Food-safety compliance vendors** monitoring client portfolios
- **Commercial insurers and underwriters** pricing restaurant risk
- **Local journalists and researchers** tracking enforcement patterns

### Usage

Failed inspections in the last quarter, geocoded:

```json
{
  "sources": ["chicago", "king_county", "marin_county"],
  "inspectedAfter": "2026-05-01",
  "failedOnly": true,
  "requireAddress": true,
  "maxResults": 2000
}
```

### Local development

```bash
pip install requests
python local_run.py --coverage
python local_run.py --after 2026-01-01 --limit 10
python local_run.py --failed-only --after 2026-01-01 --limit 20
python verify.py          # health-check every source
python test_results.py    # result-classification regression tests
```

### Legal

Public, logged-out municipal open-data endpoints served through each
jurisdiction's official Socrata API. No authentication, no account, no terms
accepted, no anti-bot circumvention. Inspection results are public records
published by the health department; facility names are businesses, not
individuals. Rate limits are respected with exponential backoff.

# Actor input Schema

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

Which health departments to fetch. Defaults to all 7.

## `inspectedAfter` (type: `string`):

Only inspections on or after this date (YYYY-MM-DD).

## `inspectedBefore` (type: `string`):

Only inspections on or before this date (YYYY-MM-DD).

## `failedOnly` (type: `boolean`):

Shortcut for resultStatus = \[FAIL]. Each jurisdiction uses its own vocabulary (Fail / Unsatisfactory / Not In Compliance / VIOLFOUND); this normalises across all of them.

## `resultStatus` (type: `array`):

Filter by normalised outcome. Cincinnati publishes enforcement actions rather than graded inspections; New York State grades Critical/Not Critical (mapped to FAIL/CONDITIONAL); Austin publishes only a numeric score, so its result\_status is UNKNOWN throughout.

## `search` (type: `string`):

Case-insensitive substring match across facility name, address, violation text, inspection type and risk category.

## `requireAddress` (type: `boolean`):

Drop records with no resolvable street address.

## `maxResultsPerSource` (type: `integer`):

Caps results from each source. Leave empty for no limit.

## `maxResults` (type: `integer`):

Hard cap across all jurisdictions. Use this to bound cost.

## `includeRawRecord` (type: `boolean`):

Attach the original record under `raw`. Prince George's County publishes per-criterion pass/fail columns (hand washing, cold holding, cooking temperature) that only appear here.

## `socrataAppToken` (type: `string`):

A free Socrata app token raises rate limits above the shared anonymous pool.

## Actor input object example

```json
{
  "sources": [
    "chicago",
    "king_county",
    "marin_county"
  ],
  "inspectedAfter": "2026-01-01",
  "failedOnly": false,
  "requireAddress": false,
  "maxResults": 1000,
  "includeRawRecord": false
}
```

# 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": [
        "chicago",
        "king_county",
        "marin_county"
    ],
    "inspectedAfter": "2026-01-01",
    "maxResults": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("formal_sadness_xvx/us-food-inspections").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": [
        "chicago",
        "king_county",
        "marin_county",
    ],
    "inspectedAfter": "2026-01-01",
    "maxResults": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("formal_sadness_xvx/us-food-inspections").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "chicago",
    "king_county",
    "marin_county"
  ],
  "inspectedAfter": "2026-01-01",
  "maxResults": 1000
}' |
apify call formal_sadness_xvx/us-food-inspections --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=formal_sadness_xvx/us-food-inspections",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/zVqsaepLaXMTiYNyX/builds/BtUgWdfYErFG6pbVW/openapi.json
