# US Building Permits Scraper - Construction & Contractor Leads (`scrapesage/us-building-permits-scraper`) Actor

Scrape US building permits from official city & county open data: contractor/owner contacts, project value, description, trade (solar, roofing, HVAC, electrical, plumbing), geo & lead score. Filter by city, date, trade & value for fresh construction leads, plus monitoring. No browser.

- **URL**: https://apify.com/scrapesage/us-building-permits-scraper.md
- **Developed by:** [Scrape Sage](https://apify.com/scrapesage) (community)
- **Categories:** Lead generation, Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 permit records

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## US Building Permits Scraper — Construction & Contractor Leads (Solar, Roofing, HVAC)

Pull **fresh US building permits** from official **city & county open-data portals** and turn them into **ready-to-work construction leads** — with **contractor & owner contacts, project value, work description, trade category, geo coordinates and a 0–100 lead score**. Filter by city, date, trade and project size; schedule it to deliver **only new permits** every day.

Permits are **public government records**, so there is **no login, no API key, no browser, and no anti-bot** — just fast, clean JSON with 99%+ reliability.

### Why this building permits scraper?

A building permit is one of the strongest buying signals in construction: a homeowner or developer just got approval to spend money, and the contractor on the job is named. This actor reads each city's **authoritative open-data API**, normalizes every source into **one rich schema**, and adds the lead-gen fields most scrapers drop:

| Data | Typical scrapers | This actor |
|---|---|---|
| Contractor / builder / applicant name | partial | ✅ |
| Contractor **phone** (where the city publishes it) | ❌ | ✅ |
| Property owner name / business | ❌ | ✅ where available |
| Project / construction value (USD) | partial | ✅ |
| **Trade category** (solar, roofing, HVAC, electrical, plumbing…) | ❌ | ✅ derived |
| Work description, permit type & work class | partial | ✅ |
| Geo coordinates (lat/lng) + full address | partial | ✅ |
| **Lead score** 0–100 | ❌ | ✅ |
| Full original record (`raw`) | ❌ | ✅ every field |
| **Monitor mode** — only new permits | ❌ | ✅ |

Supported sources (more added over time, and you can add any Socrata portal yourself): **Chicago IL, New York City NY, Los Angeles CA, San Francisco CA, Austin TX, Cincinnati OH, Mesa AZ, Baton Rouge LA, Montgomery County MD, Collin County TX, Marin County CA, Corona CA, Howard County MD.**

### Use cases

- **Construction lead generation** — solar, roofing, HVAC, electrical, plumbing, remodeling, pool and demolition companies: find homeowners who just pulled a permit and the contractor named on it. Filter by `tradeCategories` and `minProjectValue`, score by `leadScore`, and reach out.
- **Sub-contractor & supplier prospecting** — building-material suppliers, equipment rental, and trade subs can target active projects by city, value and trade.
- **Proptech & real-estate intelligence** — track new construction, additions and renovations by neighborhood; enrich property records with permit activity and project value.
- **Market & competitor analysis** — see which contractors are pulling the most permits, in which cities, for what project values — a live view of construction demand.
- **Fresh daily feeds** — combine **monitor mode** with a schedule to push only newly issued permits into your CRM, sales sequence, or data warehouse.

### How to use

1. [Sign up for Apify](https://console.apify.com/sign-up) — the free plan is enough to try this actor.
2. Open the **US Building Permits Scraper**, pick the **cities** you want, and add filters (trade, date, project value).
3. Click **Start** and watch permits stream into the dataset table.
4. **Export** as JSON, CSV, Excel, or XML — or pull results programmatically via the [Apify API](https://docs.apify.com/api/v2).

### Input

```json
{
    "cities": ["austin", "chicago", "los-angeles"],
    "lastNDays": 30,
    "tradeCategories": ["solar", "roofing", "hvac"],
    "minProjectValue": 10000,
    "onlyWithContractorContact": true,
    "maxResultsPerCity": 500,
    "maxResults": 2000,
    "monitorMode": false
}
````

- **cities** — which supported city/county portals to pull from. Leave empty or include `all` for every source.
- **lastNDays** — only permits issued in the last N days (great for daily lead pulls). Ignored if `issuedAfter` is set.
- **issuedAfter / issuedBefore** — explicit issue-date window (`YYYY-MM-DD`).
- **tradeCategories** — keep only matching trades: `solar`, `roofing`, `hvac`, `electrical`, `plumbing`, `pool_spa`, `demolition`, `new_construction`, `addition_remodel`, `fence`, `sign`, `grading`.
- **keyword** — free-text search in the permit description/type (e.g. `solar pv`, `kitchen remodel`).
- **minProjectValue** — only permits at or above this reported/estimated value (USD).
- **zipCodes** — restrict to specific ZIP codes (where the source publishes a ZIP field).
- **onlyWithContractorContact** *(default false)* — keep only permits that name a contractor/builder/applicant.
- **maxResultsPerCity / maxResults** — caps; permits are returned newest-issued first.
- **monitorMode** *(default false)* — emit only permits not seen on previous runs (see below).
- **customSocrataSources** — add any other government Socrata permit portal (see Tips).

### Output

One record per permit. Every original source field is preserved under `raw`:

```json
{
    "permitId": "austin:2026-075288 BP",
    "permitNumber": "2026-075288 BP",
    "city": "Austin",
    "state": "TX",
    "country": "US",
    "permitType": "Building Permit · R- 435 Renovations/Remodel",
    "workClass": "Remodel",
    "tradeCategories": ["roofing", "addition_remodel"],
    "description": "Express: Roof replacement with new decking",
    "status": "Active",
    "dateApplied": "2026-06-02T00:00:00.000Z",
    "dateIssued": "2026-06-14T00:00:00.000Z",
    "dateExpires": null,
    "address": "8506 STILLWOOD LN",
    "addressFull": "8506 STILLWOOD LN Austin, TX, 78757",
    "addressCity": "Austin",
    "addressState": "TX",
    "zip": "78757",
    "latitude": 30.357,
    "longitude": -97.738,
    "projectValue": 18500,
    "squareFeet": null,
    "units": null,
    "contractor": {
        "role": "Contractor",
        "name": "Cole Shiflet",
        "company": "Lone Star Roofing",
        "phone": "5126628181",
        "license": null,
        "trade": "General Contractor",
        "address": "16808 Radho",
        "city": "Round Rock"
    },
    "owner": null,
    "hasContractorContact": true,
    "permitUrl": "https://abc.austintexas.gov/web/permit/public-search-other?...",
    "source": {
        "portal": "data.austintexas.gov",
        "datasetId": "3syk-w9eu",
        "datasetName": "Issued Construction Permits",
        "datasetUrl": "https://data.austintexas.gov/d/3syk-w9eu"
    },
    "leadScore": 70,
    "scrapedAt": "2026-06-16T00:00:00.000Z",
    "raw": { "...": "every original column from the source dataset" }
}
```

Fields are `null` only when the source doesn't publish them (e.g. not every city exposes contractor phone or project value), never because the scraper skipped them.

### Monitor only new permits

Turn this scraper into a continuous lead feed:

- Set **`monitorMode: true`**. The actor remembers every permit it has already returned (in a named key-value store) and emits **only permits it hasn't seen before**.
- Create an Apify **[Schedule](https://docs.apify.com/platform/schedules)** (e.g. daily). Monitor mode and the scheduler are complementary: the **schedule** controls *when* the run starts, **monitor mode** controls *what's new* — so each scheduled run delivers only fresh permits.
- Use **`monitorKey`** to keep separate seen-lists per saved search (e.g. `solar-texas` vs `roofing-california`) so different scheduled runs don't share state.

### Automate & schedule

- **[Apify API](https://docs.apify.com/api/v2)** — start runs, fetch datasets, manage schedules over REST.
- **[apify-client for JavaScript](https://docs.apify.com/api/client/js/)** and **[Python](https://docs.apify.com/api/client/python/)** — official SDKs.
- **[Schedules](https://docs.apify.com/platform/schedules)** — run it hourly/daily/weekly for a fresh construction-lead pipeline.
- **[Webhooks](https://docs.apify.com/platform/integrations/webhooks)** — trigger CRM imports, Slack alerts, or email sequences the moment a run finishes.

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

const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });

const run = await client.actor('scrapesage/us-building-permits-scraper').call({
    cities: ['austin', 'mesa', 'marin-county'],
    tradeCategories: ['solar', 'roofing', 'hvac'],
    lastNDays: 7,
    onlyWithContractorContact: true,
    monitorMode: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Got ${items.length} fresh construction leads`);
```

### Integrate with any app

Connect the dataset to 5,000+ apps — no code required:

- **[Make](https://docs.apify.com/platform/integrations/make)** — multi-step automation scenarios.
- **[Zapier](https://docs.apify.com/platform/integrations/zapier)** — push new permits straight into your CRM.
- **[Slack](https://docs.apify.com/platform/integrations/slack)** — get pinged when a monitored search finds new permits.
- **[Google Drive / Sheets](https://docs.apify.com/platform/integrations/drive)** — auto-export every run to a spreadsheet.
- **[Airbyte](https://docs.apify.com/platform/integrations/airbyte)** — pipe results into your data warehouse.
- **[GitHub](https://docs.apify.com/platform/integrations/github)** — trigger runs from commits or releases.

### Use with AI assistants (MCP)

The output is clean, LLM-ready JSON. Call this actor from Claude, ChatGPT, or any agent through the **[Apify MCP server](https://docs.apify.com/platform/integrations/mcp)** — ask your assistant to "find solar permits issued in Austin this week with a contractor contact" and let it run this scraper for you.

### Agent-ready: autonomous payments (x402 & Skyfire)

This actor is **agent-ready** — AI agents can discover it, run it, and **pay for it autonomously**, with no Apify account and no human in the loop. It uses [pay-per-event](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event) pricing and [limited permissions](https://docs.apify.com/platform/actors/development/permissions), so it qualifies for Apify's agentic-payment standards:

- **[x402](https://docs.apify.com/platform/integrations/x402)** — an open, HTTP-native payment protocol. Agents pay per run in USDC on the Base network directly through the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp) — no account, no API key.
- **[Skyfire](https://docs.apify.com/platform/integrations/skyfire)** — agent-to-service payments for fully autonomous AI-agent workflows.

Building an AI agent, MCP tool, or autonomous data pipeline? This scraper is ready to plug in and pay as it goes.

### More scrapers from scrapesage

Build a full **B2B lead-gen and market-intelligence stack**:

- **[FMCSA Trucking Scraper](https://apify.com/scrapesage/fmcsa-trucking-scraper)** — US motor carriers with phone, email, fleet & authority leads.
- **[US Business Formation Scraper](https://apify.com/scrapesage/us-business-formation-scraper)** — newly registered LLCs & companies as fresh B2B leads.
- **[Hipages Scraper](https://apify.com/scrapesage/hipages-scraper)** — Australian tradie & home-services business leads.
- **[Houzz Scraper](https://apify.com/scrapesage/houzz-scraper)** — home-improvement pros, contacts & reviews.
- **[Rent.com Scraper](https://apify.com/scrapesage/rent-com-scraper)** — apartments, rentals & property-management leads.
- **[FastExpert Scraper](https://apify.com/scrapesage/fastexpert-agent-scraper)** — real-estate agent leads, stats & reviews.
- **[Redfin Scraper](https://apify.com/scrapesage/redfin-scraper)** — listings, sold comps & home estimates.
- **[USAspending Scraper](https://apify.com/scrapesage/usaspending-scraper)** — federal awards & contractor leads.

### Tips

- **Add any city.** Most US cities publish permits on a Socrata portal. Find the dataset, then add it via `customSocrataSources`: `{ "domain": "data.x.gov", "datasetId": "abcd-1234", "dateField": "issued_date", "city": "Springfield", "state": "IL", "descField": "description", "valueField": "valuation", "addressField": "address", "zipField": "zip" }`. Only `domain`, `datasetId` and `dateField` are required.
- **Daily lead feed.** Use `monitorMode: true` + `lastNDays: 2` + a daily [Schedule](https://docs.apify.com/platform/schedules) to get just-issued permits each morning.
- **High-value only.** Set `minProjectValue` (e.g. `50000`) and sort/filter by `leadScore` to focus on big jobs.
- **Cost control.** Caps (`maxResults`, `maxResultsPerCity`) and the trade/date filters keep runs small and cheap — these are government APIs, so there are no proxy costs.

### FAQ

**Where does the data come from?** Each city/county's official open-data portal (Socrata) — the same public records anyone can browse, served as structured JSON.

**Do I need an API key or proxy?** No. These are public government APIs. A proxy is optional and only helps avoid per-IP rate limits on very large pulls.

**Which cities are supported?** Chicago, New York City, Los Angeles, San Francisco, Austin, Cincinnati, Mesa, Baton Rouge, Montgomery County (MD), Collin County (TX), Marin County (CA), Corona (CA) and Howard County (MD) — and you can add any other Socrata permit portal yourself.

**Does every permit include a contractor phone?** No — only where the city publishes it (e.g. Austin). Others provide the contractor/builder/applicant name, the owner, or both. Use `onlyWithContractorContact` to keep only permits that name someone.

**How do I get only new permits?** Turn on `monitorMode` and schedule the actor. Each run returns only permits not seen before.

**Can I export to Google Sheets, CSV, or Excel?** Yes — one click in the dataset view, or automatically on every run via the [Google Drive integration](https://docs.apify.com/platform/integrations/drive).

**Is this legal?** Building permits are public records. You're responsible for using the data in compliance with applicable laws (e.g. CCPA/GDPR for personal data) and each portal's terms of use.

### Need help?

Open an issue on the actor's **Issues** tab, or visit the [Apify help center](https://help.apify.com/). Feature requests — including new cities — are welcome; this actor is actively maintained.

# Actor input Schema

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

Which US city/county open-data portals to pull permits from. Leave empty (or include <code>all</code>) to pull from every supported source. Add more portals with <b>Custom Socrata sources</b> below.

## `lastNDays` (type: `integer`):

Convenience filter — only permits issued in the last N days. Ignored if <b>Issued after</b> is set. Great for daily lead pulls (e.g. <code>7</code> or <code>30</code>).

## `issuedAfter` (type: `string`):

Only permits issued on or after this date (YYYY-MM-DD).

## `issuedBefore` (type: `string`):

Only permits issued on or before this date (YYYY-MM-DD).

## `tradeCategories` (type: `array`):

Only permits matching these trades (derived from permit type + work class + description). Perfect for solar, roofing, HVAC, electrical or plumbing sales teams.

## `keyword` (type: `string`):

Free-text search within the permit description / type (e.g. <code>swimming pool</code>, <code>solar pv</code>, <code>kitchen remodel</code>).

## `minProjectValue` (type: `integer`):

Only permits with a reported/estimated project value at or above this amount. Filters out small jobs and surfaces high-value leads.

## `zipCodes` (type: `array`):

Only permits in these ZIP codes (applied where the source publishes a ZIP field).

## `onlyWithContractorContact` (type: `boolean`):

Keep only permits that include a contractor, builder or applicant name — i.e. ready-to-contact construction leads.

## `maxResultsPerCity` (type: `integer`):

Cap permits returned from each source. Permits are returned newest-issued first.

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

Overall cap on permits across all selected sources.

## `monitorMode` (type: `boolean`):

Remembers every permit it has already returned (in a named key-value store) and emits only permits it has not seen before. Schedule this actor (e.g. daily) and each run delivers only fresh permits — ideal for a continuous lead feed. Works alongside Apify <a href="https://docs.apify.com/platform/schedules">Schedules</a>.

## `monitorStoreName` (type: `string`):

Name of the key-value store that holds seen-permit state (lowercase, digits and hyphens only).

## `monitorKey` (type: `string`):

Optional. Keeps separate monitor state per saved search (e.g. <code>solar-texas</code>) so different scheduled runs don't share a seen-list.

## `customSocrataSources` (type: `array`):

Add any other government Socrata permit portal. Each item: <code>{ "domain": "data.x.gov", "datasetId": "abcd-1234", "dateField": "issued\_date", "city": "Springfield", "state": "IL", "permitNumberField": "permit\_number", "descField": "description", "valueField": "valuation", "addressField": "address", "zipField": "zip" }</code>. Only domain, datasetId and dateField are required.

## `socrataAppToken` (type: `string`):

Optional Socrata app token for higher rate limits on very large pulls. Not required — these portals work anonymously.

## `proxyConfiguration` (type: `object`):

Optional. Government open-data portals work without proxies, but proxy rotation helps avoid per-IP rate limits on very large pulls.

## Actor input object example

```json
{
  "cities": [
    "austin",
    "chicago",
    "los-angeles",
    "new-york"
  ],
  "onlyWithContractorContact": false,
  "maxResultsPerCity": 500,
  "maxResults": 1000,
  "monitorMode": false,
  "monitorStoreName": "building-permits-monitor",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped permit records as JSON items in the default dataset.

# 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 = {
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapesage/us-building-permits-scraper").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 = { "proxyConfiguration": { "useApifyProxy": True } }

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

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

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

```

## CLI example

```bash
echo '{
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapesage/us-building-permits-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=scrapesage/us-building-permits-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "US Building Permits Scraper - Construction & Contractor Leads",
        "description": "Scrape US building permits from official city & county open data: contractor/owner contacts, project value, description, trade (solar, roofing, HVAC, electrical, plumbing), geo & lead score. Filter by city, date, trade & value for fresh construction leads, plus monitoring. No browser.",
        "version": "0.1",
        "x-build-id": "B3yUavWWqTevejwgc"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/scrapesage~us-building-permits-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-scrapesage-us-building-permits-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/scrapesage~us-building-permits-scraper/runs": {
            "post": {
                "operationId": "runs-sync-scrapesage-us-building-permits-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/scrapesage~us-building-permits-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-scrapesage-us-building-permits-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "cities": {
                        "title": "Cities / counties",
                        "type": "array",
                        "description": "Which US city/county open-data portals to pull permits from. Leave empty (or include <code>all</code>) to pull from every supported source. Add more portals with <b>Custom Socrata sources</b> below.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "all",
                                "chicago",
                                "new-york",
                                "los-angeles",
                                "san-francisco",
                                "austin",
                                "cincinnati",
                                "mesa",
                                "baton-rouge",
                                "montgomery-county",
                                "collin-county",
                                "marin-county",
                                "corona",
                                "howard-county"
                            ],
                            "enumTitles": [
                                "All supported sources",
                                "Chicago, IL",
                                "New York City, NY",
                                "Los Angeles, CA",
                                "San Francisco, CA",
                                "Austin, TX",
                                "Cincinnati, OH",
                                "Mesa, AZ",
                                "Baton Rouge, LA",
                                "Montgomery County, MD",
                                "Collin County, TX",
                                "Marin County, CA",
                                "Corona, CA",
                                "Howard County, MD"
                            ]
                        },
                        "default": [
                            "austin",
                            "chicago",
                            "los-angeles",
                            "new-york"
                        ]
                    },
                    "lastNDays": {
                        "title": "Last N days (fresh permits)",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Convenience filter — only permits issued in the last N days. Ignored if <b>Issued after</b> is set. Great for daily lead pulls (e.g. <code>7</code> or <code>30</code>)."
                    },
                    "issuedAfter": {
                        "title": "Issued after",
                        "type": "string",
                        "description": "Only permits issued on or after this date (YYYY-MM-DD)."
                    },
                    "issuedBefore": {
                        "title": "Issued before",
                        "type": "string",
                        "description": "Only permits issued on or before this date (YYYY-MM-DD)."
                    },
                    "tradeCategories": {
                        "title": "Trade categories",
                        "type": "array",
                        "description": "Only permits matching these trades (derived from permit type + work class + description). Perfect for solar, roofing, HVAC, electrical or plumbing sales teams.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "solar",
                                "roofing",
                                "hvac",
                                "electrical",
                                "plumbing",
                                "pool_spa",
                                "demolition",
                                "new_construction",
                                "addition_remodel",
                                "fence",
                                "sign",
                                "grading"
                            ],
                            "enumTitles": [
                                "Solar / battery",
                                "Roofing",
                                "HVAC / mechanical",
                                "Electrical",
                                "Plumbing",
                                "Pool & spa",
                                "Demolition",
                                "New construction",
                                "Addition / remodel",
                                "Fence",
                                "Sign",
                                "Grading / site work"
                            ]
                        }
                    },
                    "keyword": {
                        "title": "Keyword",
                        "type": "string",
                        "description": "Free-text search within the permit description / type (e.g. <code>swimming pool</code>, <code>solar pv</code>, <code>kitchen remodel</code>)."
                    },
                    "minProjectValue": {
                        "title": "Minimum project value (USD)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Only permits with a reported/estimated project value at or above this amount. Filters out small jobs and surfaces high-value leads."
                    },
                    "zipCodes": {
                        "title": "ZIP codes",
                        "type": "array",
                        "description": "Only permits in these ZIP codes (applied where the source publishes a ZIP field).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "onlyWithContractorContact": {
                        "title": "Only permits with a contractor/applicant contact",
                        "type": "boolean",
                        "description": "Keep only permits that include a contractor, builder or applicant name — i.e. ready-to-contact construction leads.",
                        "default": false
                    },
                    "maxResultsPerCity": {
                        "title": "Max results per city",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Cap permits returned from each source. Permits are returned newest-issued first.",
                        "default": 500
                    },
                    "maxResults": {
                        "title": "Max results (total)",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Overall cap on permits across all selected sources.",
                        "default": 1000
                    },
                    "monitorMode": {
                        "title": "Monitor mode (only new permits)",
                        "type": "boolean",
                        "description": "Remembers every permit it has already returned (in a named key-value store) and emits only permits it has not seen before. Schedule this actor (e.g. daily) and each run delivers only fresh permits — ideal for a continuous lead feed. Works alongside Apify <a href=\"https://docs.apify.com/platform/schedules\">Schedules</a>.",
                        "default": false
                    },
                    "monitorStoreName": {
                        "title": "Monitor store name",
                        "type": "string",
                        "description": "Name of the key-value store that holds seen-permit state (lowercase, digits and hyphens only).",
                        "default": "building-permits-monitor"
                    },
                    "monitorKey": {
                        "title": "Monitor key (saved-search namespace)",
                        "type": "string",
                        "description": "Optional. Keeps separate monitor state per saved search (e.g. <code>solar-texas</code>) so different scheduled runs don't share a seen-list."
                    },
                    "customSocrataSources": {
                        "title": "Custom Socrata sources (advanced)",
                        "type": "array",
                        "description": "Add any other government Socrata permit portal. Each item: <code>{ \"domain\": \"data.x.gov\", \"datasetId\": \"abcd-1234\", \"dateField\": \"issued_date\", \"city\": \"Springfield\", \"state\": \"IL\", \"permitNumberField\": \"permit_number\", \"descField\": \"description\", \"valueField\": \"valuation\", \"addressField\": \"address\", \"zipField\": \"zip\" }</code>. Only domain, datasetId and dateField are required."
                    },
                    "socrataAppToken": {
                        "title": "Socrata app token (optional)",
                        "type": "string",
                        "description": "Optional Socrata app token for higher rate limits on very large pulls. Not required — these portals work anonymously."
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Optional. Government open-data portals work without proxies, but proxy rotation helps avoid per-IP rate limits on very large pulls.",
                        "default": {
                            "useApifyProxy": true
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
