NYC Restaurant Inspection Results Scraper avatar

NYC Restaurant Inspection Results Scraper

Pricing

$1.50 / 1,000 inspection row returneds

Go to Apify Store
NYC Restaurant Inspection Results Scraper

NYC Restaurant Inspection Results Scraper

Restaurant inspection violations in New York City from NYC Open Data (DOHMH): name, address, violation, grade and inspection date, filtered by date and borough.

Pricing

$1.50 / 1,000 inspection row returneds

Rating

0.0

(0)

Developer

Jason

Jason

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

17 days ago

Last modified

Share

Pulls New York City's restaurant health inspection results as the city publishes them, straight from NYC Open Data (DOHMH, the Department of Health and Mental Hygiene) through its public Socrata SODA API. No API key, no login, no browser automation. One flat JSON record per violation row — the dataset's own grain, not per restaurant and not per inspection (see What you get for exactly what that means and why it matters for counting). Filter by inspection-date window, borough, restaurant-name/cuisine keyword, letter grade and whether the violation is flagged critical. Citywide volume runs roughly 300–600 violation rows per weekday (measured 21–25 September 2026) and is close to zero on weekends — see Limits and freshness before sizing maxItems.

What you get

Row grain, read this first. The source dataset is "every sustained or not-yet-adjudicated violation citation from every inspection," not one row per restaurant or one row per inspection:

  • A single inspection that cites, say, five violations produces five rows, one per violationCode, all sharing the same restaurant, date and inspection type.
  • An inspection that finds no violations produces one row with violationCode: null and action reading "No violations were recorded at the time of this inspection."
  • A restaurant that has applied for a permit but has not yet been inspected gets one placeholder row with inspectionDate: "1900-01-01" (the source's own placeholder) and every inspection/violation field null.
  • Measured on a full working week (21–25 September 2026, 2,332 rows): 696 distinct inspections from 646 distinct restaurants, averaging 3.35 violation rows per inspection (median 3, max 13 in that week). Do not report row counts as restaurant counts or inspection counts — divide by camis or by inspectionDate+inspectionType first.
FieldDescription
id<camis>:<inspectionDate>:<inspectionType-slug>:<violationCode>, e.g. 40546082:2023-08-15:Cycle-Inspection-Initial-Inspection:02B. The dataset has no single-column primary key for a violation row. CAMIS + date + violation code alone is usually unique but measurably is not always: a live sample (488 rows, one day, one restaurant) showed 4 collisions where the same restaurant had both an Initial and a Re-inspection on the same calendar date citing the same code. Adding inspection type removed every observed collision. The Actor also de-duplicates defensively at run time and reports any further collision as filteredOut.duplicate in SUMMARY.
sourceAlways nyc-restaurant-inspections.
urlA Socrata data-explorer link pre-filtered to this exact CAMIS + inspection date + inspection type + violation code. This dataset has no public per-row permalink of its own (no restaurant detail page, no inspection id in a URL) — this is the closest public link to the one row; for the "not yet inspected" placeholder row it links to the restaurant's CAMIS alone.
camisNYC's own unique restaurant/establishment id, stable across every inspection of that restaurant.
dbaThe restaurant's "doing business as" name, as filed.
cuisineDescriptionThe source's own cuisine category (Pizza, Coffee/Tea, Hamburgers, …). Not published on the not-yet-inspected placeholder row.
borough, building, street, address, zipcodeLocation. address joins building + street with the borough and ZIP. The source's own boro value "0" (not geocoded to a borough) is reported as borough: null, not the literal string "0".
phoneThe restaurant's phone number on file, digits only, as published.
latitude, longitudeProperty coordinates where geocoded.
inspectionDateISO YYYY-MM-DD. This, not recordDate, is the freshness field this Actor filters on — see the box below.
pendingFirstInspectiontrue only for the 1900-01-01 placeholder row described above.
inspectionTypeThe inspection cycle and stage, e.g. Cycle Inspection / Initial Inspection, Cycle Inspection / Re-inspection, Pre-permit (Operational) / Initial Inspection.
actionThe source's own one-line outcome of the inspection (violations cited, none recorded, establishment closed/re-opened/re-closed).
hasViolationtrue when violationCode is set; a convenience flag equivalent to violationCode !== null.
violationCode, violationDescriptionThe cited code and its text, e.g. 02B / "Hot TCS food item not held at or above 140 °F." null on a no-violation or not-yet-inspected row.
criticalFlagCritical, Not Critical or Not Applicable (no violation on this row). These are the three values the live API returns (verified 2026-10-05); there is no "Critical Violation" synonym.
scoreThe inspection's numeric point score (lower is better); repeated on every violation row of the same inspection.
gradeOne of A, B, C, N (Not Yet Graded), P (Grade Pending, issued on reopening), Z (Grade Pending), or null. About half of all rows carry no grade at all (51% in the dataset total, measured 2026-10-05) — many inspection types (Smoke-Free Air Act, Trans Fat, Calorie Posting, Administrative Miscellaneous, …) do not produce a letter grade.
gradeDateISO date the grade was assigned; null alongside grade: null.
recordDateFull ISO timestamp of the nightly batch that last pulled this row into the published dataset — not when the inspection happened. See the box below; it is informational only, not filterable by this Actor.
extracommunityBoard, councilDistrict, censusTract, bin, bbl, nta (Neighborhood Tabulation Area), plus any column NYC adds later that this Actor has not mapped by hand.
fetchedAtISO timestamp of the run that produced the record.

Why inspectionDate, not recordDate, is the freshness filter. recordDate is the timestamp of NYC's nightly data-pull batch, not the inspection date: querying the live API (2026-10-05) showed recordDate clustered within seconds of the batch run for every row regardless of age — a 2017 inspection and a 2026 inspection pulled in the same nightly batch both carry essentially the same recordDate. It tells you when the extract was refreshed, not which rows are new. inspectionDate is the actual event date and is what every input filter in this Actor (daysBack, dateFrom/dateTo) is applied to.

Input

InputTypeDefaultDescription
daysBackinteger7Look-back window on inspectionDate, counted back from the run. Ignored when dateFrom/dateTo are set.
dateFromstring–Earliest inspection date, YYYY-MM-DD, inclusive. Overrides daysBack.
dateTostringtodayLatest inspection date, YYYY-MM-DD, inclusive.
boroughsstring[]allManhattan, Brooklyn, Queens, Bronx, Staten Island. Empty keeps every borough, including the small number of rows the source could not geocode to one.
keywordsstring[]noneCase-insensitive substrings OR-matched against dba and cuisineDescription.
gradesstring[]allA, B, C, N, P, Z. Setting this excludes every ungraded row (about half of the total), since a null grade never matches a non-empty filter.
onlyCriticalbooleanfalseKeep only rows whose criticalFlag is Critical. Also excludes every no-violation row.
socrataAppTokenstring (secret)–Optional free Socrata app token, sent as X-App-Token. Not required; it only lifts the shared per-IP throttle. Register at https://evergreen.data.socrata.com/profile/app_tokens.
maxItemsinteger500Stop after this many rows. You are charged per row delivered, so this is also the cost cap. Rows come newest-first by inspection date, so a value below the window's volume drops the oldest rows. Citywide volume is roughly 300–600 rows/weekday (measured 21–25 September 2026) and near zero on weekends, so a gap-free 7-day window needs about 3,000.

Every default is chosen so "Try it" with zero edits returns a small, fast, cheap result: the last 7 days, every borough, 500-row cap.

New critical violations in Brooklyn and Queens this week:

{ "daysBack": 7, "boroughs": ["Brooklyn", "Queens"], "onlyCritical": true, "maxItems": 500 }

C grades and below, citywide, for a compliance watch-list:

{ "daysBack": 30, "grades": ["C"], "maxItems": 2000 }

An unknown borough, an unknown grade, a malformed date or a reversed date range fails fast: the run stops with the validation message as its status before a single request is made. A keyword that matches nothing returns zero rows and charges nothing.

Output example

A real record from a live run on 2026-10-05 (daysBack: 10, no other filters):

{
"id": "50122004:2026-10-01:Cycle-Inspection-Reopening-Inspection:10F",
"source": "nyc-restaurant-inspections",
"url": "https://data.cityofnewyork.us/d/43nn-pn8j/explore/query/SELECT%20*%20WHERE%20camis%20%3D%20'50122004'%20AND%20inspection_date%20%3D%20'2026-10-01T00%3A00%3A00.000'%20AND%20inspection_type%20%3D%20'Cycle%20Inspection%20%2F%20Reopening%20Inspection'%20AND%20violation_code%20%3D%20'10F'/page/filter",
"camis": "50122004",
"dba": "EL PERRO",
"cuisineDescription": "Latin American",
"borough": "Queens",
"building": "80-26",
"street": "NORTHERN BLVD",
"address": "80-26 NORTHERN BLVD, Queens, NY 11372",
"zipcode": "11372",
"phone": "7184240763",
"latitude": 40.755380766968,
"longitude": -73.886911697316,
"inspectionDate": "2026-10-01",
"pendingFirstInspection": false,
"inspectionType": "Cycle Inspection / Reopening Inspection",
"action": "Establishment re-opened by DOHMH.",
"hasViolation": true,
"violationCode": "10F",
"violationDescription": "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.",
"criticalFlag": "Not Critical",
"score": 2,
"grade": "Z",
"gradeDate": "2026-10-01",
"recordDate": "2026-10-02T06:00:16.000Z",
"extra": {
"communityBoard": "403",
"councilDistrict": "25",
"censusTract": "028500",
"bin": "4028773",
"bbl": "4012530001",
"nta": "QN28"
},
"fetchedAt": "2026-10-05T02:57:01.814Z"
}

A not-yet-inspected restaurant looks like this (fields the source never publishes for it simply stay null):

{
"id": "50180010:1900-01-01:noinspection:noviolation",
"source": "nyc-restaurant-inspections",
"camis": "50180010",
"dba": "NEFIS LLC",
"borough": "Manhattan",
"address": "956 1 AVENUE, Manhattan, NY 10022",
"inspectionDate": "1900-01-01",
"pendingFirstInspection": true,
"inspectionType": null,
"violationCode": null,
"hasViolation": false,
"grade": null,
"criticalFlag": "Not Applicable"
}

The run also writes a SUMMARY record to the key-value store with rows scanned and kept, the newest inspection date reached, how many rows each filter removed, and a windowExhausted completeness flag (true only when the whole date window was read, nothing on the last page was left unread, and every kept row was actually delivered).

Pricing

Pay per result: US$0.0015 per row delivered (US$1.50 per 1,000 rows) — priced as a bulk government compliance register, in line with this repo's other bulk-register Actors (comparable to the FDIC and NZBN company registers). You are charged only for rows pushed to the dataset; a run that returns nothing costs nothing. A gap-free 7-day citywide pull (about 3,000 rows, see Limits and freshness) costs roughly US$4.50. Runs on the Apify free plan are limited by the platform's free-tier usage allowance.

Data source and licence

DOHMH New York City Restaurant Inspection Results, NYC Department of Health and Mental Hygiene: GET https://data.cityofnewyork.us/resource/43nn-pn8j.json. Dataset page: https://data.cityofnewyork.us/d/43nn-pn8j. Read live on 2026-10-05.

This dataset's own Socrata metadata (https://data.cityofnewyork.us/api/views/43nn-pn8j.json, read 2026-10-05) carries no separate license or licenseId field (both are empty) and sets attribution to "Department of Health and Mental Hygiene (DOHMH)". Like every dataset on the portal, it is still published under NYC Open Data, whose enabling law (New York City Local Law 11 of 2012, Admin. Code § 23-502(d)) requires city datasets to be available "without registration requirement, license requirement, or usage restrictions." Terms of use: https://www.nyc.gov/home/terms-of-use.page. Commercial reuse is permitted; the City warrants nothing about completeness or accuracy. Attribute to DOHMH via NYC Open Data.

Socrata app tokens — optional, free, and only relevant to rate limits: https://dev.socrata.com/docs/app-tokens.html.

Personal data

Every record in this feed is a public health-inspection record about a business, not about an individual. It carries a restaurant's trading name, address and phone number, and (per What you get) the dataset publishes no individual owner or officer at all — just the establishment. That still does not make downstream use of the phone number automatic:

  • phone is the restaurant's own published business line. If you use it for outbound sales or marketing calls, US telemarketing law still applies to the call itself: the TCPA (47 U.S.C. § 227) and FCC rules on autodialled/pre-recorded calls and texts, and the National Do Not Call Registry, regardless of whose name is on the account.
  • Commercial email built from this data is covered by CAN-SPAM (15 U.S.C. § 7701 et seq.): accurate headers, a physical postal address, and a working opt-out honoured within ten business days.
  • A poor inspection grade is a sensitive fact about a small business. Using this feed to contact a restaurant about its violations (pest control, compliance consulting, insurance) is a legitimate, common use of the data; publishing it as a standalone "worst restaurants" shaming list is a different use with its own reputational and legal exposure, and is your responsibility, not NYC's or this Actor's.
  • score and grade are the city's official determination; this Actor does not reinterpret, rank or editorialise them.

Limits and freshness

Volumes, measured live. The full dataset held 296,175 rows on 2026-10-05 ($select=count(*)), going back to 2007-08-10 (excluding the 1900-01-01 not-yet-inspected placeholder rows, of which there were 3,865). A full working week, 21–25 September 2026, returned 2,332 rows: 471, 523, 501, 608 and 229 rows Monday through Friday. The weekend of 20–21 September returned 12 rows total. The seven days ending 2026-10-05 (28 Sep – 1 Oct, the most recent inspections available at measurement time) returned 1,396 rows over 4 active days. Budget roughly 300–600 rows per weekday citywide and near-zero on weekends and public holidays.

Freshness and publication lag, measured 2026-10-05. The newest inspectionDate available was 2026-10-01, while the dataset's own metadata (rowsUpdatedAt) showed the last nightly refresh at 2026-10-02 06:00 UTC — a lag of about one business day between an inspection happening and it appearing in the API, consistent with the metadata's own stated Update Frequency: Daily. The most recent day's row count is typically a small fraction of a full day's (1 row for 2026-10-01 at measurement time) because that day's inspections were still being entered when the nightly extract ran; the preceding days (09-28 through 09-30: 423, 484, 488 rows) are representative of a complete day. A daysBack of 1 can legitimately return very few or zero rows — that is the source's own entry lag, not a defect in the Actor.

Row grain and double counting. See What you get above. Do not sum score, average grade, or count rows as restaurants/inspections without first grouping by camis (restaurants) or by camis + inspectionDate + inspectionType (inspections).

Grade coverage, measured 2026-10-05 (dataset total, 296,175 rows). A 97,941 (33%), B 18,067 (6%), C 12,982 (4%), N 10,079 (3%), Z 5,124 (2%), P 724 (0.2%), and 151,258 rows (51%) carry no grade at all — the inspection types that never produce a letter grade (Smoke-Free Air Act, Trans Fat, Calorie Posting, Sodium Warning, most Administrative Miscellaneous inspections) make up a large share of all rows. Setting the grades filter necessarily excludes that 51%.

Rate limits and paging. The SODA API is read in pages of 1,000 rows ordered inspection_date DESC, :id, with a 400ms pause between pages and a ceiling of 500 pages (500,000 rows) per run. Without an app token Socrata throttles by shared IP pool and can answer 503/429; fetchWithRetry backs off exponentially and honours Retry-After. A default 7-day run needs only one to a few requests, so the throttle is rarely reached in normal use; supply socrataAppToken if pulling months of history on a tight schedule.

Duplicate rows. CAMIS + inspection date + violation code alone is not always unique (see the id row in What you get); adding inspection type removed every collision observed in live testing. The Actor still de-duplicates on the full id at run time, reporting the count as filteredOut.duplicate in SUMMARY.

Scope. This Actor reads restaurant and college-cafeteria inspections only — the one dataset DOHMH publishes under this id. It does not cover other NYC food-safety datasets (mobile food vendors, farmers markets) or other cities. Only establishments in an "active" status as of the source's own data pull are included, per the dataset's own description; a restaurant that has closed permanently eventually drops out of the feed entirely rather than appearing with a "closed" status.

Use cases

  • Compliance and pest-control lead generation. A daily feed of restaurants that just failed an inspection, by borough or cuisine, for pest control, cleaning, HACCP consulting or legal compliance outreach.
  • Food-safety and public-health research. Track violation codes, critical-flag rates and grade distribution by borough, cuisine or time, using the full extra geography (community board, council district, census tract) for neighbourhood-level analysis.
  • Insurance and real-estate due diligence. Attach a restaurant tenant's recent inspection and grade history (via camis) to an underwriting or leasing decision, or screen a commercial property's food-service tenants for compliance risk.

Support

Open an issue on the Actor's Issues tab on Apify Store. Replies within 14 days. Bug reports that name the run id and the input used are fixed fastest.