Request a tool
All toolsAutomationsGuidesMCP serverRequest a toolPlatformsCategories
US Building Permits Scraper icon

US Building Permits Scraper

Building permits from 14 US cities and counties: permit number, type, status, issue date, address, work description, declared value and contractor.

49 runs on Apify $0.00185 per permit ($1.85 / 1,000)
Run this in the cloudRun on Apify →

Developer & Research Tools

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust jurisdictions, issuedAfter, issuedBefore (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Pricing

$0.00185 per permit = $1.85 per 1,000

You are charged forWhenPrice
Permit scrapedOne building permit with its type, status, value and address. Jurisdictions that cannot answer a filter are skipped and never charged.$0.00185

Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-20, and they are what you are actually charged.

Inputs

FieldWhat it doesType
jurisdictionsWhich permit rolls to read. Leave empty to read all fourteen. Keys: new-york-city, chicago, los-angeles, austin, seattle, new-orleans, cincinnati, mesa-az, orlando, baton-rouge, cleveland, columbus-oh, raleigh, nashville.array
issuedAfterOnly permits issued on or after this date, as YYYY-MM-DD. This is the filter that makes the feed useful as a leading indicator: a permit issued this week is a job starting next month. Every jurisdiction supports it.string
issuedBeforeOnly permits issued on or before this date, as YYYY-MM-DD. Combine with the field above for a fixed window.string
permitNumbersUp to 50 permit numbers. When this is filled in, every other filter is ignored so you get exactly those permits. The number has to match the jurisdiction's own formatting.array
addressContainsMatch part of the site address, case-insensitive - a street name such as BROADWAY, or a house number and street together. Matched against whatever address text the jurisdiction publishes, which on some rolls is the street name only.string
cityNarrow to one city. On a roll that covers a single city already, this either matches that city or the jurisdiction is skipped and nothing is charged for it.string
zipCodeA five-digit US postal code. Four of the fourteen rolls publish no postal code column; those are skipped, free, with a row saying so.string
permitTypeContainsMatch part of the permit type or work class, case-insensitive - ROOF, SOLAR, ELECTRIC, DEMOLITION, NEW. Each city names its own types, so try a short word rather than a whole phrase.string
descriptionContainsMatch part of the free-text scope of work, case-insensitive - SOLAR, ROOF REPLACEMENT, KITCHEN. Orlando publishes no scope-of-work text and is skipped, free, when you use this.string
contractorContainsMatch part of the contractor or permit-holder business name. Los Angeles and Cincinnati do not name the contractor on their permit roll and are skipped, free, when you use this.string
statusContainsMatch part of the permit status - ISSUED, FINAL, EXPIRED. Each city has its own vocabulary. Baton Rouge and Nashville publish no status and are skipped, free, when you use this.string
minValueUsdOnly permits whose declared construction value is above this figure, in US dollars. New York City, Los Angeles and Cincinnati publish that value as text rather than a number, so they cannot be compared against it and are skipped, free, with a row saying so.integer
maxItemsTotal permits to return across every jurisdiction you picked. The budget is shared evenly first, then the rolls that still have rows take whatever the short ones did not use. Default 50, hard ceiling 20,000. Keep it low while testing - you pay per row.integer
proxyUrlsLeave this empty unless you want the requests to go through proxy servers of your own. Add each one as http://user:pass@host:port.array

What you get

A structured dataset — each result includes fields like:

jurisdictionjurisdictionKeypermitNumberpermitTypeworkClassstatusissuedDateappliedDateexpiresDateaddresscitystatezipdescription

Export every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.

Related tools in Developer & Research Tools

Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.

GitHub Scraper iconDeveloper & Research Tools

GitHub Scraper

Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.

18 use cases

Stack Overflow / Stack Exchange Scraper iconDeveloper & Research Tools

Stack Overflow / Stack Exchange Scraper

Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.

2 use cases

Package Registry Scraper (npm + PyPI) iconDeveloper & Research Tools

Package Registry Scraper (npm + PyPI)

Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.

2 use cases

arXiv Scraper iconDeveloper & Research Tools

arXiv Scraper

Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.

2 use cases

OpenAlex Scholarly Works Scraper iconDeveloper & Research Tools

OpenAlex Scholarly Works Scraper

Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.

2 use cases

Crossref Scholarly Works Scraper iconDeveloper & Research Tools

Crossref Scholarly Works Scraper

Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.

2 use cases

See all Developer & Research Tools →

US Building Permits Scraper: new permits from 14 US cities and counties in one table

Pick the cities, set an issue date, and add a filter if you want one: permit type, scope of work, contractor, status or declared value. You get one row per permit with 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. 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
Ceiling20,000 permits and 50 permit numbers per run
Account neededNone
Price$1.85 per 1,000 permits, flat on every plan

🔍 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, email 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 you give it

FieldIf you leave it outWhat it does
jurisdictionsall fourteenKeys from the table below. A key that does not exist gets a free BAD_INPUT row listing the real ones.
issuedAfterno start dateYYYY-MM-DD. Every roll supports it.
issuedBeforeno end dateThe 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.
proxyUrlsnot usedYour 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.

{
  "jurisdictions": ["seattle", "raleigh", "cleveland"],
  "issuedAfter": "2026-09-01",
  "permitTypeContains": "ROOF",
  "minValueUsd": 10000,
  "maxItems": 200
}

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 real run:

{
  "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"
}
FieldWhat it is
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". 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.
BLOCKEDA city answered, but not with permit records.
TIME_BUDGETThe run ran out of time before a roll was finished.

▶️ How to run it

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.

💰 How much does it cost?

$1.85 per 1,000 permits, which is $0.00185 a permit, flat on every Apify plan. The 200-permit example above 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 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, emails 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.

🧭 Which property data scraper do you need?

If you wantUse
Building permits from 14 US cities and countiesThis one
Who owns a property, from the public assessment rollUS Property Owner & Parcel Records Scraper
Homes for sale with price, tax history and schoolsZillow Scraper
Listings with the agent and the brokerRealtor.com Scraper
Open bids from states, counties, cities and school districtsUS Local Bid Portals Scraper

❓ Questions people ask

How current is the 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 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.

Can I look up one specific permit? Yes. Put its number in permitNumbers and every other filter is ignored.

Do I get phone numbers or owner names? No, on purpose, even on rolls that publish them.

🆘 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.