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

Scrape the official NYC DOHMH Restaurant Inspection Results open dataset. Search or filter by borough, cuisine, grade, inspection type, critical flag, ZIP code, or date range; look up a restaurant's full inspection history by CAMIS ID. Free public Socrata API, no login required.

- **URL**: https://apify.com/crawlerbros/nyc-restaurant-inspection-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## NYC Restaurant Inspection Scraper

Scrape the official **NYC Department of Health and Mental Hygiene (DOHMH) Restaurant Inspection Results** open dataset. Search or filter New York City restaurant inspections by borough, cuisine, letter grade, inspection type, critical violation flag, ZIP code, or date range — or pull a restaurant's complete inspection history by its CAMIS ID. Powered by the public NYC Open Data (Socrata) API. No login, no API key, no proxy required.

### What this actor does

- **Two modes:** `search` (filter/browse) and `byCamis` (exact restaurant lookup)
- **Rich filters:** borough, cuisine, letter grade, critical-violation flag, inspection action/type, ZIP code, inspection date range, score range, restaurant-name keyword
- **Search near a location** — filter to restaurants within a radius (in meters) of any latitude/longitude point
- **Full-text search** across restaurant and cuisine text fields
- **Every inspection row** — one record per cited violation/inspection event, so a single restaurant can appear multiple times across its inspection history
- **Geo + district data** — latitude/longitude, community board, council district, census tract, BIN, BBL, NTA
- **Empty fields are omitted** — a restaurant with no assigned grade simply has no `grade` field, never a placeholder

### Output per inspection record

- `camis` — unique NYC restaurant identifier
- `dba` — restaurant name ("doing business as")
- `boro` — borough (Manhattan, Brooklyn, Queens, Bronx, Staten Island)
- `address`, `street`, `zipcode`, `phone`
- `cuisineDescription`
- `inspectionDate`, `inspectionType`, `action`
- `violationCode`, `violationDescription`, `criticalFlag`
- `score`, `grade`, `gradeDate`
- `recordDate` — when NYC Open Data last refreshed this row
- `latitude`, `longitude`
- `communityBoard`, `councilDistrict`, `censusTract`, `bin`, `bbl`, `nta`
- `sourceUrl` — link to the restaurant on NYC's public ABC Eats grading lookup
- `recordType: "inspection"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` (filter/browse) or `byCamis` (exact lookup) |
| `searchQuery` | string | – | Full-text search across restaurant/cuisine text fields (mode=search) |
| `dbaKeyword` | string | – | Case-insensitive substring match on restaurant name |
| `camisIds` | array | – | CAMIS IDs to fetch inspection history for (mode=byCamis) |
| `borough` | string | any | Manhattan / Brooklyn / Queens / Bronx / Staten Island |
| `cuisineDescription` | string | any | One of ~90 DOHMH cuisine classifications |
| `grade` | string | any | A / B / C / Not Yet Graded / Grade Pending (2 variants) |
| `criticalFlag` | string | any | Critical / Not Critical / Not Applicable |
| `actionType` | string | any | Inspection outcome/action |
| `inspectionType` | string | any | DOHMH inspection program + phase (36 combinations) |
| `inspectionDateFrom` / `inspectionDateTo` | string | – | ISO date range (YYYY-MM-DD) |
| `zipcode` | string | – | 5-digit NYC ZIP |
| `minScore` / `maxScore` | int | – | Inspection score bounds (0–200; higher = more violation points) |
| `nearLatitude` / `nearLongitude` | number | – | Center point for a radius search. Both must be set together to activate the filter. |
| `nearRadiusMeters` | int | `500` | Radius (1–50,000 m) around `nearLatitude`/`nearLongitude`. Only applied when both coordinates are set. |
| `sortBy` | string | `inspectionDateDesc` | Sort order — inspection date, score, restaurant name, borough, ZIP code, or cuisine |
| `appToken` | string | – | Optional free Socrata app token for higher rate limits |
| `maxItems` | int | `50` | Hard cap on emitted records (1–10000) |

#### Example: browse the latest inspections in Manhattan with a failing grade

```json
{
  "mode": "search",
  "borough": "Manhattan",
  "grade": "C",
  "maxItems": 50
}
```

#### Example: full inspection history for a specific restaurant

```json
{
  "mode": "byCamis",
  "camisIds": ["41235305"]
}
```

#### Example: critical violations for pizza restaurants in a date range

```json
{
  "mode": "search",
  "cuisineDescription": "Pizza",
  "criticalFlag": "Critical",
  "inspectionDateFrom": "2025-01-01",
  "inspectionDateTo": "2025-12-31",
  "maxItems": 200
}
```

#### Example: keyword search for a restaurant chain

```json
{
  "mode": "search",
  "dbaKeyword": "starbucks",
  "maxItems": 100
}
```

#### Example: restaurants within 500m of Times Square

```json
{
  "mode": "search",
  "nearLatitude": 40.758,
  "nearLongitude": -73.9855,
  "nearRadiusMeters": 500,
  "maxItems": 100
}
```

### Use cases

- **Food safety research** — track violation trends by cuisine, borough, or time period
- **Consumer apps** — surface a restaurant's grade and violation history before a visit
- **Real estate / business intelligence** — assess food-service density and compliance by neighborhood
- **Journalism** — investigate closures, repeat violators, or grading patterns
- **Academic research** — bulk-export inspection data for public health studies

### FAQ

**What is the data source?**
The NYC Department of Health and Mental Hygiene's Restaurant Inspection Results dataset, published on NYC Open Data (Socrata, dataset ID `43nn-pn8j`) and updated regularly by the city.

**Is this affiliated with NYC or DOHMH?**
No. This is an independent, third-party actor built on NYC's public open-data API.

**Why do some records have no `grade` field?**
Only certain inspection types receive a letter grade. Ungraded inspections simply omit the field rather than showing a placeholder.

**Why does one restaurant appear multiple times?**
Each row in the source dataset represents one violation cited during one inspection. A single inspection with multiple violations produces multiple rows, and each visit to a restaurant is a separate inspection.

**What does the inspection `score` mean?**
Lower is better — DOHMH assigns points for each violation, and the cumulative score determines the letter grade (roughly: 0–13 = A, 14–27 = B, 28+ = C).

**How fresh is the data?**
NYC Open Data refreshes this dataset frequently (typically daily). Each record includes a `recordDate` showing when it was last synced upstream.

**Are there rate limits?**
The Socrata API allows unauthenticated access with reasonable limits. Supplying a free Socrata app token (optional) raises those limits, but the actor works without one.

**How does the "search near a location" filter work?**
Set both `nearLatitude` and `nearLongitude` (and optionally `nearRadiusMeters`, default 500m) to only return inspections within that radius of a point. Setting only one of the two coordinates disables the filter — both are required together. This is combined with all other filters (borough, grade, date range, etc.) using AND.

**What fields are NOT included in the output?**
The source dataset includes four `:@computed_region_*` columns (internal Socrata IDs mapping each row to a police precinct, community district, borough boundary, and city council district boundary). These are opaque numeric IDs that only resolve to anything meaningful via a separate GIS boundary-file join, so they're excluded — the human-readable `communityBoard` and `councilDistrict` fields are included instead.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `searchQuery` (type: `string`):

Free-text search across restaurant name, cuisine, and other text fields (mode=search). Leave blank to browse without a text query.

## `dbaKeyword` (type: `string`):

Case-insensitive substring match on the restaurant's DBA ("doing business as") name (mode=search). Example: `starbucks`.

## `camisIds` (type: `array`):

Unique restaurant identifiers (CAMIS numbers) to fetch full inspection history for. Example: `41235305`.

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

Filter to a single NYC borough.

## `cuisineDescription` (type: `string`):

Filter to a single cuisine type, as classified by DOHMH.

## `grade` (type: `string`):

Filter to a single official inspection grade.

## `criticalFlag` (type: `string`):

Filter by whether the cited violation was flagged as critical.

## `actionType` (type: `string`):

Filter by the outcome/action recorded for the inspection.

## `inspectionType` (type: `string`):

Filter by the DOHMH inspection program and phase.

## `inspectionDateFrom` (type: `string`):

Drop inspections before this date.

## `inspectionDateTo` (type: `string`):

Drop inspections after this date.

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

Filter to a single 5-digit NYC ZIP code, e.g. `10013`.

## `minScore` (type: `integer`):

Drop inspections scoring below this (higher score = more violation points).

## `maxScore` (type: `integer`):

Drop inspections scoring above this.

## `nearLatitude` (type: `number`):

Latitude of a point to search near (mode=search). Must be combined with `nearLongitude` — both are required together to activate the radius filter. Example: `40.7580` (Times Square).

## `nearLongitude` (type: `number`):

Longitude of a point to search near (mode=search). Must be combined with `nearLatitude`. Example: `-73.9855` (Times Square).

## `nearRadiusMeters` (type: `integer`):

Radius in meters around the `nearLatitude`/`nearLongitude` point. Only applied when both coordinates are set.

## `sortBy` (type: `string`):

Sort order for results.

## `appToken` (type: `string`):

Optional free Socrata app token to raise API rate limits. Get one at https://data.cityofnewyork.us/profile/app\_tokens. Not required — the actor works without it.

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

Hard cap on emitted records.

## Actor input object example

```json
{
  "mode": "search",
  "camisIds": [],
  "borough": "",
  "cuisineDescription": "",
  "grade": "",
  "criticalFlag": "",
  "actionType": "",
  "inspectionType": "",
  "nearRadiusMeters": 500,
  "sortBy": "inspectionDateDesc",
  "maxItems": 50
}
```

# Actor output Schema

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

Dataset containing all scraped NYC restaurant inspection records.

# 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 = {
    "mode": "search",
    "camisIds": [],
    "borough": "",
    "cuisineDescription": "",
    "grade": "",
    "criticalFlag": "",
    "actionType": "",
    "inspectionType": "",
    "nearRadiusMeters": 500,
    "sortBy": "inspectionDateDesc",
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/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 = {
    "mode": "search",
    "camisIds": [],
    "borough": "",
    "cuisineDescription": "",
    "grade": "",
    "criticalFlag": "",
    "actionType": "",
    "inspectionType": "",
    "nearRadiusMeters": 500,
    "sortBy": "inspectionDateDesc",
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/nyc-restaurant-inspection-scraper").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 '{
  "mode": "search",
  "camisIds": [],
  "borough": "",
  "cuisineDescription": "",
  "grade": "",
  "criticalFlag": "",
  "actionType": "",
  "inspectionType": "",
  "nearRadiusMeters": 500,
  "sortBy": "inspectionDateDesc",
  "maxItems": 50
}' |
apify call crawlerbros/nyc-restaurant-inspection-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=crawlerbros/nyc-restaurant-inspection-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WChJwBQZ7SS2Xi74e/builds/MTmOiNt1dLo7lLKqc/openapi.json
