# NYC Restaurant Health Inspection Scraper (`usta/nyc-restaurant-inspection-scraper`) Actor

Every NYC DOHMH restaurant health inspection violation in a date window: business name, address, borough, inspection date, score, letter grade, and violation detail. Read live from NYC Open Data on every run.

- **URL**: https://apify.com/usta/nyc-restaurant-inspection-scraper.md
- **Developed by:** [US Tech Automations](https://apify.com/usta) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 result rows

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

## Find NYC Health Code Violations by Date and Borough

**For restaurant compliance managers:** pull every inspection violation in a date window as one row each, without downloading NYC's entire multi-year dataset yourself.

Pick a date window and get one row for every violation cited on an NYC
restaurant health inspection in it: the business, its address, the
inspection date, the score and letter grade, and the specific violation. An
inspection that found no violations still gets one row.

### Introduction

Every restaurant, delicatessen, and college cafeteria in New York City is
inspected by the Department of Health and Mental Hygiene (DOHMH), which
publishes the result of every inspection — sustained or not-yet-adjudicated
violations, the numeric score, and the letter grade — as open data. This
Actor reads that dataset live from NYC's own Open Data API on every run.
Nothing is stored between runs and nothing is invented if NYC's API is down.

### Tutorial

1. Set **Inspected on or after** and **Inspected on or before** to your
   date window. Both days are included.
2. Optionally set **Borough** to one of the five boroughs, or leave it
   blank for citywide results.
3. Optionally set **ZIP code** to one 5-digit code.
4. Set **Maximum rows** to your spend cap. Every row bills, so this is also
   the most a single run can cost.
5. Click **Start**. Results land in the dataset.

### Pricing

**$0.003 per row. No start fee.**

A run that finds 200 qualifying violation-citation rows in your window costs
$0.60. A run that finds none — a quiet window, or a ZIP code with no
inspections that week — costs nothing.

**Maximum rows** is the spend cap: the run cannot cost more than that number
multiplied by $0.003, whatever the window holds.

### Reading the score correctly

NYC's score is a **penalty-point count, not a quality percentage**: fewer
points is better. NYC's own grade bands are 0-13 points = **A**, 14-27 = **B**,
28 or more = **C**. A `Z` or `P` grade means the inspection is pending
appeal. The signal worth acting on is a **high** score, a **B or C** grade,
or a violation line with `critical_flag` set to `Critical` — not a low
score.

### Input and output examples

Input:

```json
{"inspectedAfter": "2026-08-15", "inspectedBefore": "2026-09-15", "borough": "Brooklyn", "maxItems": 50}
```

Output (one row, from a live run on 2026-09-15):

```json
{
  "camis": "50063129",
  "dba": "PUBLIC ARTS / ARTSPACE",
  "boro": "Manhattan",
  "address": "215 CHRYSTIE ST/PUBLIC KITCHEN",
  "zipcode": null,
  "phone": "2127356000",
  "cuisine_description": "American",
  "inspection_date": "2026-09-13",
  "inspection_type": "Cycle Inspection / Initial Inspection",
  "action": "Violations were cited in the following area(s).",
  "violation_code": "04F",
  "violation_description": "Food preparation area, food storage area, or other area used by employees or patrons, contaminated by sewage or liquid waste.",
  "critical_flag": "Critical",
  "score": 65,
  "grade": "N",
  "grade_date": null,
  "latitude": null,
  "longitude": null,
  "record_date": "2026-09-14",
  "attribution": "Source: NYC Department of Health and Mental Hygiene (DOHMH), \"DOHMH New York City Restaurant Inspection Results\", published on NYC Open Data (data.cityofnewyork.us), dataset 43nn-pn8j. Made available under NYC Administrative Code Title 23, Chapter 5 (Local Law 11 of 2012), which requires public data sets to be provided without registration, licence, or use restriction.",
  "source_url": "https://data.cityofnewyork.us/resource/43nn-pn8j.json?camis=50063129&inspection_date=2026-09-13T00%3A00%3A00.000&violation_code=04F",
  "fetched_at": "2026-09-15T16:59:43Z"
}
```

Every field in a row:

| Field | What it holds |
|---|---|
| `camis` | NYC's own unique ID for the establishment |
| `dba` | The business's "doing business as" name |
| `boro` | Manhattan, Brooklyn, Queens, Bronx, or Staten Island |
| `address` | Street number and street, as published (a shared kitchen or food-hall stall may carry an extra descriptor after the street name) |
| `zipcode` | 5-digit ZIP, when NYC published one for this row |
| `phone` | The establishment's own listed business phone |
| `cuisine_description` | NYC's own cuisine category |
| `inspection_date` | Date of this inspection |
| `inspection_type` | e.g. "Cycle Inspection / Initial Inspection", "Pre-permit (Operational) / Initial Inspection" |
| `action` | NYC's own summary line for this inspection's outcome |
| `violation_code` / `violation_description` | The specific citation on this row; both are empty when the inspection found no violations |
| `critical_flag` | `Critical`, `Not Critical`, or `Not Applicable` |
| `score` | Penalty points for this inspection (see "Reading the score correctly" above) |
| `grade` | `A`, `B`, `C`, `Z` (pending, graded at the door), `P` (pending, on appeal), or `N` (not yet graded) |
| `grade_date` | Date the grade was issued, when one has been |
| `latitude` / `longitude` | Coordinates, when NYC geocoded this address; left blank rather than shipped as `0, 0` when it did not |
| `record_date` | The date NYC's own systems compiled this record |
| `attribution` | The required source and licence credit line |
| `source_url` | A live NYC Open Data query that returns this exact violation citation |
| `fetched_at` | When this run read NYC Open Data, in UTC |

### Recommendations

Run weekly with a 7-8 day window covering the prior week, and filter by
**Borough** or **ZIP code** when only one territory matters, to keep each
run's cost tied to the locations actually being tracked.

### Limitations

**Row grain matches NYC's own dataset.** One row is one violation citation
from one inspection. An inspection with four violations produces four rows
that repeat the same business, date, score, and grade with a different
`violation_code` on each. An inspection with none produces exactly one row
with `violation_code` left empty.

**Not every address is geocoded.** NYC leaves `latitude`/`longitude` (and
sometimes `zipcode`) blank on some rows, most often shared kitchens, food
halls, and newly-registered locations. This Actor passes that through as
`null` rather than guessing.

**Three-year rolling window, at NYC's discretion.** NYC's own dataset
description states it holds violations "up to three years prior to the most
recent inspection" for restaurants currently active; a closed restaurant's
older history can drop out of NYC's own source, not this Actor.

**A source outage returns nothing, not guesses.** If NYC Open Data cannot be
reached or answers with an error, the run pushes no rows, costs nothing, and
writes a status of `UNKNOWN` with the error to `OUTPUT`.

### FAQ

**Where does the data come from?**
NYC Open Data's own Socrata API, dataset `43nn-pn8j`, published by the
Department of Health and Mental Hygiene. Read live on every run, never from
a stored copy.

**Does it need an API key or a login?**
No. NYC Administrative Code Title 23, Chapter 5 requires this data be
available without registration, licence, or use restriction.

**Why did I get fewer rows than Maximum rows?**
Either the window and filters held that many violation citations and no
more, or the run hit **Maximum rows** first.

**What happens if NYC Open Data is down?**
The run pushes no rows, costs nothing, and writes a status of `UNKNOWN` with
the error to `OUTPUT`. It never fills the gap with made-up rows.

**Is there support?**
Issues can be raised through the Apify Store issues tab for this actor. No
response time is promised.

### Refunds and support

Refunds: if a run returns zero rows for a valid input, email operations@ustechautomations.com within 7 days and we refund that run. Support: same address, replies within 2 business days.

# Actor input Schema

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

First inspection date to include, written as YYYY-MM-DD. This day is included.

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

Last inspection date to include, written as YYYY-MM-DD. This day is included.

## `borough` (type: `string`):

Optional. Restrict results to one NYC borough. Leave blank for all five.

## `zipcode` (type: `string`):

Optional. Restrict results to one 5-digit ZIP code, e.g. 10002.

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

Hard ceiling on rows returned, and therefore on the cost of the run. Every row bills at the listed price from row one; there is no start fee.

## Actor input object example

```json
{
  "inspectedAfter": "2026-08-15",
  "inspectedBefore": "2026-09-15",
  "maxItems": 100
}
```

# Actor output Schema

## `inspections` (type: `string`):

No description

## `csv` (type: `string`):

No description

## `report` (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 = {
    "inspectedAfter": "2026-08-15",
    "inspectedBefore": "2026-09-15",
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("usta/nyc-restaurant-inspection-scraper").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 = {
    "inspectedAfter": "2026-08-15",
    "inspectedBefore": "2026-09-15",
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("usta/nyc-restaurant-inspection-scraper").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 '{
  "inspectedAfter": "2026-08-15",
  "inspectedBefore": "2026-09-15",
  "maxItems": 100
}' |
apify call usta/nyc-restaurant-inspection-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,usta/nyc-restaurant-inspection-scraper"
        }
    }
}
```

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/JkufEbPqTNAnM4ign/builds/g1gJ6uBYmhyw6oklt/openapi.json
