# US Building Permits Scraper: Contractors & Project Costs (`s-r/us-building-permits`) Actor

Search official US city and county building permits. Normalize project costs, issue and application dates, addresses, contractor licenses and public owner details for roofing, solar, HVAC and property research.

- **URL**: https://apify.com/s-r/us-building-permits.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Real estate, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 building permits

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

## US Building Permits Scraper

US Building Permits Scraper is a building permits scraper that collects official city and county permit records in one consistent dataset. Search for roofing, solar, HVAC, renovations or other construction work, then export project descriptions, declared costs, property addresses and public contractor details. Each result represents one permit from one jurisdiction, with the original permit number retained for verification and future updates.

This Actor helps contractors identify relevant projects, suppliers understand local construction activity, and property researchers connect permitted work to a property. It uses the terminology published by each jurisdiction. A permit is evidence of a recorded application or authorization, and does not establish that construction has started or that a contractor is seeking bids.

### US building permits scraper coverage

Choose jurisdictions using the identifiers below. The order matters: the Actor retrieves permits from the first jurisdiction before continuing to the next, until the global maximum is reached. A busy first city can fill the entire request. Run separate searches when you need a fixed allocation for each city.

| Identifier | Official jurisdiction | Coverage notes |
|---|---|---|
| `new_york` | New York City | DOB NOW approved permits; contractor and owner details; no application date |
| `chicago` | Chicago | Building permits and public contact roles; no property ZIP code |
| `los_angeles` | Los Angeles | Issued permits since 2020; no contractor or owner fields |
| `miami_dade` | Miami-Dade County | Contractor, license and owner details; no property ZIP or coordinates |
| `miami` | City of Miami | Contractor company and project value; no property ZIP |
| `nashville` | Nashville | Project purpose, cost and public contact; contact role is not inferred |
| `columbus` | Columbus | Permit classifications and applicant company; no work description or application date |
| `raleigh` | Raleigh | Contractor, published license, owner and address fields |
| `detroit` | Detroit | Permit and property details; no contractor information |
| `san_francisco` | San Francisco | Building permits, descriptions and declared values |
| `seattle` | Seattle | Building permits, application dates and contractor company where published |
| `austin` | Austin | Construction permits, contractor and applicant fields |
| `baton_rouge` | Baton Rouge | Building permits with contractor, applicant and owner names |
| `cincinnati` | Cincinnati | Building permits, descriptions and contractor company |
| `new_orleans` | New Orleans | Permits with public contractor, applicant and owner text |
| `montgomery_county_md` | Montgomery County, Maryland | Residential permits; commercial permits are outside this release |

Dallas and NYC BIS legacy permits are outside this release. Available date ranges and update schedules depend on the official dataset. Missing fields are returned as null, and no contact information is inferred from unrelated records.

### Input

`cities` is the list of jurisdiction identifiers. `date_field` selects `issued` or `applied`; the default is issue date. `date_from` and `date_to` accept inclusive calendar dates in `YYYY-MM-DD` form. Without dates, the search covers the preceding 30 days through the request date. A jurisdiction without application dates produces a warning when you request `applied`.

`keywords` matches any supplied term as a case insensitive substring in the work description. For example, `roof` also matches `roofing`. It is a text filter, rather than a verified trade classification. Columbus does not publish a free text work description, so keyword searches report that jurisdiction as unsupported. `permit_types` matches any supplied term across source permit and work type fields. Keyword and type filters are combined with the date and cost requirements.

`min_cost` is an inclusive minimum declared project value in US dollars. Permits without a published value cannot satisfy this filter. `max_items` is the total maximum across all selected jurisdictions, from 1 to 10,000, and defaults to 100. `include_raw` attaches the original source attributes for auditing. The Actor follows continuation pages automatically; API integrations can use `cursor` with the original input unchanged.

The following input was used to retrieve real Chicago roofing permits in October 2026:

```json
{
  "cities": ["chicago"],
  "date_from": "2026-10-01",
  "date_to": "2026-10-30",
  "keywords": ["roof"],
  "max_items": 10
}
```

### Output

Download results from the default dataset as JSON, CSV or Excel. Core fields include `permit_id`, `jurisdiction`, `status`, `permit_type`, `description`, `issued_at`, `applied_at`, `cost`, `currency`, `address`, `street`, `city`, `state`, `postal_code`, `lat`, `lng` and `property_id`. Contractor, applicant and owner information use flat fields such as `contractor_name`, `contractor_license`, `applicant_name` and `owner_name`.

`jurisdiction` identifies the authority that published the record. `city` identifies the address locality; these can differ for county datasets. New York uses the published borough as the locality. Shared address and coordinate field names make the results suitable for matching with property listings from Zillow, Redfin and Realtor.com. An address match still requires your own normalization and validation.

For example, Chicago permit `101085459`, issued on `2026-10-29`, describes a new residence and garage with a roof deck at `1965 W BERWYN AVE, Chicago, IL`, with a declared value of 350,000 USD. Its general contractor is `RESTORE PROS CORP`. Other public contact roles remain available in `contacts`, so an electrical contractor is not mistaken for the general contractor.

Every row includes `source`, `source_dataset`, `source_url` and `fetched_at`. Costs of zero or one dollar remain as published. Dates preserve the source calendar date. Warnings identify jurisdictions that returned no matches, lack a requested filter, or could not be served. A partial response retains usable results from other jurisdictions.

### Pricing

Billing is per delivered permit. The current price appears in the Actor pricing tab. There is no custom run start charge. Searches that deliver no permits have no permit event charge. Users on the free plan receive at most 10 permits per run; these delivered permits still count toward usage. Paying users can request the full maximum. Duplicate permits within one collection are identified by jurisdiction and permit number.

### FAQ

#### Why is the contractor, ZIP code or owner missing?

Each authority publishes different fields. Los Angeles does not expose contractor information in this dataset, while Chicago does not publish a property ZIP code. Missing values remain null. Contractor mailing addresses are never substituted for the permitted property's location.

#### Can I use this to find roofing, solar or HVAC projects?

Yes. Supply the relevant description terms through `keywords`, optionally alongside permit type and cost filters. Results reflect the authority's text, so abbreviations and local terminology can affect recall. A keyword match does not confirm sales intent or project timing.

#### Does max_items apply to every city separately?

It is a global total, processed in the order in `cities`. Request separate runs for independent city quotas. The Actor may deliver fewer rows when filters are selective or a source is unavailable, and reports source warnings alongside the results.

#### Can I reproduce and audit a record?

Use the retained permit number, dataset reference and public query URL. Enable `include_raw` to retain the published attributes. Official datasets can be updated or corrected after collection, so save `fetched_at` with your exports when comparing successive runs.

# Actor input Schema

## `cities` (type: `array`):

Jurisdictions in delivery order. Montgomery County covers residential permits. Dallas is not supported.

## `date_field` (type: `string`):

Choose permit issue date or application date. Unsupported application dates are reported as warnings.

## `date_from` (type: `string`):

Inclusive YYYY-MM-DD. Defaults to 30 days before the request date.

## `date_to` (type: `string`):

Inclusive YYYY-MM-DD. Defaults to the request date.

## `keywords` (type: `array`):

Case insensitive OR substrings in the work description. Supports letters, digits, spaces, apostrophes, -, / and &.

## `permit_types` (type: `array`):

Case insensitive OR substrings in source permit and work type fields. Combined with other filters using AND.

## `min_cost` (type: `integer`):

Minimum declared project cost in USD, inclusive. Records with no cost are excluded.

## `max_items` (type: `integer`):

Global maximum across all jurisdictions and continuation pages, in the order requested.

## `include_raw` (type: `boolean`):

Attach the original permit attributes as raw. Missing source values stay missing.

## `cursor` (type: `string`):

Opaque continuation token. Reuse the original input unchanged; tokens expire after 24 hours.

## Actor input object example

```json
{
  "cities": [
    "new_york"
  ],
  "date_field": "issued",
  "date_from": "2026-10-01",
  "date_to": "2026-10-30",
  "keywords": [
    "roof"
  ],
  "min_cost": 10000,
  "max_items": 100,
  "include_raw": false
}
```

# Actor output Schema

## `permits` (type: `string`):

One row per official building permit.

# 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 = {
    "cities": [
        "new_york"
    ],
    "date_from": "2026-10-01",
    "date_to": "2026-10-30",
    "keywords": [
        "roof"
    ],
    "min_cost": 10000
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/us-building-permits").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 = {
    "cities": ["new_york"],
    "date_from": "2026-10-01",
    "date_to": "2026-10-30",
    "keywords": ["roof"],
    "min_cost": 10000,
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/us-building-permits").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 '{
  "cities": [
    "new_york"
  ],
  "date_from": "2026-10-01",
  "date_to": "2026-10-30",
  "keywords": [
    "roof"
  ],
  "min_cost": 10000
}' |
apify call s-r/us-building-permits --silent --output-dataset

```

## MCP server setup

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

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/HsE0jihMimDNyybAR/builds/0gPE2LXrFnKu4sXuy/openapi.json
