Skip to main content
Docs / Permits

Building permits

Permits

Building permits from city and county building departments: who is doing the work, what it is, its value, its status, and the contacts printed on it.

Boardwalk reads building permits from city and county permit portals and serves each one as a clean record: the work and its value, the address and parcel, the status and inspections, and everyone named on the permit with the phone, email, and license printed for them. Permits sit beside planned projects: a planned project is a development moving through public meetings, a permit is a building department filing for a specific job. Coverage starts in Utah and grows through the end of 2026; GET /permits/coverage lists the places today.

EndpointMethodWhat it does
/permits/countGETHow many permits match, and what pulling them would use. Free.
/permits/searchGETMatching permits, a page at a time.
/permits/{id}GETOne permit by its permitId.
/permits/syncGETEvery matching permit in the order it changed, to keep a copy current.
/permits/contractorsGETThe companies on the matching permits, with the contacts printed for them.
/permits/aggregatePOSTCounts and declared job values by place, work type, status, trade, or month. Free.
/permits/referenceGETEvery filter value and its label. Free.
/permits/coverageGETPlaces with permits and whether your plan covers each one. Free.

Permits also work with permit alerts, saved lists and CSV exports. The endpoint table lists every permit route and its cost, and Permit filters lists every filter and value.

What it costs

Permits use the same allowance as planned projects. On County, State, and National, each permit that is new to your account or changed since you last received it uses 1 export record from the shared monthly allowance; an unchanged permit you already hold is free. On Standalone API Only and the free trial it is 1 credit per new or changed permit. Contacts printed on the permit come with the record and use no Find Contact Info allowance.

Cost ofGET /permits/search

Plan dependent: 1 export record per new or changed permit (1 credit on API Only and the trial)

County, State, and National: 1 export record per permit that is new to this account or changed since the last delivery, against the shared 100,000 a month. Permits and planned projects share that counter, across the API, MCP, CSV, and the app. An unchanged permit already delivered is free. Standalone API Only and the free trial: 1 credit per new or changed permit; unchanged owned permits are free. Contacts printed on the permit come with the record and use no Find Contact Info allowance.

Cost ofGET /permits/count

Free (0 credits)

Free, and it does not use an export record. Returns the match count and what GET /permits/search would bill for it: new or changed permits against permits you already hold unchanged.

Count new homes permitted in Provo in the last 90 days, and what pulling them would use

bash
curl 'https://api.boardwalkai.com/api/v1/permits/count?cities=Provo&workTypes=new_home&dateRange=90d' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": {
    "matchCount": 186,
    "approximate": false,
    "billable": {
      "newOrChanged": 150,
      "unchangedOwned": 36,
      "exact": true,
      "exportRecords": 150
    },
    "affordability": {
      "maxAffordableRecords": 99850,
      "unit": "export_record"
    }
  },
  "meta": {
    "appliedFilters": {
      "cities": [
        4417
      ],
      "workTypes": [
        "new_home"
      ],
      "dateRange": "90d"
    },
    "appliedDefaults": {
      "jobKinds": [
        "major",
        "minor"
      ],
      "dateField": "activity",
      "sort": "recent"
    },
    "resolvedFrom": {
      "cities": [
        {
          "input": "Provo",
          "id": 4417
        }
      ]

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

What a search returns by default

  • Major and minor jobs. Record-only entries (licenses, property records, and planning cases a city files as permits) come back only with includeRecords=true.
  • Permits with activity in the last 12 months (dateField=activity, dateRange=12m). Send dateRange=all for every date. meta.appliedDefaults lists every default the API assumed.
  • The permits map opens on Issued, any date. Send dateField=issued&dateRange=all to match it.
  • Newest activity first, 25 per page (up to 100).
  • Places in one request OR together: cities=Provo,Orem is permits in either city.

Your plan area

A search reads only the places your plan covers. A request for a place wholly outside it answers 403 geographic_access_denied and uses nothing. Ask GET /permits/coverage which places have permits and which of them your plan includes.

Contacts and private individuals

A permit names the people on the job: contractors, architects, engineers, applicants, and owners, homeowners included. Every one is in parties, with the phone, email, address, and license the permit printed for them. A person who is not a business is marked isPrivateIndividual: true so you can apply your own rules before contacting them. whoToCall is the best party to call first. Your use of contact data is governed by the Boardwalk terms of service.

Homeowner phones and emails

A private individual's name and address always show. Their phone and email (usually a homeowner's) can be withheld. In their place the party carries contactWithheld: {withheld, hasPhone, hasEmail}, which says why and whether there is a phone or an email to see.

Your planwithheldWhat you see
Free trialupgradeThe name and address. The phone and email are withheld until you move to a paid plan.
Paid plan, calling reminder not yet acceptedacknowledgeThe name and address. The phone and email show once the user the API key belongs to accepts the one-time calling reminder on the Boardwalk website: sign in, open a permit that lists a homeowner, and choose Show phone.

Company and employee contacts are never withheld, and neither is a person on the permit as a contractor, with a license number, or for another business role. The same rule covers whoToCall, CSV exports, and the source text, where phones and emails read [phone withheld] and [email withheld]. On such a permit, phones and emails in the record's own text (the printed description, summaries, and process and inspection notes) read the same way, counted in textContactWithheld.

Calls and texts to homeowners are covered by the TCPA, Do Not Call, and state rules. Check numbers against the National Do Not Call Registry, and do not use robocalls, AI or recorded voices, or bulk texts without the person's prior written consent.

A page of results

The record below is the standard representation, the default. Send representation=compact for lists or representation=full for the whole city record; Permit record fields lists every field. The homeowner, Pat Example, printed a phone on the permit, and this key's user has not accepted the calling reminder yet, so the phone is withheld with withheld: "acknowledge".

The first permit of that match, with the contacts printed on it (the homeowner's phone is withheld until the calling reminder is accepted)

bash
curl 'https://api.boardwalkai.com/api/v1/permits/search?cities=Provo&workTypes=new_home&dateRange=90d&limit=1' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": [
    {
      "permitId": 9100001,
      "permitNumber": "BLD-2026-00042",
      "title": "New single-family home",
      "workTypes": [
        {
          "key": "new_home",
          "label": "New homes"
        }
      ],
      "status": "active",
      "statusLabel": "Active",
      "stage": "issued",
      "stageLabel": "Issued",
      "address": {
        "line1": "100 Example St",
        "city": "Exampleton",
        "state": "UT",
        "zip": "84000"
      },
      "county": "Example County",
      "jobValue": 425000,
      "appliedOn": "2026-08-01",
      "issuedOn": "2026-09-15",
      "lastActivity": {
        "date": "2026-10-02",
        "label": "Footing inspection"
      },
      "contractor": "Example Builders LLC",
      "whoToCall": {
        "name": "Example Builders LLC",
        "role": "Contractor",
        "contactPerson": "Sam Example",
        "phone": "(555) 010-0042",
        "email": "office@builders.example.test"
      },
      "boardwalkUrl": "https://boardwalkai.com/map/#permit=9100001",
      "delivery": {

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

When a permit counts as changed

A permit you already hold is billed again only when its content changed: a new status, an inspection, a new party, a different value. Counters that move by themselves every day (daysOpen, daysSinceIssued), the time-based why-now reasons, and company contacts taken from the same company's other permits are not part of that comparison, so a re-read of an unchanged permit stays free. delivery on every record says whether it was new to you, changed, and billed.

Read one permit again: it has not changed since you received it, so it is free

bash
curl 'https://api.boardwalkai.com/api/v1/permits/9100001?representation=compact' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": {
    "permitId": 9100001,
    "permitNumber": "BLD-2026-00042",
    "title": "New single-family home",
    "workTypes": [
      {
        "key": "new_home",
        "label": "New homes"
      }
    ],
    "status": "active",
    "statusLabel": "Active",
    "stage": "issued",
    "stageLabel": "Issued",
    "address": {
      "line1": "100 Example St",
      "city": "Exampleton",
      "state": "UT",
      "zip": "84000"
    },
    "county": "Example County",
    "jobValue": 425000,
    "appliedOn": "2026-08-01",
    "issuedOn": "2026-09-15",
    "lastActivity": {
      "date": "2026-10-02",
      "label": "Footing inspection"
    },
    "contractor": "Example Builders LLC",
    "whoToCall": {
      "name": "Example Builders LLC",
      "role": "Contractor",
      "contactPerson": "Sam Example",
      "phone": "(555) 010-0042",
      "email": "office@builders.example.test"
    },
    "boardwalkUrl": "https://boardwalkai.com/map/#permit=9100001",
    "delivery": {
      "alreadyExported": true,

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Keeping a copy current

GET /permits/sync walks every matching permit in the order it last changed, 100 per page. Keep meta.nextCursor and send it back as cursor to continue; a cursor only resumes the filters it came from. Sync has no default date window. Deletions are not sent: a permit that stops being served stops appearing, and GET /permits/{id} answers 404 for it.

Cost ofGET /permits/sync

Plan dependent: 1 export record per new or changed permit (1 credit on API Only and the trial)

County, State, and National: 1 export record per new or changed permit, against the shared 100,000 a month. Unchanged permits bill 0, so walking the same filters again costs only what changed. Standalone API Only and the free trial: 1 credit per new or changed permit.

The first page of a copy of every new-home permit in Provo; send meta.nextCursor back as cursor for the next

bash
curl 'https://api.boardwalkai.com/api/v1/permits/sync?cities=Provo&workTypes=new_home&representation=compact&limit=1' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": [
    {
      "permitId": 9100001,
      "permitNumber": "BLD-2026-00042",
      "title": "New single-family home",
      "workTypes": [
        {
          "key": "new_home",
          "label": "New homes"
        }
      ],
      "status": "active",
      "statusLabel": "Active",
      "stage": "issued",
      "stageLabel": "Issued",
      "address": {
        "line1": "100 Example St",
        "city": "Exampleton",
        "state": "UT",
        "zip": "84000"
      },
      "county": "Example County",
      "jobValue": 425000,
      "appliedOn": "2026-08-01",
      "issuedOn": "2026-09-15",
      "lastActivity": {
        "date": "2026-10-02",
        "label": "Footing inspection"
      },
      "contractor": "Example Builders LLC",
      "whoToCall": {
        "name": "Example Builders LLC",
        "role": "Contractor",
        "contactPerson": "Sam Example",
        "phone": "(555) 010-0042",
        "email": "office@builders.example.test"
      },
      "boardwalkUrl": "https://boardwalkai.com/map/#permit=9100001",
      "delivery": {

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Contractors

GET /permits/contractors takes the permit filters and groups the matching permits by the company named on them as general contractor, builder, or trade contractor. Each row has the number of permits, their declared job value, the latest activity, the cities, the top kinds of work, and the company contacts printed on those permits. Owner-builders who are private individuals are left out and counted in meta.privateLeftOut. Send a row's contractorKey back to search as contractorKeys to list its permits. Sort with sort=permits, value, recent. Each row that is new to your account or changed uses 1 export record (or 1 credit), the same as a permit.

The busiest new-home builder in Provo over the last 12 months, with the contacts on its permits

bash
curl 'https://api.boardwalkai.com/api/v1/permits/contractors?cities=Provo&workTypes=new_home&limit=1' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": [
    {
      "contractorKey": "6c733815b523d162",
      "name": "Example Builders LLC",
      "role": "general_contractor",
      "roleLabel": "General contractor",
      "permits": 14,
      "jobValue": {
        "sum": 5950000,
        "known": 14
      },
      "lastActivityOn": "2026-10-02",
      "cityIds": [
        4417
      ],
      "cities": [
        "Provo"
      ],
      "topWorkTypes": [
        {
          "key": "new_home",
          "label": "New homes",
          "count": 14
        }
      ],
      "phone": "(555) 010-0042",
      "email": "office@builders.example.test",
      "website": null,
      "license": {
        "number": "12345-5501",
        "state": "UT",
        "class": "B100"
      },
      "basis": "permits",
      "links": {
        "permits": "/api/v1/permits/search?contractorKeys=6c733815b523d162"
      },
      "delivery": {
        "alreadyExported": false,

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Permit alerts

A permit alert is a saved permit search that runs daily, weekly, or monthly and adds the new matching permits, up to 1,000 a run. Its id starts with palr_. Permit alerts live at /permit-alerts; planned-project alerts live at /alerts, and an id from one never works on the other. An alert takes the same filter names and values as search, except permitIds, contractorKeys, placeKey, or bbox.

  • Up to 25 active permit alerts per account, counted apart from planned-project alerts. One more is 403 alert_limit_reached.
  • Unless your plan is nationwide, every alert names its places: at least one, and at most 5 states and 25 counties or cities (403 alert_geography_required, 403 alert_geography_too_broad). A place outside your plan area is refused, and every run stays inside your plan area.
  • With no date filter, an alert keeps the last 12 months of activity as a rolling window. Its filters show it.
  • Creating, previewing, editing, and deleting an alert are free.
  • A run you start with POST /permit-alerts/{alertId}/run bills each permit it adds that is new to your account or changed: 1 export record on County, State, and National, 1 credit on Standalone API Only and the free trial. Scheduled runs bill the same way for an alert created over the API, or whose filters were changed over the API (metered: true). Scheduled runs of an alert made on the website are free.
  • If your allowance cannot cover a run, nothing is added or charged: the run answers status: "insufficient_credits" with a quote.
  • Up to 3 manual runs per alert a day, each with an Idempotency-Key.
  • Reading what a run found, GET /permit-alerts/{alertId}/matches, is free every time.

Create a weekly permit alert for new homes in Provo (free)

bash
curl -X POST 'https://api.boardwalkai.com/api/v1/permit-alerts' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "New homes in Provo",
    "filters": {
        "cities": [
            "Provo"
        ],
        "workTypes": [
            "new_home"
        ]
    },
    "cadence": {
        "frequency": "weekly"
    }
}'

Response · HTTP 201

json
{
  "data": {
    "id": "palr_3f9c2a7e41d84b06a1c5e8f20d7b9a44",
    "dataset": "permits",
    "name": "New homes in Provo",
    "filters": {
      "workTypes": [
        "new_home"
      ],
      "cities": [
        4417
      ],
      "dateField": "activity",
      "dateRange": "12m"
    },
    "cadence": {
      "frequency": "weekly"
    },
    "share": {
      "mode": "private",
      "userIds": [],
      "optOutUserIds": []
    },
    "active": true,
    "isOwner": true,
    "counts": {
      "pending": 0,
      "saved": 0,
      "dismissed": 0,
      "all": 0
    },
    "maxPerRun": 1000,
    "lastRunAt": null,
    "lastRunCapped": false,
    "lastRunRefusal": null,
    "metered": true,
    "nextRunAt": "2026-11-02T15:00:00+00:00",
    "createdAt": "2026-11-02T15:00:00+00:00",
    "links": {
      "matches": "/api/v1/permit-alerts/palr_3f9c2a7e41d84b06a1c5e8f20d7b9a44/matches",

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

A week later: how many permits match, and how many the next run would add (free)

bash
curl 'https://api.boardwalkai.com/api/v1/permit-alerts/palr_3f9c2a7e41d84b06a1c5e8f20d7b9a44/preview' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": {
    "alertId": "palr_3f9c2a7e41d84b06a1c5e8f20d7b9a44",
    "matchCount": 418,
    "matchCountIsExact": true,
    "newSinceLastRun": 6,
    "lastRunAt": "2026-11-02T15:00:00+00:00",
    "lastRunCapped": false,
    "maxPerRun": 1000
  },
  "meta": {
    "creditsUsed": 0
  }
}

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Run it now: the 6 new permits are added and billed; read them free from links.matches

bash
curl -X POST 'https://api.boardwalkai.com/api/v1/permit-alerts/palr_3f9c2a7e41d84b06a1c5e8f20d7b9a44/run' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY' \
  -H 'Idempotency-Key: provo-new-homes-2026-11-09'

Response · HTTP 201

json
{
  "data": {
    "alertId": "palr_3f9c2a7e41d84b06a1c5e8f20d7b9a44",
    "status": "completed",
    "ranAt": "2026-11-09T15:00:00+00:00",
    "newMatches": 6,
    "capped": false,
    "deliveryId": "pdlv_20261109150000",
    "manualRunsLeftToday": 2
  },
  "meta": {
    "creditsUsed": 6
  }
}

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Lists and CSV exports

Save permits to Saved or any named list with POST /lists/{listId}/permits, up to 100 ids at a time, for free. GET /lists/{listId}/permits reads them back as permit records and is billed like search.

POST /permits/exports builds a CSV of up to 10,000 permits from one source: filters, permitIds, listId, or alertId. It needs an Idempotency-Key. Nothing is charged when you queue it. When the file is built, each permit that is new to your account or changed uses 1 export record (or 1 credit on Standalone API Only and the free trial), and a failed export charges nothing. meta.billableUpTo is the most it can charge; set maxCredits to cap it. Poll GET /exports/{id} until status is ready, then download the file from downloadUrl.

Queue a CSV of the same 186 permits: nothing is charged until the file is built

bash
curl -X POST 'https://api.boardwalkai.com/api/v1/permits/exports' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY' \
  -H 'Idempotency-Key: provo-new-homes-csv-2026-11-02' \
  -H 'Content-Type: application/json' \
  -d '{
    "filters": {
        "cities": [
            "Provo"
        ],
        "workTypes": [
            "new_home"
        ],
        "dateRange": "90d"
    },
    "filename": "provo-new-homes.csv"
}'

Response · HTTP 202

json
{
  "exportJobId": 51234,
  "status": "queued",
  "dataset": "permits",
  "format": "csv",
  "pollUrl": "https://api.boardwalkai.com/api/v1/exports/51234",
  "downloadUrl": "https://api.boardwalkai.com/api/v1/exports/51234/download",
  "meta": {
    "requestedFrom": "filters",
    "requested": 186,
    "estimatedRows": 186,
    "unavailable": 0,
    "deliveryMode": "all",
    "billableUpTo": 150,
    "maxCredits": null,
    "creditsUsed": 0,
    "note": "Poll pollUrl until status is ready, then download. The export is charged when the file is built; a failed export charges nothing.",
    "matchCount": 186,
    "appliedFilters": {
      "cities": [
        4417
      ],
      "workTypes": [
        "new_home"
      ],
      "dateRange": "90d"
    },
    "appliedDefaults": {
      "jobKinds": [
        "major",
        "minor"
      ],
      "dateField": "activity",
      "sort": "recent"
    },
    "warnings": []
  }
}

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Poll until status is ready, then download: 150 new or changed permits were charged, 36 you already held were free

bash
curl 'https://api.boardwalkai.com/api/v1/exports/51234' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "exportJobId": 51234,
  "status": "ready",
  "format": "csv",
  "pollUrl": "https://api.boardwalkai.com/api/v1/exports/51234",
  "downloadUrl": "https://api.boardwalkai.com/api/v1/exports/51234/download",
  "webExportsUrl": "https://boardwalkai.com/exports",
  "job": {
    "id": 51234,
    "kind": "csv",
    "dataset": "permits",
    "status": "ready",
    "filename": "provo-new-homes.csv",
    "sourceLabel": "API permit export",
    "rowCount": 186,
    "creditsCharged": 150,
    "creditsReserved": 0,
    "contactBilling": {
      "permitExport": {
        "source": "api",
        "mcpTool": null,
        "requestedFrom": "filters",
        "deliveryMode": "all",
        "maxCredits": null,
        "jobKinds": [
          "major",
          "minor"
        ],
        "selection": {
          "requested": 186,
          "delivered": 186,
          "unavailable": 0,
          "leftOut": 0,
          "trialTruncated": false,
          "billable": 150
        }
      }
    },
    "projectCount": 0,
    "recordCount": 186,

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Market size and coverage

Aggregates

POST /permits/aggregate counts permits and totals their declared job values by up to 3 of: state, county, city, work type, status, stage, trade, Building Type, month, and more. It is free and never returns a permit, contractor, address, or party. A group of fewer than 5 permits is left out and counted in meta.suppressedGroups. Value metrics cover permits that declare a value for their own work, and coverage.valueKnown is that share. A whole project's value printed on a smaller permit (jobValueScope: parent_project) is left out, so one project is not totaled once per permit. GET /analytics/datasets describes this dataset too.

What kind of work is being permitted in Utah County, for free

bash
curl -X POST 'https://api.boardwalkai.com/api/v1/permits/aggregate' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "filters": {
        "counties": [
            "Utah County"
        ],
        "dateRange": "12m"
    },
    "groupBy": [
        "workGroup"
    ],
    "metrics": [
        "count",
        "medianJobValue",
        "contractors"
    ],
    "limit": 3
}'

Response · HTTP 200

json
{
  "data": {
    "groups": [
      {
        "keys": {
          "workGroup": {
            "token": "new",
            "label": "New buildings"
          }
        },
        "metrics": {
          "count": 2140,
          "medianJobValue": 398000,
          "contractors": 212
        },
        "coverage": {
          "valueKnown": 0.9612
        }
      },
      {
        "keys": {
          "workGroup": {
            "token": "remodel",
            "label": "Additions and remodels"
          }
        },
        "metrics": {
          "count": 1310,
          "medianJobValue": 42000,
          "contractors": 388
        },
        "coverage": {
          "valueKnown": 0.8871
        }
      },
      {
        "keys": {
          "workGroup": {
            "token": "systems",
            "label": "Building systems"

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Coverage

Where permits are covered, and which places a County plan includes

bash
curl 'https://api.boardwalkai.com/api/v1/permits/coverage' \
  -H 'Authorization: Bearer bwk_live_YOUR_KEY'

Response · HTTP 200

json
{
  "data": [
    {
      "level": "state",
      "id": 1,
      "name": "Utah",
      "permitCount": 64412,
      "entitled": false,
      "partlyEntitled": true,
      "children": [
        {
          "level": "county",
          "id": 2871,
          "name": "Utah County",
          "permitCount": 18204,
          "entitled": true
        },
        {
          "level": "county",
          "id": 2865,
          "name": "Salt Lake County",
          "permitCount": 24410,
          "entitled": false
        }
      ]
    }
  ],
  "meta": {
    "permits": 64412,
    "builtAt": "2026-11-02T06:00:00+00:00",
    "stale": false,
    "planCoversPermits": true,
    "creditsUsed": 0,
    "dataset": "permits",
    "schemaVersion": "2026-09-16"
  }
}

The response shape on this page is the one the endpoint builds: the keys, their nesting, and their types are asserted against the shipping code by a test. The numbers inside it are the scenario being walked through, not a measurement of the corpus.

Warnings and outages

  • permits_missing_from_cache in meta.warnings: some matched permits could not be read just now and are not in the page. Nothing was charged for them; retry to pick them up.
  • 503 search_unavailable with a Retry-After header: permit search is unavailable for a moment. Nothing was charged.
  • 400 invalid_filter: error.details.problems names each filter value that was not understood, with the closest value we know.

Ready to make a call?

A free Boardwalk trial includes API access, 50 record-export credits (search, CSV, MCP), and 5 Find Contact Info lookups. Counting, taxonomy, location lookups, and the analytics plane cost nothing, so you can evaluate the data before you spend a credit.