US Building Permits Scraper - 14 Cities & Counties avatar

US Building Permits Scraper - 14 Cities & Counties

Pricing

from $1.79 / 1,000 permits

Go to Apify Store
US Building Permits Scraper - 14 Cities & Counties

US Building Permits Scraper - 14 Cities & Counties

One row per building permit from 14 US city and county open-data rolls: permit number, type, status, issue date, address, scope of work, declared value and contractor. New York City, Chicago, Los Angeles, Austin, Seattle and nine more. $1.85 per 1,000 permits.

Pricing

from $1.79 / 1,000 permits

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

18 hours ago

Last modified

Share

Pick US cities, set an issue date, and get one row per building permit: the number, type, status, issue date, address, scope of work, declared value, contractor and the city's own record link, under the same field names in every city. Add a filter if you want one: permit type, scope of work, contractor, status or declared value.

The awkward part: every city publishes different columns, so a roll that cannot answer one of your filters is skipped, free, with a row saying why. And big cities such as San Francisco, Boston, Houston and Philadelphia are not in it at all.

InputJurisdiction keys, an issue-date window, optional filters on address, city, postal code, type, scope, contractor, status or value, or exact permit numbers
OutputOne row per permit: number, type, work class, status, issue, applied and expiry dates, address, scope of work, declared value, contractor, coordinates, record link
Ceiling20,000 permits and 50 permit numbers per run
Account neededNone, and no API key
Price$1.85 per 1,000 permits on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 2,700

🔍 What US Building Permits Scraper does

A permit issued this week is a job starting next month. Each roll is read newest first, so issuedAfter turns the run into a feed of what was just approved.

Before anything is fetched, the run works out which rolls can answer your filters. A roll that cannot, such as one with no postal codes when you filter on a postal code, is skipped with a free FILTER_UNSUPPORTED row naming the roll and the filter. It is never sent a looser question and billed for the answer.

Your row budget is split evenly across the rolls first. Whatever a short roll does not use goes to the ones that still have permits.

No phone numbers, e-mail addresses or owner names are read, even where a city publishes them. Orlando's only free-text field is usually the homeowner's name, so it is not read, which is why Orlando comes back with no scope of work. On Chicago only contacts typed as a contractor are read.

Two rolls are narrower than their name. Seattle covers building permits only, not its electrical, trade or land-use permits. Nashville publishes a rolling recent window rather than its full history.

📋 What data you get from each building permit

What you getField
The jurisdiction, and the city's own permit numberjurisdiction, jurisdictionKey, permitNumber
Type, work class and status, in the city's own wordspermitType, workClass, status
Issued, applied, expiry and status datesissuedDate, appliedDate, expiresDate, statusDate
The site address, parcel and positionaddress, city, state, zip, parcelId, latitude, longitude
The scope of work and its declared valuedescription, declaredValueUsd
The contractor, where the city names onecontractorName, contractorLicense, contractorLicenseType, contractorTrade
Floor area and housing units, where publishedbuildingAreaSqFt, housingUnits
The city's own record page, and the department that published itrecordUrl, publisher

▶️ How to scrape US building permits

  1. Open US Building Permits Scraper and click Try for free.
  2. Pick your cities in Jurisdictions. The form arrives with Seattle and Austin chosen.
  3. Set Issued on or after. It arrives set to 2026-09-01, so move it to the date you care about.
  4. Add a filter such as Permit type contains or Declared value over if you want one, and set Maximum rows.
  5. Click Start, then download the dataset as CSV, JSON or Excel, or read it from the Apify API.

💰 How much does it cost to scrape building permits?

$1.85 per 1,000 permits, which is $0.00185 a permit on the Free plan. Paid plans pay a little less: $1.83 on Starter, $1.81 on Scale and $1.79 on Business. On the free plan, the $5 Apify gives you each month covers about 2,700 permits. The 200-permit example below comes to about 37 cents.

You pay for permit rows and nothing else per row. The sample row and every diagnostic row are free, and so are rolls skipped for a filter they cannot answer, records missing a permit number or an issue date, and repeats inside one run. If you set a maximum charge on the run, it stops at that figure instead of going past it.

📥 What you give it

{
"jurisdictions": ["seattle", "raleigh", "cleveland"],
"issuedAfter": "2026-09-01",
"permitTypeContains": "ROOF",
"minValueUsd": 10000,
"maxItems": 200
}
FieldDefaultWhat it is
jurisdictionsall fourteen; the form starts with seattle and austinKeys from the table below. A key that does not exist gets a free BAD_INPUT row listing the real ones.
issuedAfternone; the box starts at 2026-09-01YYYY-MM-DD. Every roll supports it.
issuedBeforenoneThe other end of the window, same format.
permitNumbersnoneUp to 50 numbers for a direct lookup, in the city's own format. When set, every filter except jurisdictions is ignored, dates included.
addressContainsany addressPart of the site address, case-insensitive. Some rolls hold only the street name, so a street name matches more widely than a house number.
cityany cityNarrows to one city. A single-city roll either is that city or is skipped, free.
zipCodeanyA five-digit postal code. Chicago, Mesa, Orlando and Cleveland cannot be filtered on one and are skipped.
permitTypeContainsany typePart of the permit type or work class, such as ROOF, SOLAR, DEMOLITION or NEW. Every city names its own types, so a short word travels further than a phrase.
descriptionContainsanyPart of the scope of work, such as KITCHEN or TEAR OFF. Orlando is skipped.
contractorContainsanyPart of the contractor or permit-holder business name. Los Angeles and Cincinnati are skipped.
statusContainsanyPart of the status, such as ISSUED, FINAL or EXPIRED, in each city's own words. Baton Rouge and Nashville are skipped.
minValueUsdno minimumOnly permits declared above this figure, in dollars. New York City, Los Angeles and Cincinnati store the value as text and are skipped.
maxItems50Total permits, 1 to 20,000, shared across the rolls.
proxyUrlsnoneYour own servers to send the traffic through, as http://user:pass@host:port. Leave it empty unless you need that.
JurisdictionKeyPermits on the roll, 20 Sep 2026Skipped when you filter on
New York City, NYnew-york-city1,002,770declared value
Chicago, ILchicago847,927postal code
Los Angeles, CAlos-angeles412,133contractor, declared value
Austin, TXaustin2,376,402nothing
Seattle, WAseattle193,083nothing
New Orleans, LAnew-orleans464,215nothing
Cincinnati, OHcincinnati180,257contractor, declared value
Mesa, AZmesa-az156,208postal code
Orlando, FLorlando1,110,846scope of work, postal code
Baton Rouge, LAbaton-rouge143,535status
Cleveland, OHcleveland200,435postal code
Columbus, OHcolumbus-oh682,606nothing
Raleigh, NCraleigh184,185nothing
Nashville, TNnashville29,512status

A roll that carries decades of history holds far more old permits than new ones, so set issuedAfter if you only want what is current. The counts move as cities publish.

An input with nothing in it, such as {} sent through the API, returns one free sample row so you can see the shape first.

📤 What you get back

A real row from a run on 30 September 2026:

{
"ok": true,
"charged": true,
"recordType": "permit",
"jurisdiction": "Seattle, WA",
"jurisdictionKey": "seattle",
"permitNumber": "7088872-CN",
"permitType": "Addition/Alteration",
"workClass": "Non-Residential Institutional",
"status": "Issued",
"issuedDate": "2026-09-28",
"appliedDate": "2026-06-24",
"expiresDate": "2028-03-28",
"statusDate": null,
"address": "3919 OKANOGAN LN NE",
"city": "SEATTLE",
"state": "WA",
"zip": "98195",
"description": "Shoring and excavation for future Institutional building [UW CHEMICAL SCIENCES] per plan",
"declaredValueUsd": 1885061,
"contractorName": null,
"contractorLicense": null,
"contractorLicenseType": null,
"contractorTrade": null,
"buildingAreaSqFt": null,
"housingUnits": null,
"parcelId": null,
"latitude": 47.65377801,
"longitude": -122.30999134,
"recordUrl": "https://services.seattle.gov/portal/customize/LinkToRecord.aspx?altId=7088872-CN",
"publisher": "Seattle Department of Construction and Inspections",
"scrapedAt": "2026-09-30T02:37:45.865Z"
}
FieldHow to read it
permitNumberThe city's own number. Not unique on its own: two cities can share a number, and one number can cover several lines of work. Key on jurisdictionKey, permitNumber and permitType together.
permitType, workClass, statusThe city's own wording, never normalised. Seattle says "Addition/Alteration", Austin "Plumbing Permit". Filter with a short substring.
issuedDateAlways filled, as YYYY-MM-DD. A record with no issue date is never delivered.
descriptionThe scope of work as the applicant wrote it. Columbus gives a work category instead, and Orlando gives nothing.
declaredValueUsdWhat the applicant declared the work is worth. It is not an appraisal and cities do not audit it. A zero comes back as null.
contractorName, contractorTrade, contractorLicenseThe contractor or permit-holder business, where the city publishes one. Seattle fills it on well under a fifth of its permits, as in the row above.
recordUrlThe city's own page for the permit. Six of the fourteen publish one.
latitude, longitudeAs the city published them, not geocoded here. null where it publishes none.

🧾 Reading the output

Three kinds of row can share a dataset.

RowHow to spot itCharged
A permitrecordType: "permit", charged: trueyes
The samplerecordType: "sample", _sample: true, charged: falseno
A diagnosticrecordType: "diagnostic", _diagnostic: true, charged: false, an errorCodeno

To keep only permits, filter on recordType == "permit". charged: true marks a real permit row; read it as that, not as a receipt. A diagnostic about one roll names it in jurisdiction.

CodeWhat it means
FILTER_UNSUPPORTEDOne roll was skipped because it cannot answer a filter you set. The row names the roll and the filter.
NO_RESULTSA roll had nothing matching, or none of the rolls you picked can answer your filters.
BAD_INPUTA jurisdiction key that does not exist, or a city refused the query as sent.
RATE_LIMITED, SERVER_ERRORThat city's server was busy or answered with an error. The other rolls carry on.
NETWORKA city's server could not be reached, or sent back something unreadable.
NOT_SERVEDThis permit was not served this run. Re-run it later. Until 12 October 2026 this code was called BLOCKED.
TIME_BUDGETThe run ran out of time before a roll was finished.
CHARGE_ERRORSome charges could not be recorded, so some rows may have arrived unbilled.

💡 What people use it for

  • A roofing, solar or HVAC contractor watching what has just been permitted in their own area, filtered with permitTypeContains.
  • A supplier sizing next quarter from what has actually been approved: count new-build permits above a declared value.
  • A lender or proptech team spotting renovation work on a property before it reaches a listing.
  • Anyone who has been checking four city portals by hand every Monday morning and would rather have one table.

🚧 What it does not do

  • Fourteen jurisdictions, not fifty states. San Francisco, Boston, Philadelphia, Denver, Phoenix, Houston, Dallas, San Antonio, San Diego, Miami, Detroit, Baltimore, Washington DC, Kansas City and Honolulu are not covered. The cities that were checked and left out mostly had rolls that were stale, too thin to be a permit record, or split into one dataset per year.
  • No contact details. No phones, e-mails or owner names, and nothing joined to parcel or assessment data.
  • No normalised vocabulary. Types and statuses stay in each city's own words.
  • No fields a city does not publish. A missing contractor or value is null, never filled in.
  • Only as fresh as the city. A permit revised after the roll was last refreshed still reads as it did, and each city refreshes on its own schedule.
  • Repeats across runs are billed again. Inside one run a permit is charged once. On a schedule, de-duplicate on jurisdictionKey, permitNumber and permitType.
  • It is not an official tool of any platform or company named on this page. Dami's Studio is independent and is not affiliated with or endorsed by any of them.

🧭 Which property data scraper do you need?

If you wantUse
Building permits from 14 US cities and countiesThis one
Parcel and assessment records from the public assessment rollUS Property Owner & Parcel Records Scraper
Local contractors by trade and town, with phone numbersYellow Pages US Scraper
Open bids from states, counties, cities and school districtsUS Local Bid Portals Scraper

❓ Questions people ask

How current is the permit data?

Whatever the city last published. On most rolls that means permits issued within the last day or two, and a long weekend shows up as a gap.

Can I get a daily feed of new building permits?

Yes, and it is the main way people run it. Schedule it with a recent issuedAfter and de-duplicate against what you already have.

Why is contractorName empty on some rows?

The city did not publish one. Los Angeles and Cincinnati never name the contractor, and Seattle rarely does.

Why is the same address in there several times?

One job often needs several permits: building, then electrical, plumbing and mechanical. Each is its own permit and its own row.

Do I need an account or an API key?

No. These are open-data rolls the cities publish themselves, and nothing here needs a key.

Can I call it from code or connect it to an AI assistant?

Yes. The API tab has ready-made code for Python, JavaScript and the command line. For Claude, ChatGPT or another MCP client, connect https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/us-building-permits-scraper. Either way the run happens on your Apify account at the same price.

Each roll is a public record the city publishes as open data. No phone numbers, e-mail addresses or owner names are read, even where a city publishes them. Apify's write-up on the legality of web scraping is a fair starting point, and we are not lawyers.

🆘 If something breaks

Open the Issues tab on the actor page and send the run ID with the input you used. The errorCode, jurisdiction and details on a diagnostic row usually name the reason already.