# Chicago Building Permits API – Contractor & Construction Leads (`implementable_antler/chicago-permits`) Actor

Chicago building permits API: construction leads with every contractor by role, valuation, coordinates and trade tags (HVAC, electrical, plumbing, roofing). Permit data CSV/JSON. No homeowner names.

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

## Pricing

from $2.50 / 1,000 results

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

## Chicago Building Permits API – Contractor & Construction Leads

City of Chicago issued building permits from the official Chicago Data Portal (dataset ydr8-5enu): address, coordinates, valuation, every **contractor by role** (general, electrical, plumbing, ventilation/refrigeration, mason…), and sub-trade tags with provenance. HVAC, electrical and plumbing tags also come from the listed contractor roles. Owner records are excluded, and permits where the owner acts as their own contractor ("OWNER AS GENERAL CONTRACTOR", "OWNER ACTING AS OWN PLUMBER") are skipped entirely by default (`excludeOwnerBuilders`), because they are homeowner jobs, not contractor leads.

**Use it for:** contractor leads and construction leads (new jobs by trade), a building permits API for your CRM, or a permit data CSV/Excel export for market research. Runs on a schedule; export CSV, Excel, JSON or call the API.

### Sample rows (table)

| issueDate | address | workType | contractorName | valuation | trades |
|---|---|---|---|---|---|
| 2026-10-18 | 5901 W BERENICE AVE, CHICAGO, IL | Renovation/Alteration | HAZI ENTERPRISE LLC | $200,000 | HVAC, electrical, masonry/facade, plumbi |
| 2026-10-18 | 3402 S LOWE AVE, CHICAGO, IL | Detached Frame Garage | KUO CONSTRUCTION, INC. | $10,000 | electrical, framing, roofing |
| 2026-10-16 | 732 S KENNETH AVE, CHICAGO, IL | Renovation/Alteration | ROSS HAITH | $10,000 | HVAC, electrical, plumbing, concrete, wi |
| 2026-10-16 | 1737 N SAWYER AVE, CHICAGO, IL | Renovation/Alteration | DOLGIN CONSTRUCTION, INC. | $150,000 | HVAC, electrical, plumbing, demolition,  |
| 2026-10-16 | 2309 S LOOMIS ST, CHICAGO, IL | Renovation/Alteration | SACHSE CONSTRUCTION AND DEVELOPMENT COMP | $2,490,632 | HVAC, electrical, masonry/facade, plumbi |

### Quick start

Example input (also prefilled in the input form):

```json
{
  "days": 7,
  "trades": [
    "HVAC",
    "roofing"
  ],
  "excludeRenewals": true,
  "maxItems": 100,
  "explicitTradesOnly": true
}
```

### Input

| Field | Description |
|---|---|
| `days` | How many days back from today to include, by permit issue date (1-366). |
| `trades` | Only return permits tagged with at least one of these trades. Leave empty for all trades. |
| `explicitTradesOnly` | When filtering by trade, match only trades stated by the permit type, a listed contractor role or the description, and ignore 'implied' tags (e.g. roofing implied by a new house). On by default for lead generation; turn off to include implied new-build trades. |
| `excludeRenewals` | Skip permit renewals/reinstatements (often old jobs re-issued). Recommended for lead generation. |
| `excludeOwnerBuilders` | Skip permits where the owner acts as their own contractor ("OWNER AS GENERAL CONTRACTOR", "OWNER ACTING AS OWN PLUMBER"). Default on. |
| `dateFrom` | Earliest issue date, YYYY-MM-DD. When set, overrides 'Days back'. |
| `dateTo` | Latest issue date, YYYY-MM-DD. Defaults to today. |
| `minValuation` | Only permits with a declared valuation at or above this amount. Rows with no or zero valuation are dropped when this is set. |
| `excludeNonBuilding` | Skip administrative and non-building records (administrative changes, reinstatement-only and similar no-work records). |
| `maxItems` | Maximum number of records to return (1-50,000). |

### Fields

Every Ledger permit Actor (Deschutes/Bend, Chicago, NYC) outputs the same fields:

| Field | Meaning |
|---|---|
| `recordId` | Unique row key. One job can produce several rows (NYC: one per work type; Bend: one per sub-permit such as building, electrical, plumbing, mechanical). Dedupe on `permitID` if you want one row per permit. |
| `permitID` | The agency's permit/application number. |
| `jurisdiction` | City or county that issued the permit. |
| `address` | Site address as published. |
| `parcel` | Tax lot / PIN / BBL (see completeness). |
| `latitude`, `longitude` | WGS84 coordinates where the source publishes them (Chicago, NYC); null for Deschutes/Bend. |
| `workType` | The agency's work/permit-type label (e.g. Plumbing, Electrical, New - Single Family Dwelling). |
| `permitType`, `permitSubtype` | The agency's broader permit category and subtype (see "Source mapping"). |
| `filingReason`, `isRenewal` | Why it was filed; `true` for renewals/reinstatements (usually old jobs). |
| `description` | Free-text scope of work. |
| `status` | Permit status as published. A value ending in "(inferred: …)" means the source left it blank and we derived it. |
| `contractorName` | The agency's main contractor field (general contractor where known). Never a record the agency labels as owner. |
| `contractors` | All contractor roles listed on the permit, `[{role, name}]`. |
| `contractorLicense` | `{type, number}` where published (NYC). |
| `valuation` | Declared job value in USD (null if not published). |
| `squareFeet` | Square footage where published. |
| `applicationDate`, `approvedDate`, `issueDate`, `expirationDate`, `finalDate` | ISO dates (YYYY-MM-DD); null when the source doesn't publish that date. |
| `trades` | All sub-trades the job likely needs, including implied ones. |
| `explicitTrades` | Only trades stated by the permit type, a listed contractor role, or the description. |
| `tradeDetails` | `[{trade, source, confidence}]`: source `permitType`/`contractorRole` (high), `keyword` (medium), `implied` (low, e.g. a new house implies roofing). |
| `sourceUrl` | Link to the record (or the official export containing it) at the source. |
| `source` | Publisher and dataset name. |

### Source mapping

| Source field | Ledger field |
|---|---|
| work_type (falls back to permit_type when blank, which is most non-express permits) | `workType` |
| permit_type / review_type / permit_milestone | `permitType` / `permitSubtype` / `filingReason` |
| permit_status; when blank (about 8% of records) we output `ISSUED (inferred: source status blank, issue date present)` | `status` |
| contact_N_type/name where the role is a CONTRACTOR and not OWNER | `contractors`, `contractorName` (GC first) |
| reported_cost, pin_list, latitude, longitude, application_start_date, issue_date | `valuation`, `parcel`, `latitude`, `longitude`, `applicationDate`, `issueDate` |
Reinstated permits (`PERMIT - REINSTATE REVOKED PMT`) are flagged `isRenewal` and excluded by default. `minValuation` and the date range are applied at the source.

### Completeness

Measured on all 2,694 records issued 2026-09-20 to 2026-10-18 (default filters):

| Contractor name | Valuation > 0 | Parcel (PIN) | Lat/lon | Status inferred |
|---|---|---|---|---|
| 98% | 87% | 80% | 98% | 8% |

### Sample output

(Generated 2026-10-19 by the current version.)

```json
{
  "recordId": "3453472",
  "permitID": "101085337",
  "jurisdiction": "Chicago",
  "address": "5901 W BERENICE AVE, CHICAGO, IL",
  "latitude": 41.95023073559158,
  "longitude": -87.77435488410342,
  "workType": "Renovation/Alteration",
  "permitType": "PERMIT - RENOVATION/ALTERATION",
  "isRenewal": false,
  "description": "SPR 2019 CBCERECT REAR 2 STORY FRAME ADDITION, INTERIOR REHABILITATION AND NEW REAR OPEN WOOD DECK AT EXISTING 2 STORY WITH BASEMENT SFR AS PER PLAN.",
  "status": "ACTIVE",
  "contractorName": "HAZI ENTERPRISE LLC",
  "contractors": [
    {
      "role": "CONTRACTOR-ELECTRICAL",
      "name": "SWITCH ELECTRIC, INC"
    },
    {
      "role": "CONTRACTOR-GENERAL CONTRACTOR",
      "name": "HAZI ENTERPRISE LLC"
    },
    {
      "role": "MASONRY CONTRACTOR",
      "name": "BUCOVINA INC."
    },
    {
      "role": "CONTRACTOR-PLUMBER/PLUMBING",
      "name": "JAM PLUMBING SERVICES,INC."
    },
    {
      "role": "CONTRACTOR-REFRIGERATION",
      "name": "GOLD COAST HVAC"
    },
    {
      "role": "CONTRACTOR-VENTILATION",
      "name": "GOLD COAST HVAC"
    }
  ],
  "valuation": 200000.0,
  "issueDate": "2026-10-18",
  "trades": [
    "HVAC",
    "electrical",
    "masonry/facade",
    "plumbing",
    "framing",
    "general contracting",
    "decks/porches"
  ],
  "explicitTrades": [
    "HVAC",
    "electrical",
    "masonry/facade",
    "plumbing",
    "framing",
    "general contracting",
    "decks/porches"
  ],
  "tradeDetails": [
    {
      "trade": "HVAC",
      "source": "contractorRole",
      "confidence": "high"
    },
    {
      "trade": "electrical",
      "source": "contractorRole",
      "confidence": "high"
    },
    {
      "trade": "masonry/facade",
      "source": "contractorRole",
      "confidence": "high"
    },
    {
      "trade": "plumbing",
      "source": "contractorRole",
      "confidence": "high"
    },
    {
      "trade": "framing",
      "source": "keyword",
      "confidence": "medium"
    },
    {
      "trade": "general contracting",
      "source": "keyword",
      "confidence": "medium"
    },
    {
      "trade": "decks/porches",
      "source": "keyword",
      "confidence": "medium"
    }
  ],
  "sourceUrl": "https://data.cityofchicago.org/resource/ydr8-5enu.json?permit_=101085337"
}
```

### Pricing

**Pay per result: $2.50 per 1,000 permit records ($0.0025 per result)**, plus a $0.00005 Apify start fee per run. You pay only for rows written to your dataset; Apify platform usage (compute) is included in that price, not billed separately. Set a maximum cost per run in Apify Console or the API and the run stops at that limit. Users on the Apify free plan can try it within their free monthly credit.

### Privacy

Owner and homeowner records are not included: no field or contact role that the agency labels as owner is exported. Personal phone numbers and emails are not exported either. **Contractor names are the agency's contractor field as published. Most are businesses, but some are sole proprietors or individual licensees (e.g. a licensed individual general or alarm contractor).** Treat contractor contact as B2B. Follow the TCPA, CAN-SPAM, do-not-call rules and the source's terms; contact at a permit address should be by mail only unless you have consent.

### Reliability

Each run reads the official feed live. Requests retry up to 3 times with backoff (2 s, 4 s, 8 s) on rate limits, server errors and timeouts. If a run finds 0 matching records it pushes nothing and sets a status message saying so. A change in the source's columns stops the run with a clear error.

### Limitations

- Trade tags are rules applied to permit types, contractor roles and descriptions, so they are not guaranteed. With a trade filter set, only explicit (stated) trades match by default; set `explicitTradesOnly` to false to include implied new-build trades.
- The City's terms let it "require a user of this data to terminate any and all display, distribution or other use" at any time; if that happens this Actor will stop serving the data.
- Not affiliated with or endorsed by the data publisher.

### Attribution and terms

Source: City of Chicago Data Portal, Building Permits (ydr8-5enu).

#### Required City of Chicago disclaimer

> This site provides applications using data that has been modified for use from its original source, www.cityofchicago.org, the official website of the City of Chicago. The City of Chicago makes no claims as to the content, accuracy, timeliness, or completeness of any of the data provided at this site. The data provided at this site is subject to change at any time. It is understood that the data provided at this site is being used at one's own risk.

# Actor input Schema

## `days` (type: `integer`):

How many days back from today to include, by permit issue date (1-366).

## `trades` (type: `array`):

Only return permits tagged with at least one of these trades. Leave empty for all trades.

## `explicitTradesOnly` (type: `boolean`):

When filtering by trade, match only trades stated by the permit type, a listed contractor role or the description, and ignore 'implied' tags (e.g. roofing implied by a new house). On by default for lead generation; turn off to include implied new-build trades.

## `excludeRenewals` (type: `boolean`):

Skip permit renewals/reinstatements (often old jobs re-issued). Recommended for lead generation.

## `dateFrom` (type: `string`):

Earliest issue date, YYYY-MM-DD. When set, overrides 'Days back'.

## `dateTo` (type: `string`):

Latest issue date, YYYY-MM-DD. Defaults to today.

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

Only permits with a declared valuation at or above this amount. Rows with no or zero valuation are dropped when this is set.

## `excludeNonBuilding` (type: `boolean`):

Skip administrative and non-building records (administrative changes, reinstatement-only and similar no-work records).

## `excludeOwnerBuilders` (type: `boolean`):

Skip permits where the property owner acts as their own contractor (e.g. 'OWNER AS GENERAL CONTRACTOR', 'OWNER ACTING AS OWN PLUMBER'). These are homeowner jobs, not contractor leads.

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

Maximum number of records to return (1-50,000).

## Actor input object example

```json
{
  "days": 7,
  "trades": [
    "HVAC",
    "roofing"
  ],
  "explicitTradesOnly": true,
  "excludeRenewals": true,
  "excludeNonBuilding": true,
  "excludeOwnerBuilders": true,
  "maxItems": 100
}
```

# Actor output Schema

## `results` (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 = {
    "days": 7,
    "trades": [
        "HVAC",
        "roofing"
    ],
    "explicitTradesOnly": true,
    "excludeRenewals": true,
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("implementable_antler/chicago-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 = {
    "days": 7,
    "trades": [
        "HVAC",
        "roofing",
    ],
    "explicitTradesOnly": True,
    "excludeRenewals": True,
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("implementable_antler/chicago-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 '{
  "days": 7,
  "trades": [
    "HVAC",
    "roofing"
  ],
  "explicitTradesOnly": true,
  "excludeRenewals": true,
  "maxItems": 100
}' |
apify call implementable_antler/chicago-permits --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,implementable_antler/chicago-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/yCefxKeYD0N9MwfdL/builds/aXLKstM3DJYXzMzpd/openapi.json
