# Building Permits Pro API - Scored Leads | Johto Data (`johto.data/building-permits-pro`) Actor

Building permits from 8 US city permit portals, newest first, deduplicated and classified: work category, trade tags, property type, valuation band, normalized address and contractor, lead score 0-100. Per-city retries and a run report.

- **URL**: https://apify.com/johto.data/building-permits-pro.md
- **Developed by:** [Jo](https://apify.com/johto.data) (community)
- **Categories:** Lead generation, Real estate, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 Permits Pro API: Scored Leads | Johto Data

### For AI agents

- **What it returns:** one row per building permit from 8 US city permit portals, newest first, deduplicated, with a lead score from 0 to 100.
- **Cities:** Austin TX, Seattle WA, San Francisco CA, New York NY, Chicago IL, Cincinnati OH, Baton Rouge LA, Marin County CA.
- **Call:** `{"cities":["austin","seattle"],"daysBack":7,"maxItems":25,"minLeadScore":50}`
- **Key fields:** `issueDate`, `daysSinceIssued`, `workCategory`, `tradeTags`, `propertyType`, `valuation`, `valuationBand`, `fullAddress`, `zip`, `lat`, `lon`, `contractorName`, `leadScore`, `sourceUrl`.
- **Price:** pay per delivered permit (see the Pricing tab). Status and error rows are free.
- **Run report:** the `RUN_REPORT` record lists rows read, delivered and duplicates removed per city, plus HTTP retries.

### Why Pro

This is the upgraded tier of our standard building permits Actor. We tested both on the same input: 8 cities, 14 days back, 25 permits per city, on Oct 22, 2026.

| Measured | Standard | Pro |
|---|---|---|
| Median permit age delivered | 9 days | 2 days |
| Duplicate permit ids in output | 7 | 0 |
| Fields per row | 22 | 30 |
| Work category and trade tags | no | yes |
| Lead score 0 to 100 | no | yes |
| Retries | 4, linear delay, any error | 5, exponential backoff, only 429 and 5xx |
| One city failing | fails the whole run | other cities continue, free error row |
| Runtime | about 7 s | about 6 s |

### Filters

- `cities`, `daysBack`, `maxItems` (per city)
- `keywords` (description and type)
- `minValuation`
- `workCategories`: new_construction, addition, remodel, repair, demolition, roofing, solar, pool, adu, sign, fence, trade_work, other, or a trade tag such as electrical, plumbing, hvac
- `propertyTypes`: residential, commercial, unknown
- `minLeadScore`
- `requireContractor`
- `onlyNew`: return only permits you have not received before, so you pay for new rows only

### Lead score

- Recency: up to 40 points (2 points lost per day since issue).
- Job value: up to 35 points.
- A named contractor company: 15 points.
- A geocoded address: 10 points.

### Output example

```json
{"id":"austin:2026-123456 BP","cityName":"Austin","state":"TX","permitNumber":"2026-123456 BP","issueDate":"2026-10-21","daysSinceIssued":1,"workCategory":"remodel","tradeTags":["electrical","plumbing"],"propertyType":"residential","valuation":85000,"valuationBand":"50k-250k","fullAddress":"123 EXAMPLE ST, Austin, TX 78704","contractorName":"Example Builders LLC","leadScore":82}
```

The example values are illustrative.

### Notes

- Person names (owners, applicants, individual contractors) are removed. Only company names are kept.
- NYC rows carry the attribution NYC requires. Marin rows carry ODbL attribution. Chicago rows carry the City of Chicago disclaimer.
- Coverage depends on each city portal. Fields a city does not publish are null.

### Support

Open an issue on the Issues tab. Johto Data replies within one business day.

# Actor input Schema

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

City slugs to pull: austin, seattle, san-francisco, cincinnati, baton-rouge, marin-county, new-york, chicago.

## `daysBack` (type: `integer`):

Only permits issued within this many days.

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

Match in permit type or work description, e.g. \["solar","roof","pool","ADU","demolition"]. Empty = all permits.

## `minValuation` (type: `integer`):

Skip permits below this declared job value (where the city reports one).

## `maxItems` (type: `integer`):

Maximum permits per city, newest first. Each delivered permit is one charged result. Status and error rows are free.

## `onlyNew` (type: `boolean`):

Remember permits across runs (per account) and return only permits not delivered before. You pay only for new permits.

## `workCategories` (type: `array`):

Keep only these categories or trade tags. Categories: new_construction, addition, remodel, repair, demolition, roofing, solar, pool, adu, sign, fence, trade_work, other. Trade tags: electrical, plumbing, hvac, solar, roofing, fire, windows_doors, foundation.

## `propertyTypes` (type: `array`):

Keep only residential, commercial or unknown. Empty keeps all.

## `minLeadScore` (type: `integer`):

Lead score = recency (up to 40) + job value (up to 35) + named contractor (15) + geocoded (10).

## `requireContractor` (type: `boolean`):

Keep only permits where the city lists a contractor company (Austin, Seattle, Cincinnati, Baton Rouge).

## Actor input object example

```json
{
  "cities": [
    "austin"
  ],
  "daysBack": 14,
  "keywords": [
    "solar"
  ],
  "minValuation": 0,
  "maxItems": 25,
  "onlyNew": false,
  "workCategories": [],
  "propertyTypes": [],
  "minLeadScore": 0,
  "requireContractor": false
}
```

# Actor output Schema

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

No description

## `runReport` (type: `string`):

No description

# 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": [
        "austin"
    ],
    "keywords": [
        "solar"
    ],
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("johto.data/building-permits-pro").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": ["austin"],
    "keywords": ["solar"],
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("johto.data/building-permits-pro").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": [
    "austin"
  ],
  "keywords": [
    "solar"
  ],
  "maxItems": 25
}' |
apify call johto.data/building-permits-pro --silent --output-dataset

```

## MCP server setup

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

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/H2mdMHwfde36g5bVb/builds/oR8dLRXZeW1HuyTMl/openapi.json
