# California Building Permits by Trade: Solar, Roofing, HVAC (`chaucerlabs/california-building-permits-by-trade`) Actor

Newly issued building, electrical and mechanical permits from Los Angeles, San Francisco and San Jose official open data, tagged by trade (solar, battery, EV charger, roofing, HVAC, ADU, pool, plumbing...). Filter by city, trade, date, valuation and ZIP.

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

## Pricing

$4.00 / 1,000 permit 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

## California Building Permits by Trade: Solar, Roofing, HVAC

Get **newly issued building permits from California's biggest cities**, normalized into one clean format and **tagged by trade**, so a solar installer, roofer, HVAC contractor or ADU builder can pull just the jobs that matter to them.

City permit portals each use different columns, codes and update schedules. This Actor reads the official open-data feeds, maps everything to one schema, and labels each permit with the trades it involves.

### Cities covered

| City | Permits included | Contractor names | Valuation | Update cadence |
|---|---|---|---|---|
| **Los Angeles** | Building, electrical, mechanical/plumbing | No | Building permits | Weekly (Saturdays) |
| **San Francisco** | DBI building permits + PermitSF electrical, plumbing/mechanical, re-roofing, remodel and fire permits | Yes (DBI permits, with CSLB license #) | DBI permits | Daily |
| **San Jose** | All building permits issued in the last 30 days | Yes, plus applicant and owner | Yes | Daily |

More California cities are planned.

### Trade tags

`solar`, `battery_storage`, `ev_charger`, `roofing`, `hvac`, `adu`, `pool_spa`, `electrical`, `plumbing`, `water_heater`, `kitchen_bath`, `windows_doors`, `new_construction`, `addition`, `demolition`, `fire_protection`, `seismic_retrofit`, `foundation`, `sign`, `tenant_improvement`, `fence_wall`, `grading`

Tags come from the city's own permit type and flags (for example LA's solar and EV flags) plus the work description. A permit can carry several tags. `primaryTrade` picks the most specific one.

### What you get for each permit

| Field | Example |
|---|---|
| City, permit number, permit page link | Los Angeles, 26041-90000-12345, LADBS permit report |
| Issue date, status | 2026-10-10, Issued |
| Trades, primary trade | solar, battery_storage, electrical → solar |
| Work description | Roof-mounted solar PV system, 32 modules with microinverters, and energy storage system |
| Valuation | $45,000 (where the city publishes it) |
| Address, ZIP, parcel (APN) | 11359 W Biona Dr, 90066 |
| Property type | residential / commercial / unknown |
| Contractor, CSLB license, contractor address | San Francisco and San Jose |
| Applicant, owner | San Jose |
| Latitude/longitude | Los Angeles and San Francisco |

### Filters

City, trade, issue-date range (days back or start/end), minimum and maximum valuation, property type, ZIP codes, keyword (e.g. `heat pump`, `Powerwall`), only permits with a named contractor, and exclude supplemental/revision permits (on by default).

**Monitor mode** remembers the permits you've already received, so a weekly or daily schedule only returns new ones.

### Use cases

- **Solar, battery and EV charger installers:** see who just pulled a re-roof or panel upgrade, and which competitors are installing where.
- **Roofers, HVAC, plumbing and electrical contractors:** follow-on work, market share and territory planning.
- **ADU and remodel builders, architects and lenders:** new ADU and addition activity by ZIP.
- **Building-material suppliers and distributors:** find active contractors (with license numbers in SF).
- **Pool, landscaping, window and insurance companies:** reach homeowners at the moment of a project.
- **Analysts:** track permit volume and job value by trade and city.

### Input example

```json
{
  "cities": ["los_angeles", "san_jose"],
  "trades": ["solar", "battery_storage", "roofing"],
  "daysBack": 14,
  "propertyTypes": ["residential"]
}
```

### Output example (trimmed)

```json
{
  "city": "Los Angeles",
  "permitNumber": "26016-30000-27716",
  "permitType": "Building / Bldg-Alter/Repair",
  "workDescription": "Re-roof with Class A or B material weighing less than 6 pounds per sq. ft. over existing solid sheathing 22 Squares...",
  "trades": ["roofing"],
  "primaryTrade": "roofing",
  "issueDate": "2026-10-10",
  "valuationUsd": 15000,
  "address": "700-702 N WATERLOO ST",
  "zip": "90026",
  "propertyType": "residential",
  "permitUrl": "https://www.ladbsservices2.lacity.org/OnlineServices/PermitReport/PcisPermitDetail?id1=26016&id2=30000&id3=27716"
}
```

Each run also saves `RUN_STATS`: permits fetched per city, permits returned, counts per trade, and any portal errors. If one city's portal is down, the others still return.

### Pricing

You pay per permit returned. Permits removed by your filters are never charged. See the Pricing tab for the current rate.

### Notes and limits

- **Freshness:** Los Angeles publishes weekly, so recent days fill in after each Saturday refresh. San Francisco and San Jose update daily.
- **San Jose** publishes a rolling 30-day file, so older San Jose permits aren't available.
- **Tags are keyword-based.** Accuracy is high for clear descriptions, like LA's standard re-roof and solar language, but short or vague descriptions may be untagged or over-tagged.
- **Valuation** is the applicant's declared job value. Many electrical/mechanical permits don't have one.
- **Source:** official city open-data portals (data.lacity.org, data.sf.gov, data.sanjoseca.gov), used through their public APIs. No login and no scraping of permit-search websites.
- **Contact compliance:** owner and contractor names are public records, but your outreach must follow the laws that apply to you.

# Actor input Schema

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

Which city portals to read. Default: all three.

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

Keep permits tagged with any of these trades. Empty = all permits.

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

Calendar days back from the end date. Los Angeles publishes weekly, so 14 is a good default.

## `startDate` (type: `string`):

Optional, YYYY-MM-DD issue date. Overrides Days back. San Jose only offers the last 30 days.

## `endDate` (type: `string`):

Optional, YYYY-MM-DD. Defaults to today. Max range 92 days.

## `minValuationUsd` (type: `integer`):

Keep permits with a declared job value of at least this much. Permits with no valuation (most electrical/mechanical permits) are dropped when this is set.

## `maxValuationUsd` (type: `integer`):

0 = no maximum.

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

Empty = all.

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

Keep only these 5-digit ZIP codes. Empty = all.

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

Optional text that must appear in the work description, permit type or address, e.g. heat pump, Tesla, Powerwall.

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

Contractor names are published for San Francisco (DBI permits) and San Jose.

## `excludeSupplemental` (type: `boolean`):

Drop paperwork-only permits such as plan revisions, contractor changes and permit re-issues.

## `includeUntagged` (type: `boolean`):

Only applies when Trades is empty.

## `onlyNewSinceLastRun` (type: `boolean`):

Schedule weekly or daily for a no-repeat permit feed.

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

0 = no limit.

## Actor input object example

```json
{
  "cities": [
    "los_angeles",
    "san_francisco",
    "san_jose"
  ],
  "daysBack": 14,
  "minValuationUsd": 0,
  "maxValuationUsd": 0,
  "requireContractor": false,
  "excludeSupplemental": true,
  "includeUntagged": true,
  "onlyNewSinceLastRun": false,
  "maxResults": 0
}
```

# Actor output Schema

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

Issued permits with trade tags, description, valuation, address, contractor (where published) and permit link.

## `runStats` (type: `string`):

Permits fetched per city, permits returned, trade counts and any source errors.

# 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": [
        "los_angeles",
        "san_francisco",
        "san_jose"
    ],
    "daysBack": 14
};

// Run the Actor and wait for it to finish
const run = await client.actor("chaucerlabs/california-building-permits-by-trade").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": [
        "los_angeles",
        "san_francisco",
        "san_jose",
    ],
    "daysBack": 14,
}

# Run the Actor and wait for it to finish
run = client.actor("chaucerlabs/california-building-permits-by-trade").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": [
    "los_angeles",
    "san_francisco",
    "san_jose"
  ],
  "daysBack": 14
}' |
apify call chaucerlabs/california-building-permits-by-trade --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,chaucerlabs/california-building-permits-by-trade"
        }
    }
}
```

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/kdWg7N6MlCMrayaSd/builds/cf3dWtbVBe7sWKYXt/openapi.json
