# Building Permit API - Permits, Contractors, Property Data (`nabeelbaghoor/building-permits-contractors-api`) Actor

US building permit data by address, city, county, ZIP or jurisdiction: permit type, status, job value, fees, approval and construction duration, inspection pass rate and work tags, the licensed contractors behind them, properties with permit history, zoning decisions and monthly market metrics.

- **URL**: https://apify.com/nabeelbaghoor/building-permits-contractors-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Real estate, Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 permit returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Building Permit API - Permits, Contractors, Property Data

Find out who is building what, where, and what it cost, from the permits US cities and counties issue.

### What it collects

- **Permits**: number, type and subtype, description and the provider's cleaned version of it, issuing jurisdiction, status, declared job value and fees, filing, issue, start, end and final dates, approval and construction duration in days, inspection pass rate and work tags.
- **Contractors**: legal and business name, trading name, licence number with issue and expiry dates, the trade classifications derived from a firm's actual work, business phone, email, website and LinkedIn, SIC and NAICS codes, revenue and employee count bands, review count and rating, plus permit count, average and total job value, average construction duration and average inspection pass rate.
- **Properties**: parcel number, type, legal owner and owner type, year built, lot size, building area, storey and unit counts, assessed market value, coordinates, and the whole permit history summarised on one record, including the last permit date by tag and by status.
- **Zoning and land use decisions**: title, date, description, asset class, category and subcategory, previous and new zoning with the uses the change allows, project value, lot size, the applicant, owner, representative and developer named on it, and the source URL.
- **Market metrics**: permit count, active and in-review counts, contractor count, average and total job value, average approval and construction duration and average inspection pass rate, by month, for an address, city, county, jurisdiction or a single contractor.
- **Coverage and reference data**: which fields are thin in a given area and date range, every permit tag you can filter on, every covered ZIP code, the current data release date, and your own credit usage.

### FAQ

#### What is building permit data used for?

Finding work before it is visible any other way. A filed permit names the property, the job, its declared value and the contractor, weeks or months before the work happens, which is why it drives lead generation for trades and suppliers, market sizing for manufacturers, property condition checks for insurers and lenders, and competitive tracking for contractors.

#### How do I search a city or a ZIP code?

Run the Geography lookup service first. This API identifies a place by its own `geo_id`, not by name, so a city typed as text will not work anywhere else in the actor. Look up the city, county, jurisdiction, ZIP or address, copy the `geo_id` from the result, and paste it into the Geography id field for the search you actually want. The actor refuses a search with no `geo_id` and says this, rather than returning an empty run you have to diagnose.

#### Can I find contractors by trade?

Yes, and by more than the licence says. The contractor trade filter uses classifications this API derives from the permits a firm has actually pulled, so a firm licensed as a general contractor that only does roofing is findable as a roofer. Select several trades and a firm must match all of them. Prefix a value with a minus to exclude it.

#### How do I find solar, roofing or HVAC jobs specifically?

Use permit tags. Tags are the work type as the provider classifies it, independent of how each jurisdiction words its own permit types, which is what makes a query comparable across the 20,000-plus authorities that issue permits. A minus prefix excludes, so `solar` with `-roofing` means solar work on jobs that are not roofing. Run the Permit tag list service to see every tag available.

#### Can I find properties that have *not* had work done?

Yes. Property search returns one record per property with its permit history summarised on it, including the last permit date by tag, so you can look for properties with no roofing permit on record, or none in the last fifteen years. That is the search that is hard to do from a permit feed alone, because absence is not an event.

#### What is a jurisdiction, and why does it matter?

The authority that issues the permit, which is often not the city the property sits in. Permit availability and the fields each authority publishes vary between them, so the same query returns richer data in one place than another. Run the Data coverage service for a geography and date range before a large collection: it reports which fields are thin or missing there, so an empty column is a known fact rather than a surprise.

#### Why did my filter seem to do nothing?

It may not have been sent. Each service accepts its own set of filters, and this provider refuses an unknown parameter outright rather than ignoring it, so the actor drops filters the chosen service does not take before the request and names every one of them in the run log. An empty run here is an explained one.

#### Does this actor change anything in my account?

No. Every route it calls is a read. This API has no write surface at all, so a scheduled run cannot alter anything beyond spending your provider credits on the rows it returns.

#### Does it return personal data about homeowners?

No. This API publishes two endpoints that return named individuals with personal email, phone, income range, net worth and household attributes, and neither is exposed here. Contractor rows carry business contact details for the firm, which is what the construction use case needs.

#### Do I need my own API key?

Yes. This actor is bring-your-own-key and never ships a key of its own. Paste your key from your provider account, or set it once as the DATA\_API\_KEY environment secret. The provider bills per record returned, and the actor reads the credit headers on every response so the run log tells you what it spent and what is left.

#### What does it cost?

Pay per result. A contractor or property row costs the most, because each one summarises a whole history; a permit row less; a market metric a fraction of that, because it is an aggregate you use to size a market before collecting it; and a geography lookup or coverage row is priced near zero, because finding the `geo_id` you need should not cost anything worth counting. Platform usage is included. Searches that match nothing are never charged.

### Example output

```json
{
  "recordType": "permit",
  "requested": "abc123def456",
  "found": true,
  "geoId": "abc123def456",
  "recordId": "9f2c1b7ae4",
  "permitNumber": "BP-2026-004871",
  "permitType": "Residential Alteration",
  "permitStatus": "final",
  "description": "INSTALL 8.4KW ROOF MOUNTED PV SYSTEM",
  "jurisdiction": "City of Austin",
  "jobValueUsd": 24500,
  "feesUsd": 312,
  "issueDate": "2026-03-11",
  "finalDate": "2026-05-02",
  "constructionDurationDays": 38,
  "approvalDurationDays": 14,
  "inspectionPassRate": 100,
  "tags": ["solar"],
  "contractorId": "c71a90ff23",
  "propertyType": "residential",
  "propertyYearBuilt": 1998,
  "propertyBuildingArea": 2140,
  "propertyMarketValueUsd": 486000,
  "addressStreet": "W 35TH ST",
  "addressCity": "AUSTIN",
  "addressState": "TX",
  "addressZipCode": "78703",
  "record": { "...": "every field the provider published, unchanged" }
}
```

### Keyword map

building permit API, construction permit data API, contractor data API, contractor leads API, roofing leads, solar permit data, HVAC contractor list, building permit search by address, permit records by city, construction project data, property permit history, zoning decision data, land use decision API, construction market data, permit data by ZIP code, licensed contractor lookup, construction lead generation, remodel permit data, US building permits.

# Actor input Schema

## `service` (type: `string`):

Permit search returns individual permits. Contractor search returns the firms pulling them in an area. Property search returns properties with their permit history summarised on one record, including properties with no permit of a given type. Decisions returns zoning and land use rulings. The metrics services return permit counts, job values and durations by month. Geography lookup turns a place name into the geo\_id every other search needs.

## `geoId` (type: `string`):

This API identifies a place by its own geo\_id, not by name, and almost every search requires one. Run the Geography lookup service with a city, county, ZIP or address first and copy the geo\_id it returns.

## `geoType` (type: `string`):

Which level of geography the lookup, details and metrics services work at. Details exist for cities, counties and jurisdictions only. Metrics exist for addresses, cities, counties and jurisdictions.

## `searchTerms` (type: `array`):

One place name per line for the Geography lookup service, such as a city, county, jurisdiction, ZIP code or street address. Each becomes its own search.

## `permitFrom` (type: `string`):

Start of the permit date range, as YYYY-MM-DD. Required by permit search, contractor search and the coverage service, because the provider scopes those by date rather than returning everything.

## `permitTo` (type: `string`):

End of the permit date range, as YYYY-MM-DD.

## `permitTags` (type: `array`):

Filter by work type, such as solar, roofing or hvac. Prefix a tag with a minus to exclude it, so solar and -roofing means has solar but not roofing. Run the Permit tag list service to see every tag this API publishes.

## `permitStatus` (type: `array`):

Filter by permit status. Leave empty for all statuses.

## `permitQuery` (type: `string`):

Free text matched against the permit description, for work the tags do not capture.

## `permitHasContractor` (type: `boolean`):

Restrict to permits that name a contractor. Useful when the point of the run is the firm rather than the job.

## `permitMinJobValue` (type: `integer`):

Only permits whose declared job value is at least this, in whole US dollars as the provider publishes it.

## `permitMinFees` (type: `integer`):

Only permits whose fees are at least this, in whole US dollars.

## `permitMinApprovalDuration` (type: `integer`):

Only permits that took at least this many days from filing to approval.

## `permitMinConstructionDuration` (type: `integer`):

Only permits whose construction ran at least this many days.

## `permitMinInspectionPassRate` (type: `integer`):

Only permits whose inspections passed at least this percentage of the time.

## `propertyType` (type: `array`):

Filter by the type of property the work was done on. Choose several to match any of them.

## `propertyMinMarketValue` (type: `integer`):

Only properties assessed at or above this value.

## `propertyMaxMarketValue` (type: `integer`):

Only properties assessed at or below this value. Accepted by property search.

## `propertyMinBuildingArea` (type: `integer`):

Only properties whose building area is at least this, in the unit the provider publishes.

## `propertyMaxBuildingArea` (type: `integer`):

Only properties whose building area is at most this. Accepted by property search.

## `propertyMinLotSize` (type: `integer`):

Only properties whose lot is at least this size.

## `propertyMaxLotSize` (type: `integer`):

Only properties whose lot is at most this size. Accepted by property search.

## `propertyMinStoryCount` (type: `integer`):

Only properties with at least this many storeys.

## `propertyMinUnitCount` (type: `integer`):

Only properties with at least this many units, for finding multifamily stock.

## `propertyMaxUnitCount` (type: `integer`):

Only properties with at most this many units. Accepted by property search.

## `propertyMinYearBuilt` (type: `integer`):

Only properties built in or after this year. Accepted by property search.

## `propertyMaxYearBuilt` (type: `integer`):

Only properties built in or before this year, for finding ageing stock. Accepted by property search.

## `legalOwner` (type: `array`):

One owner name per line, for property search. Matches properties held by those named owners.

## `permitTagsUnfinaled` (type: `array`):

On property search, restrict to properties with an open, not-yet-final permit carrying these tags.

## `contractorClassification` (type: `array`):

Filter by the trade classification this API derives from a firm's work. Several values mean a firm must have all of them. Prefix with a minus to exclude.

## `contractorName` (type: `string`):

Match contractors by name.

## `contractorWebsite` (type: `string`):

Match contractors by website domain.

## `contractorLicense` (type: `string`):

Match a contractor by its licence number.

## `contractorMinTotalJobValue` (type: `integer`):

Only contractors whose permits total at least this much work, for separating real operators from one-permit firms.

## `contractorMinPermitCount` (type: `integer`):

Only contractors who have pulled at least this many permits.

## `contractorMinInspectionPassRate` (type: `integer`):

Only contractors whose inspections pass at least this percentage of the time.

## `includeTallies` (type: `boolean`):

Ask contractor search for the per-status and per-tag counts behind each firm's totals.

## `contractorIds` (type: `array`):

One contractor id per line, for the contractor lookup, permits-by-contractor and contractor metrics services.

## `permitIds` (type: `array`):

One permit id per line, for the permit lookup service.

## `addressIds` (type: `array`):

One address geo\_id per line, for the property lookup service.

## `decisionIds` (type: `array`):

One decision id per line, for the decision lookup service.

## `decisionFrom` (type: `string`):

Start of the decision date range, as YYYY-MM-DD. Required by decision search.

## `decisionTo` (type: `string`):

End of the decision date range, as YYYY-MM-DD.

## `decisionQuery` (type: `string`):

Free text matched against the decision title and description.

## `assetClass` (type: `array`):

Filter zoning and land use decisions by asset class, as the provider labels them.

## `decisionCategory` (type: `array`):

Filter decisions by category, as the provider labels them.

## `decisionSubcategory` (type: `array`):

Filter decisions by subcategory, as the provider labels them.

## `decisionMinProjectValue` (type: `integer`):

Only decisions on projects worth at least this much.

## `decisionMaxProjectValue` (type: `integer`):

Only decisions on projects worth at most this much.

## `metricFrom` (type: `string`):

Start of the metric date range, as YYYY-MM-DD. Required by the monthly metrics services.

## `metricTo` (type: `string`):

End of the metric date range, as YYYY-MM-DD.

## `metricTag` (type: `string`):

Which work type the metrics are reported for. The metrics services report per tag rather than in aggregate, so the provider requires one.

## `metricPropertyType` (type: `string`):

Which property type the metrics are reported for. Required by the city, county, jurisdiction and contractor metrics services.

## `maxResults` (type: `integer`):

Stop after this many rows. Billing here is per record on the provider's side as well as this actor's, so this cap is what keeps a wide search from spending more than you meant.

## `pageSize` (type: `integer`):

How many records to ask for per call. The provider allows up to 100 and defaults to 10.

## `requestsPerMinute` (type: `integer`):

Pacing ceiling. The provider rate limits individually rather than by a published number, so lower this if a run starts being throttled.

## `apiKey` (type: `string`):

Your own API key for the building permit data provider, from your account's API key page. This actor is bring-your-own-key and never ships a key of its own. Leave blank to use the DATA\_API\_KEY environment secret instead.

## `baseUrl` (type: `string`):

Override the API host. Only needed if your account is served from somewhere other than the public endpoint.

## Actor input object example

```json
{
  "service": "permitSearch",
  "geoType": "city",
  "permitHasContractor": false,
  "includeTallies": false,
  "maxResults": 1000,
  "pageSize": 100,
  "requestsPerMinute": 60
}
```

# Actor output Schema

## `records` (type: `string`):

One row per record, alongside the geography or id that produced it.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/building-permits-contractors-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/building-permits-contractors-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call nabeelbaghoor/building-permits-contractors-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/building-permits-contractors-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/b1rH1MpbtNOlLdyiD/builds/25dJ0SoUZoRPtlfpR/openapi.json
