How-to
How to run New York Contractor License Lookup & Leads Scraper
Step-by-step: the exact input, what each field does, and the rows that come back from NYC DCWP + Westchester + Nassau + Buffalo.
New York State has no statewide contractor licence. Home improvement contractors are licensed by the city or county they work in, so a New York contractor list means going to each agency that publishes one. New York City’s Department of Consumer and Worker Protection (DCWP) puts its 18,931 licences on NYC Open Data, which does export a CSV, and Buffalo does the same for its 851. Westchester County shows its roster only on a map you search one ZIP code or city at a time, across 778 ZIP codes. Nassau County’s Consumer Affairs report shows 15 vendors a page across 12 licence types. If you only need NYC, the Open Data export is free and good enough. People open this actor when they need several of these at once in one format, or want to check a batch of licence numbers without knowing which agency issued each one.
What you’ll need
Nothing but an Apify account. You don’t need an API key or a proxy. We keep a stored copy of the four rosters and your run filters that copy, so it never waits on a county website. You pay $0.004 per licence returned, plus Apify’s own platform usage.
Step 1 — Open the actor
Open New York Contractor License Lookup & Leads Scraper on the Apify Store and press Try for free. The actor’s own page on this site lists its live health and pricing.
Step 2 — Fill in the input
| Field | Type | Required | What it does |
|---|---|---|---|
mode | string | no | auto = export when no query is given, otherwise lookup. (default auto) |
query | string | no | A business name (or listing number) to look up in the stored copy of the directory. Kept alongside ‘queries’ for saved tasks. |
queries | array | no | A business name (or listing number) to look up in the stored copy of the directory. Several values in one run; combined with ‘query’. (default []) |
status | string | no | Only listings in this status. Kept alongside ‘statuses’ for saved tasks. |
statuses | array | no | Only listings in this status. Several values in one run; combined with ‘status’. (default []) |
city | string | no | Only listings in this city. Kept alongside ‘cities’ for saved tasks. |
cities | array | no | Only listings in this city. Several values in one run; combined with ‘city’. (default []) |
categories | array | no | Shown as Locality in the Console. Only rows from this locality (one or more). Available: buffalo, nassau, nyc, westchester. (default []) |
maxResults | integer | no | Per query in lookup mode; per run in export mode. (default 500) |
Three fields decide what comes back. status takes the agencies’ own words, normalised: leave it empty and you get all 36,391 licences, 5,429 of them expired NYC ones, so for a lead list set it to Active (30,514 rows on 2026-10-27). categories here means locality, not trade: nyc, westchester, nassau or buffalo. city matches the address on the licence, which is where people get surprised: a Brooklyn search returns 3,020 active licences, 2,862 from NYC and the rest firms with a Brooklyn address licensed by Westchester or Nassau. To check licences, put their numbers in queries. NYC numbers look like 0760935-DCA and Nassau’s like H3811010000; Westchester and Buffalo publish no licence number, so look those up by company name.
A working input:
{
"status": "Active",
"city": "Brooklyn",
"maxResults": 500
}
Step 3 — Run it
Press Start. Rows are pushed to the dataset as they’re found, so the run log fills in as it works.
Press Start. A run reads the stored copy, so it finishes in seconds: our first test-customer run, 3 rows, took 11 s. The input above returns 500 of the 3,020 active Brooklyn licences; raise maxResults to 3,020 to get them all. An empty result means no licence matches all your filters together, usually a city spelled differently from the agency’s address or a locality that doesn’t cover that city. You get a free no_match row saying so, and a licence number that isn’t on file gets one too.
Step 4 — Read the output
Each run returns a labelled table, not raw JSON:
Every row looks like this — a real row from a real run:
{
"listing_id": "0760935-DCA",
"name": "ALL TYPE SIDING & ROOFING INC.",
"phone": "7187456718",
"phone_e164": "+17187456718",
"street": "814 64TH ST",
"city": "BROOKLYN",
"region": "NY",
"postal_code": "11220",
"country": "US",
"category": "home improvement contractor",
"categories": [
"home improvement contractor"
],
"lat": 40.63289693131491,
"lng": -74.01136331221319,
"source": "snapshot",
"source_url": "https://data.cityofnewyork.us/resource/w7w3-xahh.json?license_nbr=0760935-DCA",
"extras": {
"locality": "nyc",
"status": "Active",
"expires": "2027-02-28",
"dba": "ROCCO'S ALL TYPE ROOFING",
"borough": "Brooklyn",
"licensed_since": "2003-02-04"
}
}
| Field | Example value | Meaning |
|---|---|---|
listing_id | 0760935-DCA | Licence number: NYC DCWP (e.g. 0760935-DCA) or Nassau (e.g. H3811010000); empty for Westchester and Buffalo, which issue none publicly |
name | ALL TYPE SIDING & ROOFING INC. | Company name |
extras.locality | — | Which roster the row came from: nyc, westchester, nassau or buffalo |
extras.status | — | Active, Expired, Surrendered, Suspended, … |
extras.expires | — | Licence expiry date (NYC, Westchester, Buffalo) |
phone | 7187456718 | Phone as the agency printed it |
phone_e164 | +17187456718 | The same number in +1 format |
street | 814 64TH ST | Street address (NYC, Westchester, Nassau) |
Sellers come for phone and street; people checking a contractor come for extras.status and extras.expires. Of the 30,514 active licences on 2026-10-27, 29,561 have a phone (96.9%) and 29,661 a street address. Watch for what each locality leaves out. No locality publishes email. Buffalo’s 851 rows have no street address, Nassau’s have no expiry date, and only NYC and most of Westchester carry map coordinates. NYC’s own dataset was last updated by the city on 2026-08-20. Westchester and Buffalo publish only an expiry date, so a licence whose date has passed comes back as Expired with extras.status_note saying the status came from the date.
Step 5 — Export it
Open the Dataset tab and export to CSV, JSON, or Excel — or pull the same rows from the API, which is what you want if this is going to run on a schedule.
What it costs
$0.004 per licence returned. Free rows (no match, unknown category, stale data) cost nothing. The Brooklyn example above is 500 licences, $2.00; all 3,020 active Brooklyn licences are $12.08. Every active licence across the four localities, 30,514 rows, is about $122.06. Apify’s platform usage for a run this short is a fraction of a cent on top.
Where the data comes from
This reads NYC DCWP + Westchester + Nassau + Buffalo directly. The target is Four local rosters swept into a stored copy over plain HTTP: NYC DCWP and Buffalo licences from Socrata, Westchester County’s map handler read once per ZIP code (778), and Nassau County’s Oracle APEX vendor report paged through its own ajax pager; runs filter the copy and call no source. Twice a day our checks run this actor against the stored copy and fail if active NYC licences in Brooklyn stop returning 50 rows with licence numbers, names and street addresses (what “verified” means).
Reading the four takes about 13.5 minutes, and 13 of them are Westchester: its map answers one ZIP code per request, and the request fails with a server error unless it carries a cache-busting parameter the page adds itself. Nassau’s report has to be paged with tokens copied from its own page. NYC and Buffalo are ordinary open-data feeds. Suffolk, Rockland, Putnam and the rest of the state publish no roster we can read, so they are not in this dataset.
See it used
Brooklyn NY Licensed Home Improvement Contractors with Phone Numbers runs the input above for a supplier building a Brooklyn call list.
If your contractors also work across the Hudson, New Jersey Contractor License Lookup covers New Jersey’s home improvement contractors.
Last updated 2026-10-27