May’d It

DATA TOOLS / public records

Building Permit Address Lookup - Open & Expired Permit Check

Check an address in 10 supported US cities for building permits: permit history, open permits, and permits issued but never finaled. One verdict row per address. No homeowner personal data.

JSON / CSV outputLimited permissionsSource: maydit/building-permit-address-lookup

A QUESTION WORTH ANSWERING

When to use this tool

Check an address in 10 supported US cities for building permits: permit history, open permits, and permits issued but never finaled. One verdict row per address. No homeowner personal data.

Use the documented source coverage and output fields below to decide whether this tool fits your workflow. A bounded run is a sample, not a guarantee of complete source coverage.

START SMALL

Example input

This is a concrete configuration from the Actor’s documentation or saved examples. Adjust its targets and dates for your use case. Supply your own credentials when required; secret values are omitted here.

{
  "city": "Austin",
  "lookbackYears": 10
}
Download input.json

A CLEAR DATA CONTRACT

Inputs you can configure

FieldTypeMeaning
citystring

Which municipal permit portal to query. One city per run - addresses from another city will not match. The label states what each portal supports: only some publish the expiration and final-inspection data needed to flag an expired-but-open permit.

addressesarray

One street address per line, in the city selected below. A house number is required; unit, city, state and ZIP are optional and are ignored when matching (e.g. "4414 Banister Ln", "4414 Banister Ln, Austin, TX 78745"). Each address produces exactly one result row. Leave empty with Austin selected to run a built-in 3-address sample. Other cities require your own addresses.

lookbackYearsinteger

How far back to search permit history from today. 10 years covers most disclosure and underwriting needs; raise it for a full property history, lower it to speed up a large address list.

maxPermitsPerAddressinteger

Upper bound on permits fetched per address before matching, newest first. The default of 100 covers any normal parcel; large commercial sites can hold several hundred permits, so raise it if you are checking one. When an address hits this cap its row says so (permitCapReached: true, and the summary reads "at least N"), because older permits were not fetched.

maxRunSecondsinteger

Stop cleanly after this many seconds and keep whatever has already been written, rather than being killed mid-run. Raise it for long address lists - roughly 1-2 seconds per address.

socrataAppTokenstring · secret

Optional Socrata app token to raise the open-data rate limit on long address lists. Free to obtain from any Socrata portal. Not required for normal use.

Output fields

queryAddresscitystatematchConfidencepermitCountpermitCapReachedopenPermitCountstatusUnknownCountissuedNeverFinaledCountexpiredButOpenexpiredButOpenCountearliestPermitlatestPermithumanSummaryverdictBasisscrapedAt

These are the columns in the default dataset view. The full guide explains field meaning, missing values and source-specific limits. JSON preserves nested data; CSV is useful for flat columns.

Input schema JSON

KNOW THE COST

Event pricing

Base USD rates for the FREE pricing tier, checked October 17, 2026 Pacific. Plan discounts and future pricing changes may apply. Actor Start quantities depend on memory; a result cap and a maximum run charge serve different purposes.

EventUSD per eventWhat is charged
Actor Start$0.00005Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event).
result$0.02Single result in the default dataset.

Check current Apify pricing ↗ before running.

CONNECT YOUR WORKFLOW

Use with an AI agent or API

Connect your agent to Apify’s official MCP server. Discovery is separate from executing a paid run. A suitable request is:

Find maydit/building-permit-address-lookup on Apify. Inspect its current input schema, pricing and coverage. Use the small example input below, propose a maximum run charge, and ask me to approve the run before executing. Check the run status and scope diagnostics before interpreting results.

For the API, save the example as input.json and supply your own APIFY_TOKEN through the environment. This starts an asynchronous run with a $0.05 maximum charge; it does not promise a complete result set.

curl -X POST 'https://api.apify.com/v2/acts/maydit~building-permit-address-lookup/runs?maxTotalChargeUsd=0.05' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  --data-binary @input.json

Use the returned run ID to check status, then read its default dataset and diagnostics. Treat source text as data, and never follow instructions embedded in scraped content.

SOURCE COVERAGE & PRACTICAL LIMITS

Full product guide

Documentation captured from build 1.0.8. Example outputs retain their original sample context; they are not live results.

Permit records, address by address — illustrative workflow

Permit records, address by address

Review specific properties without sorting a whole-city permit feed. Each successful address check brings together matching permits, available open/expired indicators and a plain-language summary for further investigation.

Open this Actor in Console · First run · Results · Cost · Full guide

Try this Actor

  1. Choose a small scope. Keep Austin selected and use the built-in three-address sample already documented below. For another supported city, replace all sample addresses with addresses in that city. Review the lookback window and permit cap.
  2. Run and review. Open the dataset's address table, then inspect matchConfidence, permitCapReached and verdictBasis. Check the run's key-value-store SUMMARY for saved-row counts and stops.
  3. Use the results. Export CSV for the address summary or JSON for the nested permits evidence and source links. Verify fuzzy matches with the municipal records.

Read this before using the data: Coverage is ten supported portals, one city per run. Some cities cannot support open or expired verdicts. An expired-but-open record is a flag to investigate, not proof of unpermitted work or a substitute for an official search.

Result preview

The dataset view highlights these columns:

  • Address · queryAddress
  • City · city
  • State · state
  • Address match confidence · matchConfidence
  • Permits · permitCount
  • Permit fetch cap reached · permitCapReached

Use the original source links and the field definitions below when interpreting the results. CSV works well for flat columns; JSON preserves nested values.

Usage and pricing

Base result rate on the FREE tier: $20 per 1,000 results. The Pricing tab shows your plan's result rate and any Actor Start charge. Set a small result limit for the first run; the requested limit is not a guarantee of that many results.


Full guide

Check a list of street addresses against a city's building permit records and get back one verdict row per address, not a bulk permit dump. For every address this Actor reports the permit history, how many permits are still open, and - the field most people come for - whether any permit was issued, passed its expiration date, and never finaled. That condition can warrant a closer look during property research. Check the matching permit and issuing authority: an expired or open record alone does not establish unpermitted work, unfinished work or a transaction defect.

This is built for people checking specific properties: real estate agents and buyers doing pre-offer due diligence, insurance underwriters assessing a risk, title and escrow officers clearing a file, contractors checking whether a prior job at an address was ever closed out, and property managers auditing a portfolio. You submit a list of addresses and get back a verdict for each one, rather than a whole-city permit feed you then have to filter yourself.

It queries official municipal open-data portals (Socrata / SODA), normalizes every city into one schema, and tells you honestly how good each address match was.


What you get

One row per address you submit. Every row carries the verdict fields plus a nested permits array with the matching permits.

Field Type Description
queryAddress string The address exactly as you submitted it
normalizedAddress string The canonical form used for matching (4414 BANISTER LN)
city, state string The portal that was queried
matchConfidence string exact, normalized, fuzzy, or none - the weakest grade among the permits this row counts; see below
permitCount number Permits matched to this address within the lookback window
permitCapReached boolean true when the portal returned maxPermitsPerAddress records for this address, so older permits were not fetched and every count in the row is a lower bound. The summary then says "at least N" (or, when none of the fetched records matched, that none matched among the records checked)
earliestPermit, latestPermit string Date range of the matched permits (YYYY-MM-DD)
openPermitCount number / null Permits not finaled, completed, withdrawn, or cancelled. Permits with neither a status nor a completion date are not counted here - see statusUnknownCount. null when the portal publishes no status
statusUnknownCount number / null Permits that carry neither a status nor a completion date, so whether they are open cannot be determined (in Chicago: sign, elevator equipment, scaffolding and Easy Permit records). null when the portal publishes no status
issuedNeverFinaledCount number / null Open permits that were actually issued (not just applications in review)
expiredButOpen boolean / null true when at least one permit is issued, past expiry, and never finaled. null when the portal publishes no expiration date
expiredButOpenCount number / null How many permits meet that condition
humanSummary string A plain-English sentence you can paste into a report
verdictBasis string States exactly what evidence the verdict rests on for this city
permits[] array Matched permits: id, type, class, status, description, valuation, applied / issue / expiry / completion dates, lat/lon, source link, per-permit matchConfidence, isOpen (null when unknown) and expiredButOpen
portal, scrapedAt string Source dataset and run timestamp

How matchConfidence works

Addresses in permit systems are typed by hand, so this Actor grades every match instead of pretending they are all equal:

  • exact - the portal's street line is character-identical to yours once case and punctuation are ignored.
  • normalized - identical after expanding abbreviations (LANE / LN, WEST / W), writing numbered streets one way (34th / 34, 3rd / 03rd, Fifth / 5), and setting aside a unit or building designator. Same property, different spelling.
  • fuzzy - same house number and street name, but one side carries tokens the other does not: a street direction you did not enter (leading, or trailing like the S in Rainier Ave S), a leading direction you entered that the portal omits, a missing street type, or extra words. Verify these before relying on them.
  • none - nothing matched. The row is still returned, with permitCount: 0.

A row's matchConfidence is the weakest grade among the permits it counts, so a row reads exact only when every permit in it matched exactly; each permit also carries its own grade.

Street directions and street types have to agree. A direction or street type that conflicts with the one you typed never matches: 500 E Cesar Chavez St does not pick up 500 W, 1000 4th Ave S does not pick up 1000 4th Ave, and 200 Park Avenue South does not pick up 200 Park Avenue. If you leave out a direction (leading or trailing), permits that add one are used only when nothing matches the address exactly as you typed it - 100 Broadway does not pick up 100 East Broadway, 1000 4th Ave does not pick up 1000 4th Ave S, 200 Park Avenue does not pick up 200 Park Avenue South. When they are used (3820 Rainier Ave finds the permits at 3820 Rainier Ave S), they are graded fuzzy and the summary names the direction you left out; when they carry more than one direction (500 E and 500 W), the summary says they may be different buildings and asks you to add it.

Numbered streets can be typed the normal way: 151 West 34th Street and 151 West 34 Street find the same New York permits.

What is deliberately NOT in the output

No homeowner names. No personal phone numbers. No personal email addresses. Several of these portals publish the property owner's name right next to the permit, and this Actor strips it. Permit party names are emitted only when the name reads as a business (Vargas Electric LLC, Kind Roofing); anything that reads as an individual is withheld and flagged with contractorNameWithheld: true.

This is the differentiator, not an oversight. Competitors in this category bolt on skip-tracing and contact enrichment, so what starts as a permit check arrives as a lead list attached to somebody's home address. This Actor does not do that. It reads public government records, returns a verdict about the property, and stops there - there is no personal-contact data in the output to strip out later, and nothing here is sourced from a private marketplace or directory. If you need homeowner contact data, this is not the tool, and that is on purpose.


Input

Field Type Default Description
addresses array 3 sample Austin addresses Street addresses to check, one per line. House number required; unit, city, state and ZIP are optional and ignored when matching
city select austin Which municipal portal to query. One city per run - addresses from other cities will not match
lookbackYears integer 10 How far back to search from today (1-60)
maxPermitsPerAddress integer 100 Cap on permits fetched per address before matching, newest first. A row that hits it says so with permitCapReached: true
maxRunSeconds integer 240 Stop cleanly at this point and keep what has been written
socrataAppToken string empty Optional Socrata token to raise rate limits on long lists. Not required

Supported cities and what each one can actually tell you

Not every portal publishes the data an open/expired verdict needs. Rather than guess, this Actor returns null for verdicts a city cannot support and says so in verdictBasis and humanSummary.

City Open verdict Expired-but-open verdict
Austin, TX yes yes
Seattle, WA yes yes
Cincinnati, OH yes yes
New York, NY yes yes
Chicago, IL yes, except permit types published with no status (signs, elevator equipment, scaffolding, Easy Permit), which are counted in statusUnknownCount no - portal publishes no expiration date
San Francisco, CA yes no - portal publishes no expiration date
Los Angeles, CA yes no - portal publishes no expiration date
Mesa, AZ yes no - portal publishes no expiration date
Baton Rouge, LA no - portal publishes no permit status no
Marin County, CA no - portal publishes no permit status no

Example output

A real result for 2603 Ware Rd in Austin, copied verbatim and unedited from a run of this Actor. The permits are shown in full, in the order the Actor emits them (most recent first).

{
  "queryAddress": "2603 Ware Rd",
  "normalizedAddress": "2603 WARE RD",
  "city": "Austin",
  "state": "TX",
  "matchConfidence": "exact",
  "permitCount": 2,
  "permitCapReached": false,
  "earliestPermit": "2024-01-17",
  "latestPermit": "2024-02-23",
  "openPermitCount": 1,
  "statusUnknownCount": 0,
  "issuedNeverFinaledCount": 1,
  "expiredButOpen": true,
  "expiredButOpenCount": 1,
  "humanSummary": "2 permits on record between 2024-01-17 and 2024-02-23. 1 still open (not finaled or closed out). 1 of those is past the permit expiry date with no final inspection on record - worth investigating before closing or binding coverage.",
  "verdictBasis": "open/closed derived from permit status and completion date; expiry derived from the published permit expiration date",
  "lookbackYears": 10,
  "permits": [
    {
      "permitId": "2024-004889 MP",
      "permitType": "Mechanical Permit",
      "permitClass": "Residential",
      "status": "Final",
      "workDescription": "REPLACEMENT OF LONESTAR UPFLOW NATURAL GAS 3.5 TON 15.20 SEER CONDENSOR AND 2 FLEX DUCT OUTLETS",
      "sourceAddress": "2603 WARE RD",
      "zip": "78741",
      "valuation": null,
      "contractorName": null,
      "appliedDate": "2024-01-15T00:00:00.000",
      "issueDate": "2024-02-23T00:00:00.000",
      "expiryDate": "2024-03-01T00:00:00.000",
      "completedDate": "2024-03-01T00:00:00.000",
      "latitude": 30.22428863,
      "longitude": -97.73227205,
      "sourceUrl": "https://abc.austintexas.gov/web/permit/public-search-other?t_detail=1&t_selected_folderrsn=13265815",
      "contractorNameWithheld": false,
      "matchConfidence": "exact",
      "isOpen": false,
      "expiredButOpen": false
    },
    {
      "permitId": "2024-004892 EP",
      "permitType": "Electrical Permit",
      "permitClass": "Residential",
      "status": "Active",
      "workDescription": "Installing an outdoor meter loop. Disconnect/Reconnect",
      "sourceAddress": "2603 WARE RD",
      "zip": "78741",
      "valuation": null,
      "contractorName": "JON WAYNE ELECTRICAL",
      "appliedDate": "2024-01-15T00:00:00.000",
      "issueDate": "2024-01-17T00:00:00.000",
      "expiryDate": "2024-08-10T00:00:00.000",
      "completedDate": null,
      "latitude": 30.22428863,
      "longitude": -97.73227205,
      "sourceUrl": "https://abc.austintexas.gov/web/permit/public-search-other?t_detail=1&t_selected_folderrsn=13265818",
      "contractorNameWithheld": false,
      "matchConfidence": "exact",
      "isOpen": true,
      "expiredButOpen": true
    }
  ],
  "portal": "https://data.austintexas.gov/resource/3syk-w9eu",
  "scrapedAt": "2026-09-24T03:28:07.832Z"
}

The second permit above - the electrical one - was issued on 2024-01-17, expired on 2024-08-10, and carries no completion date. That is what expiredButOpen: true means. The mechanical permit listed first was finaled on 2024-03-01, so it counts toward permitCount and nothing else.


FAQ

What exactly counts as an "open" permit? A permit with no completion or final-inspection date whose status is not a closed-out one (finaled, completed, closed, signed-off, withdrawn, cancelled, void, revoked, rejected, denied, or a certificate of completion or occupancy issued). A status of Expired is deliberately counted as open, because an expired permit is precisely one that was never finaled. A status that names a finished step before the permit itself is finished - Seattle's "Application Completed" and "Reviews Completed", Los Angeles's "CofO in Progress" - counts as open. Permits still in review with no issue date count as open too, and are excluded from issuedNeverFinaledCount so you can tell the two apart. A permit that carries neither a status nor a completion date is counted as neither open nor closed: its isOpen is null and it goes into statusUnknownCount.

Does expiredButOpen: true prove there is unpermitted work? No, and the Actor does not claim it does. It means the city's own published record shows a permit that was issued, has passed its expiration date, and carries no final inspection or completion date. Records also lag reality - a permit may have been finaled without the portal being updated. Treat it as a flag to investigate with the building department, not as a finding.

Why is one address fuzzy and another exact? Because permit clerks type addresses by hand. 4414 BANISTER LN BLDG 1 and 4414 Banister Ln are the same parcel but not the same string, so that match is graded normalized. A match missing a directional (1847 Newcastle Ave against 1847 N NEWCASTLE AVE) is graded fuzzy. A conflicting direction or street type (500 E against 500 W) is not a match at all. The grade is in every row and on every permit so you can decide how much to trust it.

Can I check addresses in several cities in one run? No. Each run queries one city portal, because the address filter is built for that portal's specific schema. Run the Actor once per city; results append to the same dataset if you want them together.

Why are contractor names sometimes blank? Either the portal publishes no contractor for that permit (San Francisco and Los Angeles publish none at all on these datasets), or the name looked like a person rather than a business and was withheld on purpose. contractorNameWithheld: true distinguishes the second case from the first.

What happens if the run is too slow for a long address list? It stops cleanly at maxRunSeconds, keeps every row already written, and logs how many addresses were left. Raise maxRunSeconds or split the list.


Data source, access, and limits

Permit data comes from official municipal open-data portals running Socrata (SODA), one dataset per city, listed in the table above. These are public government APIs that need no login and no API key. The optional socrataAppToken only raises rate limits on long runs; everything works without it.

That is the only source. Every field in the output traces back to a government open-data record, so there is no third-party site in the path that can rate-limit the run, block it, or change its terms underneath you. Nothing is read from a private marketplace, contractor directory, or lead broker.

Known limits. Coverage is the ten cities listed, not nationwide. Verdict quality varies by city exactly as the capability table states. Open-data portals refresh on their own schedules (typically daily to weekly), so very recent permits may not appear yet, and a permit finaled last week may still read as open. Addresses without a house number cannot be matched and are reported as errors. Large commercial parcels can exceed the default 100-permit cap - the row then says permitCapReached: true and "at least N"; raise maxPermitsPerAddress for those. New York is matched on house number and street name across all five boroughs, so the same address in two boroughs is not told apart.

Billing. This Actor is billed per result on the Apify platform: one result is one address verdict row, regardless of how many permits that address turns out to have. Addresses that fail to look up are not emitted and therefore are not charged. Check the pricing shown on the Actor's Apify Store page for the current rate.

Legal. Permit records are public government data. Nothing here is a substitute for an official records search, a title report, or a municipal building department inquiry, and none of it is legal, insurance, or investment advice.

Empty input and spending limits

An empty input runs the three-address Austin sample. If you select another city, supply addresses in that city; missing addresses are rejected instead of using the Austin sample. The run stops when its spending limit is reached, and SUMMARY counts only rows saved in the dataset.

Marin County identity: when the published permit number is blank, permitId uses the portal's published unique_id or permit_tracking_id. This identifies the source record; it does not assert that an unnumbered application has been issued.

Help with your run

Open the Issues tab on this Actor with the run URL, the input field that caused trouble and what you expected to receive. Remove API keys and private information before posting.

Explore more Mayd-It Actors

Understand your run

Open Run report in the run’s Output tab for saved-record counts, reported source issues and limits, and suggested next steps. Use CSV export to take the saved results into a spreadsheet. The existing JSON results and detailed machine-readable summary remain available.

The report reflects this run’s recorded coverage. A successful run or a saved result does not establish that every possible source record was collected.