Boston Restaurant Inspections Scraper avatar

Boston Restaurant Inspections Scraper

Pricing

from $3.00 / 1,000 results

Go to Apify Store
Boston Restaurant Inspections Scraper

Boston Restaurant Inspections Scraper

Scrape City of Boston food establishment health inspection records from the public data.boston.gov CKAN datastore. Search by business name, address, neighborhood, ZIP, type, result, or date range. Lookup by license number, property ID, or violation code.

Pricing

from $3.00 / 1,000 results

Rating

0.0

(0)

Developer

Crawler Bros

Crawler Bros

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 days ago

Last modified

Share

Scrape City of Boston food establishment health inspection records from the public data.boston.gov open-data CKAN datastore. Search by business name, address, neighborhood, ZIP code, establishment type, license status, inspection result, violation status/severity, or a date range. Look up full inspection history by license number or building (property ID), or find every establishment cited for a specific health code violation citywide. HTTP-only via the public data.boston.gov API — no auth, no proxy, no login.

What this actor does

  • Four modes: search, byLicense, byPropertyId, byViolationCode
  • Full-text keyword search — search across violation descriptions and inspector comments (e.g. rodent, hand washing, hot holding)
  • Owner name search — search by the establishment's legal owner / license-holder name, independent of the business's trade name
  • Near-me radius search — find inspections within a chosen distance of a latitude/longitude
  • License expiry date range — find establishments whose food license is expiring/expired within a window (compliance & renewal monitoring)
  • Sort by date, business name, or violation severity — ascending or descending, in every mode
  • Violation severity mapping — Boston's raw */**/*** codes are mapped to Minor/Significant/Critical
  • Building history lookup — Boston uniquely tracks a propertyId per building, so you can pull every establishment ever inspected at one address across tenant changes
  • Empty fields are omitted

Output per inspection

  • recordId — Boston's internal datastore row ID
  • businessName; dbaName (when it differs from businessName)
  • legalOwner, ownerFirstName, ownerLastName — as filed by the establishment (source data occasionally lists a company name in the "first name" column and vice versa)
  • licenseNumber, licenseIssuedDate, licenseExpiresDate, licenseStatus (Active/Inactive/Deleted), licenseCategory (FS/FT/MFW/RF)
  • establishmentTypeEating & Drinking, Eating & Drinking w/ Take Out, Mobile Food Walk On, or Retail Food
  • result — inspection outcome/status code (see FAQ for the HE_* code meanings)
  • resultDateYYYY-MM-DD
  • violationCode, violationLevel (Minor/Significant/Critical), violationDescription, violationDate, violationStatus (Pass/Fail) — only present on rows that cite a specific violation
  • comments — inspector's free-text notes
  • address, city (neighborhood), state, zipCode
  • propertyId — Boston Assessing Department building identifier
  • latitude, longitude — when the city has geocoded the address
  • sourceUrl — direct link to this record in the source API
  • recordType: "foodInspection", scrapedAt

Input

FieldTypeDefaultDescription
modestringsearchsearch / byLicense / byPropertyId / byViolationCode
businessNamestringPartial match on business/DBA name (mode=search)
ownerNamestringPartial match on legal owner / license-holder name (mode=search)
addressstringPartial match on street address
citystringBoston neighborhood, exact match (mode=search)
zipCodestringZIP code (prefix match, so 02108 also matches 02108-1234)
establishmentTypestringEating & Drinking / Eating & Drinking w/ Take Out / Mobile Food Walk On / Retail Food
licenseStatusstringActive / Inactive / Deleted
resultstringInspection result/status code
violationStatusstringPass / Fail
violationLevelstringMinor / Significant / Critical
dateFrom / dateTostringInspection result date range, YYYY-MM-DD
licenseExpiresFrom / licenseExpiresTostringFood license expiration date range, YYYY-MM-DD (mode=search)
keywordstringFull-text search across violation description + comments
latitude / longitudenumberCenter point for a "near me" radius search (mode=search)
radiusKmnumber1Radius in kilometers around latitude/longitude (0.1-50)
licenseNumbersarrayLicense numbers to fetch full history for (mode=byLicense)
propertyIdsarrayProperty IDs to fetch a building's full inspection history (mode=byPropertyId)
violationCodesarrayExact violation code(s) to look up citywide (mode=byViolationCode)
sortBystringresultDateSort field: resultDate / businessName / violationLevel (all modes)
sortAscendingboolfalseOldest/lowest-first instead of newest/highest-first (all modes)
maxItemsint50Hard cap (1-10000)

Example: recent restaurants that failed inspection

{
"mode": "search",
"establishmentType": "Eating & Drinking",
"result": "HE_Fail",
"maxItems": 50
}

Example: critical violations mentioning rodents in Dorchester

{
"mode": "search",
"city": "DORCHESTER",
"violationLevel": "Critical",
"keyword": "rodent",
"maxItems": 50
}

Example: inspections in a ZIP code within a date range

{
"mode": "search",
"zipCode": "02118",
"dateFrom": "2024-01-01",
"dateTo": "2024-12-31",
"maxItems": 100
}

Example: inspections within 500 meters of a location

{
"mode": "search",
"latitude": 42.3601,
"longitude": -71.0589,
"radiusKm": 0.5,
"maxItems": 50
}

Example: full inspection history for a license number

{
"mode": "byLicense",
"licenseNumbers": ["313440"]
}

Example: full inspection history for a building

{
"mode": "byPropertyId",
"propertyIds": ["156226"]
}

Example: every establishment cited for a specific health code

{
"mode": "byViolationCode",
"violationCodes": ["22-4-601/602.11"]
}

Example: retail food licenses expiring around the annual renewal window

{
"mode": "search",
"establishmentType": "Retail Food",
"licenseExpiresFrom": "2026-12-01",
"licenseExpiresTo": "2027-01-31",
"maxItems": 100
}

Note: Boston's food licenses overwhelmingly expire in a single annual batch on January 1st (the license year), with only a handful of stragglers on other dates. A licenseExpiresFrom/licenseExpiresTo window that doesn't straddle a January 1st will typically return 0 records — this is a real characteristic of the source data, not a bug. Pick a window spanning December–January (or the January of the year you care about) to see the bulk of expirations.

Example: every establishment licensed to a given owner, most severe violations first

{
"mode": "search",
"ownerName": "Khosla",
"sortBy": "violationLevel",
"maxItems": 50
}

Use cases

  • Food safety research — analyze violation patterns across neighborhoods or establishment types
  • Restaurant/franchise due diligence — pull a business's full inspection history by license number
  • Commercial real estate / leasing — check a building's food-safety history across every tenant that has operated there via propertyId
  • Journalism & public health reporting — surface repeat failures or specific violation codes citywide
  • Insurance & risk underwriting — screen a food business's compliance record before writing a policy
  • Academic research — bulk-export inspection data for epidemiological or urban studies

FAQ

What's the data source? The City of Boston's official open-data portal (data.boston.gov), Food Establishment Inspections dataset, maintained by the Inspectional Services Department's Health Division. This actor is an independent third-party tool and is not affiliated with the City of Boston.

How far back does the data go? April 2006 to the present, updated by the city on a rolling (typically daily) basis.

Are there rate limits? The public CKAN datastore endpoint has generous limits for read-only queries; the actor paginates with small delays to stay well within them.

What do the HE_* result codes mean? They're Boston's Health Division inspection-workflow status codes, e.g. HE_Pass/HE_Fail (pass/fail a routine inspection), HE_FailExt (fail, extension granted to correct), HE_NotReq (no inspection required this cycle), HE_OutBus (establishment closed/out of business), HE_Hearing/HE_Filed/HE_Hold (administrative hearing workflow states), HE_VolClos/HE_Closure (voluntary or emergency closure). Plain Pass/Fail/Closed/NoViol/PassViol are older, pre-HE_ era result codes still present on historical rows.

Why do ownerFirstName/ownerLastName sometimes look swapped or contain a company name? That's how the source data is filed — Boston's underlying license record occasionally has a company name in the person-name columns. This actor passes through exactly what the city publishes rather than guessing at a correction.

What's the difference between businessName and dbaName? businessName is the name on file for the inspection; dbaName ("doing business as") is only included when Boston lists a different trade name for the same establishment.

Why is a violation's severity Minor/Significant/Critical instead of the raw */**/***? This actor maps Boston's raw asterisk-count severity codes to their standard food-code meaning for readability. A small number of legacy rows (<0.01%) have a malformed severity value in the source data and are simply omitted from the mapped field rather than guessed at.

Why does a record sometimes have no violationCode/violationDescription? Boston's dataset has one row per inspection event — some rows represent a clean pass or an administrative status (e.g. HE_NotReq) with no violation cited, so those violation-specific fields are simply omitted for that row.

What is propertyId? A Boston Assessing Department building identifier. Multiple different restaurants that have operated at the same street address over the years typically share the same propertyId, letting you pull a location's complete food-safety history independent of ownership changes.

How does the "near me" radius search work? Provide latitude, longitude, and optionally radiusKm (default 1 km) — the actor returns inspections whose geocoded address falls within that great-circle distance of the point you gave. Only inspections with a geocoded address can match.

Why is zipCode a prefix match instead of exact? A small number of source rows store an extended ZIP+4 code (e.g. 02119-3212); a prefix match on the 5-digit code still matches those rows correctly.

How does sortBy work? It applies to every mode. resultDate (default) sorts by inspection result date; businessName sorts alphabetically; violationLevel sorts by severity (Critical first when descending). Combine with sortAscending to reverse any of the three. Note: ~0.8% of source rows (rows with no violation cited, or a small number of malformed source values) carry a raw severity marker outside the */**/*** set; those rows' violationLevel output field is correctly omitted, but their position in a sortBy=violationLevel ordering is determined by the raw source value rather than a true severity rank — this is an upstream data quality artifact, not a bug in the sort itself.

Data Source & Limitations

This actor exposes the entire public, unauthenticated query surface of the City of Boston's Food Establishment Inspections CKAN datastore (data.boston.gov, resource id confirmed live 2026-08-17): every column the API returns is either an output field or a matching search filter, and every field with a small, stable set of known values (establishment type, license status, inspection result, violation status/severity, neighborhood, sort field) is a dropdown enum rather than free text.

What is intentionally not exposed, and why:

  • No separate "license category" filter. The raw licensecat column (FS/FT/MFW/RF) is a 1:1 duplicate of establishmentType on every one of the ~896K rows in the dataset (confirmed via a live GROUP BY query) — filtering on it would be a redundant second control for the same axis, so it's kept only as the human-readable establishmentType filter.
  • violationCodes stays free-text array, not a dropdown. There are 462 distinct violation codes in the live data — above the "<100 stable values → enum" threshold this project uses — so a curated dropdown would either be incomplete or unmaintainable; exact-code lookup (mode=byViolationCode) is the intended usage pattern here, typically copied from an inspection report.
  • state is not a filter. Over 98% of rows are MA (with a handful of Ma/ma casing variants and a small number of nulls); it is not a meaningful search axis for a single-city dataset.
  • licenseIssuedFrom/licenseIssuedTo (issdttm) and violationDate (violdttm)/statusDate (status_date) are output-only, not separate date filters. licenseExpiresFrom/licenseExpiresTo (a genuinely distinct, well-populated axis used for compliance/renewal monitoring) is exposed as a filter; adding four more date-range filters for closely-related/lower-signal timestamp columns on top of the existing dateFrom/dateTo (result date) and licenseExpiresFrom/licenseExpiresTo would add input-schema surface area without a correspondingly distinct real-world query need — all four values are still returned on every record for client-side filtering if needed.
  • No authentication-gated data exists for this dataset. The CKAN datastore_search_sql and package_show endpoints used by this actor are fully public with no API key, cookies, or login — nothing is held back for auth reasons.