# Dallas Building Permits Scraper (`crawlerbros/dallas-building-permits-scraper`) Actor

Scrape City of Dallas building permit records from the official Dallas Open Data portal (dallasopendata.com). Filter by permit type, land use, ZIP code, issued-date range, contractor, work description, and permit value/area. Lookup exact permits by permit number. No auth, no proxy required.

- **URL**: https://apify.com/crawlerbros/dallas-building-permits-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Dallas Building Permits Scraper

Scrape **building permit records for the City of Dallas, Texas** directly from the official Dallas Open Data portal (dallasopendata.com). Browse and filter permits by type, land use, ZIP code, issued-date range, contractor, work description, and declared value/area — or look up exact permits by permit number. HTTP-only via the public Socrata Open Data API. No auth, no proxy, no cookies.

### What this actor does

- **Two modes:** `search` (filtered browse) and `byPermitNumbers` (exact lookup)
- **Filters:** permit type, land use, ZIP code, work-description keyword, street-address keyword, contractor keyword, Mapsco grid-reference keyword, issued-date range, min/max permit value, min/max area
- **Sorting:** by permit value, area (high-to-low / low-to-high), or issued date (newest/oldest first)
- **Curated dropdowns** for the most common permit types, land uses, and Dallas ZIP codes, plus free-text "contains" overrides for anything not in the curated list
- **Empty fields are omitted** — a record only contains fields the source actually had data for

### Output per permit

- `permitNumber` — Dallas permit number
- `permitType` — e.g. `Building (BU) Single Family  New Construction`
- `issuedDate` — ISO date (`YYYY-MM-DD`)
- `workDescription` — free-text description of the work performed
- `landUse` — zoning/land-use classification
- `streetAddress`, `zipCode`
- `value` — declared permit value in USD (omitted when not recorded)
- `area` — declared area in square feet (omitted when not recorded)
- `contractor` — contractor name, address, and phone as filed
- `contractorPhone` — phone number extracted from `contractor`, when present
- `mapsco` — Dallas Mapsco grid reference
- `sourceUrl` — direct link to the permit's record in the Dallas Open Data API
- `recordType: "buildingPermit"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `byPermitNumbers` |
| `permitType` | string (select) | – | Filter to one exact permit type (mode=search) |
| `permitTypeCustom` | string | – | Free-text "contains" override for permit type |
| `landUse` | string (select) | – | Filter to one exact land-use classification |
| `landUseCustom` | string | – | Free-text "contains" override for land use |
| `zipCode` | string (select) | – | Filter to one Dallas ZIP code |
| `workDescriptionKeyword` | string | – | Only permits whose work description contains this text |
| `streetAddressKeyword` | string | – | Only permits whose street address contains this text |
| `contractorKeyword` | string | – | Only permits whose contractor field contains this text |
| `mapscoKeyword` | string | – | Only permits whose Mapsco grid reference contains this text |
| `issuedDateFrom` | string | – | Only permits issued on/after this date (`YYYY-MM-DD`) |
| `issuedDateTo` | string | – | Only permits issued on/before this date (`YYYY-MM-DD`) |
| `minValue` / `maxValue` | int | – | Declared permit value range (USD) |
| `minArea` / `maxArea` | int | – | Declared area range (sq ft) |
| `sortBy` | string (select) | `default` | `default` / `valueHighToLow` / `valueLowToHigh` / `areaHighToLow` / `areaLowToHigh` / `issuedDateNewestFirst` / `issuedDateOldestFirst`. `valueLowToHigh`/`areaLowToHigh` implicitly require a positive value/area (permits with no recorded value/area are excluded — see FAQ) so ascending sort never returns pages of "not recorded" rows instead of real ones |
| `permitNumbers` | array | – | Exact permit numbers to look up (mode=byPermitNumbers) |
| `maxItems` | int | `50` | Hard cap on emitted records (1–5000) |

#### Example: recent single-family new construction

```json
{
  "mode": "search",
  "permitType": "Building (BU) Single Family  New Construction",
  "maxItems": 50
}
```

#### Example: permits over $500k in a ZIP code

```json
{
  "mode": "search",
  "zipCode": "75201",
  "minValue": 500000,
  "sortBy": "valueHighToLow",
  "maxItems": 100
}
```

#### Example: keyword + date range

```json
{
  "mode": "search",
  "workDescriptionKeyword": "swimming pool",
  "issuedDateFrom": "2019-06-01",
  "issuedDateTo": "2019-08-31",
  "maxItems": 100
}
```

#### Example: most recently issued permits

```json
{
  "mode": "search",
  "sortBy": "issuedDateNewestFirst",
  "maxItems": 50
}
```

#### Example: lookup by permit number

```json
{
  "mode": "byPermitNumbers",
  "permitNumbers": ["2003133024", "1904171125"]
}
```

### Use cases

- **Real estate research** — track new construction and renovation activity in a neighborhood or ZIP code
- **Contractor lead generation** — find recently permitted projects and the contractors who filed them
- **Market analysis** — measure building activity trends by permit type, land use, or declared value
- **Insurance & lending** — verify permitted work history for a property or address
- **Journalism & civic research** — analyze development patterns across Dallas

### FAQ

**What is the data source?**
The City of Dallas's official Open Data portal (dallasopendata.com), specifically the "Building Permits" dataset (`e7gq-4sah`), published via the Socrata Open Data API.

**How current is the data?**
This dataset covers permits issued between January 2018 and August 2020. The City of Dallas has since migrated active permit tracking to its Accela Citizen Access portal and no longer updates this historical dataset — the records themselves, however, are complete and unchanged real permit filings.

**Why do some permits have no `value` or `area`?**
Not every permit type requires a declared dollar value or square footage (e.g. many electrical or plumbing alteration permits). Those fields are simply omitted rather than shown as `0`.

**How does `sortBy: issuedDateNewestFirst`/`issuedDateOldestFirst` sort chronologically if `issued_date` is stored as free text?**
The source stores `issued_date` as `MM/DD/YY` text, so a naive alphabetical sort on that column would order by month/day, not by year. Because every date in the dataset follows the same zero-padded format and the dataset only spans 2018–2020, this actor reorders the year, month, and day components before sorting so results come back in genuine chronological order, not lexical order.

**Why does `sortBy: valueLowToHigh` / `areaLowToHigh` not just sort the raw source order ascending?**
24% of permits have no recorded `value` and 61% have no recorded `area` (stored upstream as `0`, which this actor treats as "not recorded" and omits — see above). A naive ascending sort would put that entire not-recorded bucket first, so every returned record would be missing the very field you asked to sort by. Ascending sort therefore implicitly requires a positive value/area so you always get real, populated numbers back. `valueHighToLow`/`areaHighToLow` don't need this — the not-recorded rows already sort to the bottom on their own.

**Why do some permits have no `contractor`?**
Owner-built or self-filed permits often have no contractor on file in the source system; this actor omits the field rather than showing blank/placeholder text.

**What's the difference between `permitType`/`landUse` and their `Custom` counterparts?**
The dropdown lists the most common values for each field (45 of 137 permit types, 45 of 189 land uses, ranked by how many permits use them). If the value you need isn't in the dropdown, use the matching `Custom` field to match any value containing your text.

**Why is `zipCode` a full dropdown but `permitType`/`landUse`/`mapsco` are not?**
The source data contains only 71 distinct ZIP codes, so every one of them fits in the dropdown — no free-text override is needed there. Permit type (137 values) and land use (189 values) are curated to their most-common ~45 for usability, with a `Custom` contains-match field for the rest. Mapsco grid reference has 3,000+ distinct values (a fine-grained map-grid code, not a bounded category), so it is contains-match only, with no dropdown.

**Is this affiliated with the City of Dallas?**
No — this is an independent, third-party actor that queries the City of Dallas's public open-data API. It is not affiliated with or endorsed by the City of Dallas.

**Does this actor cover current/live permit activity?**
No — the source dataset only covers permits issued between January 2018 and August 2020 (see "How current is the data?" above). There is no newer permits dataset published on dallasopendata.com to switch to.

**Why is there no map/location field in the output?**
The source data has no latitude/longitude or geocoded-point column — only a text `streetAddress`, `zipCode`, and `mapsco` grid reference — so there's nothing reliable to populate a map field with.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `permitType` (type: `string`):

Filter to one exact permit type (mode=search). Pick 'Any permit type' to skip this filter, or use Permit type (custom) below for a value not in this curated list of the 45 most common types (137 exist in total).

## `permitTypeCustom` (type: `string`):

Free-text override for permit types not in the curated dropdown above (e.g. 'Excavation', 'Grading and Paving'). Matches any permit type containing this text, case-insensitive. Takes precedence over 'Permit type' when set.

## `landUse` (type: `string`):

Filter to one exact land-use classification (mode=search). Pick 'Any land use' to skip this filter, or use Land use (custom) below for a value not in this curated list of the 45 most common uses (189 exist in total).

## `landUseCustom` (type: `string`):

Free-text override for land-use values not in the curated dropdown above (e.g. 'HOSPITAL'). Matches any land use containing this text, case-insensitive. Takes precedence over 'Land use' when set.

## `zipCode` (type: `string`):

Filter to permits at addresses within one Dallas-area ZIP code (mode=search).

## `workDescriptionKeyword` (type: `string`):

Only include permits whose work description contains this text (case-insensitive), e.g. 'swimming pool', 'water heater', 'roof'.

## `streetAddressKeyword` (type: `string`):

Only include permits whose street address contains this text (case-insensitive), e.g. 'MAIN ST', 'ELM'.

## `contractorKeyword` (type: `string`):

Only include permits whose contractor field contains this text (case-insensitive), e.g. a contractor company name.

## `mapscoKeyword` (type: `string`):

Only include permits whose Dallas Mapsco grid reference contains this text (case-insensitive), e.g. '54-B'. There are 3,000+ distinct grid values in the source data, too many for a dropdown, so this is contains-match only.

## `issuedDateFrom` (type: `string`):

Only include permits issued on or after this date. Source data covers permits issued between 2018-01-01 and 2020-08-30 (Dallas migrated active permit tracking to a different system after that).

## `issuedDateTo` (type: `string`):

Only include permits issued on or before this date.

## `minValue` (type: `integer`):

Drop permits with a declared value lower than this.

## `maxValue` (type: `integer`):

Drop permits with a declared value higher than this.

## `minArea` (type: `integer`):

Drop permits with a declared area smaller than this.

## `maxArea` (type: `integer`):

Drop permits with a declared area larger than this.

## `sortBy` (type: `string`):

Ordering of results.

## `permitNumbers` (type: `array`):

Exact Dallas permit numbers to look up, e.g. '2003133024'.

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

Hard cap on emitted records.

## Actor input object example

```json
{
  "mode": "search",
  "permitType": "Building (BU) Single Family  New Construction",
  "landUse": "",
  "zipCode": "",
  "sortBy": "default",
  "permitNumbers": [],
  "maxItems": 50
}
```

# Actor output Schema

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

Dataset containing all scraped Dallas building permits.

# 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 = {
    "mode": "search",
    "permitType": "Building (BU) Single Family  New Construction",
    "landUse": "",
    "zipCode": "",
    "sortBy": "default",
    "permitNumbers": [],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/dallas-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 = {
    "mode": "search",
    "permitType": "Building (BU) Single Family  New Construction",
    "landUse": "",
    "zipCode": "",
    "sortBy": "default",
    "permitNumbers": [],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/dallas-building-permits-scraper").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 '{
  "mode": "search",
  "permitType": "Building (BU) Single Family  New Construction",
  "landUse": "",
  "zipCode": "",
  "sortBy": "default",
  "permitNumbers": [],
  "maxItems": 50
}' |
apify call crawlerbros/dallas-building-permits-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/rIAuDZdKIKkif7Ue8/builds/RW4KKe7uGxfejeUjt/openapi.json
