NYC Restaurant Inspection Data Scraper (DOHMH Grades JSON)
Pricing
from $3.00 / 1,000 inspection row delivereds
NYC Restaurant Inspection Data Scraper (DOHMH Grades JSON)
Scrape official NYC Open Data restaurant inspections (DOHMH): one row per inspection and violation with score, grade, action, violation codes and descriptions, borough, cuisine, address and coordinates. Filter by date, borough, cuisine or grade. Establishment data only, no personal contact fields.
Pricing
from $3.00 / 1,000 inspection row delivereds
Rating
0.0
(0)
Developer
Steadfetch
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 hours ago
Last modified
Categories
Share
Extract official New York City restaurant inspection results from the NYC Open Data Socrata API. Structured JSON with inspection dates, scores, grades, violation codes and establishment location. No browser, no login, no CAPTCHA.
What it does
The Actor queries the official DOHMH dataset DOHMH New York City Restaurant Inspection Results (Socrata resource 43nn-pn8j) and returns one dataset item per inspection or violation row.
- Date window:
sinceanduntilfilter oninspection_date. Default is the last 30 days. - Location and type filters: borough, cuisine and grade (A, B or C), all applied server-side with SoQL.
- Normalized output: scores and coordinates become numbers, dates become
YYYY-MM-DD, and missing values becomenull. - Deduplication: key is
camis+inspection_date+violation_code, both inside a run and across monitor runs. - Monitor mode: with
mode: "monitor"the Actor persists delivered keys in its key-value store and only emits rows that were never delivered before for the same borough, cuisine and grade scope. - Schema validation: every item is validated against
.actor/dataset_schema.jsonbefore it reaches the dataset. WithstrictSchema: true(the default) an invalid item fails the run.
No charge for noise: errors, retries, duplicate rows, rows already delivered by a monitor run and the source placeholder date 1900-01-01 are never pushed, so they are never billed.
Source, licence and privacy
- Source: official NYC Open Data API, dataset published by the NYC Department of Health and Mental Hygiene (DOHMH). It is keyless and updated daily by the City.
- Terms: re-read on 2026-10-20. The NYC Open Data home page links to the NYC.gov Terms of Use, which require lawful use and do not prohibit lawful automated access to public data; the Socrata metadata for this dataset has no separate licence field. The Actor attributes DOHMH and links the public source URL on every row. Store publication keeps the CTO sign-off item in
PUBLISHING.md. - Privacy: establishment data only. The Actor never selects the source
phonecolumn ($selectexcludes it), and no phone, email, officer or other personal contact field is published. No personal data is collected.
Example output
{"url": "https://data.cityofnewyork.us/resource/43nn-pn8j.json?camis=40393413","title": "V & S PIZZERIA OF SHEAPSHEAD BAY","scrapedAt": "2026-10-20T08:18:21.885Z","camis": "40393413","business_name": "V & S PIZZERIA OF SHEAPSHEAD BAY","borough": "Brooklyn","building": "1723","street": "EMMONS AVENUE","zipcode": "11235","cuisine_description": "Pizza","inspection_date": "2026-10-17","action": "Violations were cited in the following area(s).","violation_code": "10F","violation_description": "Non-food contact surface or equipment made of unacceptable material, not kept clean, or not properly sealed, raised, spaced or movable to allow accessibility for cleaning on all sides, above and underneath the unit.","critical_flag": "Not Critical","score": 11,"grade": "A","grade_date": "2026-10-17","latitude": 40.583369830769,"longitude": -73.951275749439,"source_url": "https://data.cityofnewyork.us/resource/43nn-pn8j.json?camis=40393413"}
Input
Minimal run:
{"since": "2026-09-01","maxResults": 10}
Filtered monitor run:
{"since": "2026-09-01","borough": "Manhattan","cuisine": "Pizza","grade": "A","maxResults": 500,"mode": "monitor"}
| Field | Type | Description |
|---|---|---|
since | string | Optional ISO date (YYYY-MM-DD). Default: last 30 days. |
until | string | Optional ISO date (YYYY-MM-DD), inclusive. |
borough | string | Optional case-insensitive borough: Manhattan, Brooklyn, Queens, Bronx or Staten Island. |
cuisine | string | Optional case-insensitive DOHMH cuisine description, for example Pizza or American. |
grade | A, B or C | Optional grade filter. |
maxResults | integer | Default 1000, maximum 5000. |
mode | full or monitor | Default full. monitor emits only rows never delivered before in the same borough/cuisine/grade scope. |
strictSchema | boolean | Default true; fail the run on schema violations. |
No API key or login is required. An optional SOCRATA_APP_TOKEN Actor secret can be configured to raise Socrata rate limits; it is never part of the input and is never logged.
Output fields
| Field | Type | Notes |
|---|---|---|
camis | string | DOHMH establishment id (required). |
business_name | string | Source dba (required). |
borough | string or null | Source boro. |
building, street, zipcode | string or null | Establishment address. |
cuisine_description | string or null | DOHMH cuisine label. |
inspection_date | date | Required; 1900-01-01 is dropped. |
action | string or null | Inspection action text. |
violation_code, violation_description | string or null | Violation detail; null when no violation was recorded. |
critical_flag | string or null | Critical / Not Critical / Not Applicable. |
score | number or null | Published inspection score. |
grade | string or null | A, B, C, N, P or null, as published. |
grade_date | date or null | Placeholder dates become null. |
latitude, longitude | number or null | WGS84; 0/0 placeholders become null. |
url, source_url | string | Public NYC Open Data API URL for the establishment. |
scrapedAt | string | ISO 8601 UTC timestamp of the run. |
Project structure
.actor/actor.json Actor definition (payPerEvent, dataset schema, output schema).actor/input_schema.json INPUT options (date window, borough, cuisine, grade, maxResults, mode).actor/dataset_schema.json Item JSON Schema (fields) + table view for the Store.actor/pay_per_event.json PPE pricing (two synthetic platform events)src/query.ts Pure SoQL filter and API URL construction (no phone column)src/fetch.ts Socrata HTTP client: pagination, retry/backoffsrc/extract.ts Pure normalization, dedupe and monitor helpers (fixture-tested)src/main.ts Entry point: fetch, validate, monitor filter, pushsrc/validate-output.ts Ajv validator shared by the run and the teststest/ Offline fixtures and Vitest suites (no network, no credit)
Pay-per-event pricing
The Actor uses Apify pay-per-event pricing with the two synthetic platform events:
| Event | Charged when | Price |
|---|---|---|
apify-actor-start | a run starts (synthetic, automatic) | 0.00005 USD per run (Apify-recommended default; includes 5 seconds of free compute) |
apify-default-dataset-item | one inspection row is saved to the dataset (synthetic, automatic) | 0.003 USD per row = 3.00 USD per 1,000 results |
There is no custom per-item event and no Actor.charge() call: the platform already charges apify-default-dataset-item per pushed item, so adding another event would bill users twice.
The price sits inside the direct-competitor band (0.001-0.01 USD per inspection) and inside Steadfetch's 1-10 USD per 1,000 results band.
Measured cost: a 1,000-row run takes about 4.0 seconds at the 256 MB minimum memory, using one free Socrata request. At Apify's 0.20 USD per GB-hour compute reference that is about 0.00006 USD of compute per 1,000 rows, well under the 0.10 USD target and leaving a gross margin above 99%. Larger runs reuse the same page size and do not need a browser or proxy.
Development
npm installnpm test # lint + typecheck + offline Vitest suites (no credit)npm run publish:check # Steadfetch publish gatenpm run schemas:validatenpm run smoke # local Apify run (no platform credit)
The default test suite runs against the committed fixture test/fixtures/socrata-inspections.json and never touches the network. It covers the SoQL filters, the prohibited phone column, pagination, retry/backoff, deduplication, monitor state and output-schema validation.
FAQ
Do I need an API key? No. The NYC Open Data Socrata endpoint is public and keyless.
Does it use a browser? No. The source is a JSON API, so plain HTTP is roughly 10x cheaper than rendering the web front end.
Does it return personal data?
No. Only establishment fields are returned. The phone column is not even selected from the API, and no other contact field is published.
When am I charged?
Once per run start (apify-actor-start) and once per inspection row saved to the dataset (apify-default-dataset-item). Errors, retries, duplicates and the 1900-01-01 placeholder are not charged.
How does monitor mode avoid charging twice?
It stores the dedupe keys it delivered in an Actor key-value store. On the next run with the same borough, cuisine and grade scope it skips those keys before pushing, so they are not billed again. Use mode: "full" when you want the complete matching window on every run.
Why can one inspection produce several rows?
The source publishes one row per violation citation. The Actor keeps that grain, which is what compliance and analytics teams need, and deduplicates identical camis + inspection_date + violation_code rows.
Is the output schema stable?
Yes. New fields are additive and the required fields in .actor/dataset_schema.json do not change.
Maintenance
This Actor is tested daily: automated runs check that the NYC Open Data API is reachable and that the output still matches the published schema. Failures are reported as issues in the Steadfetch Actors repository.