# US Building Permits Scraper — 45 Cities, 22 States (`ai-lead-gen-tools/us-building-permits-scraper`) Actor

Issued US building permits from 45 jurisdictions in 22 states — NYC, Chicago, LA County, San Francisco, Austin, Seattle. One normalised trade vocabulary, valuation with its basis, contractor, address, dates, and a named reason for every empty field. Pick cities or states, pick a date window.

- **URL**: https://apify.com/ai-lead-gen-tools/us-building-permits-scraper.md
- **Developed by:** [Vladimir Chernykh](https://apify.com/ai-lead-gen-tools) (community)
- **Categories:** Lead generation, Real estate, Business
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.80 / 1,000 building permit records

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

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

## US Building Permits Scraper — Contractor, Trade & Valuation

Building permits from **45 open municipal permit systems in 22 US states**, normalised
to one record shape and one trade vocabulary. 12.6 million permits of history sit behind
these layers; a run returns the fresh ones.

This is a building permit *lead* feed, not a permit dump. Every record answers the four
questions a contractor, roofer, solar installer or supplier actually pays for: **who is
doing the work, what trade it is, how much it is worth, and where**.

### Why it is not just "permit records"

A jurisdiction's permit type is not a trade. Columbus writes
`Roof, Siding, Windows, Doors`, Nashville `Building Residential - New`, Anaheim
`Residential Remodel`. The field `workType` maps all of them onto one small vocabulary
(`roofing`, `solar`, `hvac`, `electrical`, `plumbing`, `new-construction`, `remodel`,
`demolition`, `signage`, `other`), which is what lets someone who sells roofs filter for
roofs in every city at once.

`valuation` never travels alone: `valuationBasis` says whether it is a declared job value
or a construction cost. A $450 000 construction cost and a $2 685 job value are not the
same number, and contractor totals never add them together silently — the portfolio row
sets `valuationBasisMixed` instead.

### Coverage — 45 jurisdictions, 22 states

| State | Jurisdictions | State | Jurisdictions |
|---|---|---|---|
| AZ | 3 | MD | 2 |
| CA | 5 | MI | 1 |
| CO | 4 | NC | 1 |
| CT | 1 | NM | 1 |
| FL | 4 | NY | 1 |
| ID | 1 | OH | 4 |
| IL | 2 | OR | 1 |
| KS | 1 | PA | 1 |
| KY | 1 | TN | 2 |
| LA | 1 | TX | 4 |
| MA | 1 | WA | 3 |

Every jurisdiction, its history size and the API it comes from:

| City ID | Jurisdiction | State | Permits on record | API |
|---|---|---|---|---|
| `us-tx-austin` | City of Austin | TX | 2 383 197 | Socrata |
| `us-ca-san-francisco` | City and County of San Francisco | CA | 1 297 749 | Socrata |
| `us-ny-nyc` | New York City | NY | 1 023 760 | Socrata |
| `us-ca-la-county` | Los Angeles County (unincorporated + contract cities) | CA | 928 761 | ArcGIS |
| `us-il-chicago` | City of Chicago | IL | 851 889 | Socrata |
| `us-oh-columbus` | City of Columbus | OH | 688 315 | ArcGIS |
| `us-fl-miami-dade` | Miami-Dade County | FL | 487 686 | ArcGIS |
| `us-wa-bellevue` | City of Bellevue | WA | 445 189 | ArcGIS |
| `us-la-new-orleans` | City of New Orleans | LA | 349 683 | Socrata |
| `us-co-boulder` | City of Boulder | CO | 337 797 | ArcGIS |
| `us-md-baltimore-city` | Baltimore City | MD | 295 869 | ArcGIS |
| `us-co-douglas` | Douglas County | CO | 286 727 | ArcGIS |
| `us-co-boulder-county` | Boulder County (unincorporated) | CO | 272 399 | ArcGIS |
| `us-md-bpds` | Maryland Building Permit Data System | MD | 257 533 | ArcGIS |
| `us-fl-miami` | City of Miami | FL | 236 389 | ArcGIS |
| `us-fl-alachua` | Alachua County | FL | 217 559 | ArcGIS |
| `us-oh-cleveland` | City of Cleveland | OH | 202 564 | ArcGIS |
| `us-wa-seattle` | City of Seattle | WA | 194 053 | Socrata |
| `us-ca-anaheim` | City of Anaheim | CA | 192 331 | ArcGIS |
| `us-nc-raleigh` | City of Raleigh | NC | 184 893 | ArcGIS |
| `us-oh-cincinnati` | City of Cincinnati | OH | 181 639 | Socrata |
| `us-ca-sacramento-county` | Sacramento County | CA | 159 396 | ArcGIS |
| `us-tx-san-antonio` | City of San Antonio | TX | 140 143 | ArcGIS |
| `us-wa-tacoma` | City of Tacoma | WA | 112 920 | ArcGIS |
| `us-az-coconino` | Coconino County | AZ | 111 645 | ArcGIS |
| `us-az-maricopa` | Maricopa County | AZ | 106 341 | ArcGIS |
| `us-ma-somerville` | City of Somerville | MA | 105 594 | Socrata |
| `us-fl-fort-pierce` | City of Fort Pierce | FL | 94 909 | ArcGIS |
| `us-nm-las-cruces` | City of Las Cruces | NM | 83 809 | ArcGIS |
| `us-tx-san-marcos` | City of San Marcos | TX | 51 811 | ArcGIS |
| `us-mi-detroit` | City of Detroit | MI | 47 998 | ArcGIS |
| `us-il-naperville` | City of Naperville | IL | 38 886 | ArcGIS |
| `us-ct-hartford` | City of Hartford | CT | 37 207 | ArcGIS |
| `us-tn-nashville` | Metro Nashville / Davidson County | TN | 29 661 | ArcGIS |
| `us-tn-memphis` | City of Memphis / Shelby County | TN | 28 404 | ArcGIS |
| `us-ky-louisville` | Louisville Metro / Jefferson County | KY | 22 639 | ArcGIS |
| `us-az-tempe` | City of Tempe | AZ | 20 744 | ArcGIS |
| `us-ks-overland-park` | City of Overland Park | KS | 14 197 | ArcGIS |
| `us-tx-kerrville` | City of Kerrville | TX | 11 098 | ArcGIS |
| `us-id-boise` | City of Boise | ID | 11 022 | ArcGIS |
| `us-ca-placentia` | City of Placentia | CA | 7 782 | ArcGIS |
| `us-oh-oxford` | City of Oxford | OH | 7 460 | ArcGIS |
| `us-co-aspen` | City of Aspen | CO | 6 553 | ArcGIS |
| `us-or-talent` | City of Talent | OR | 6 401 | ArcGIS |
| `us-pa-allentown` | City of Allentown | PA | 6 079 | ArcGIS |

37 layers are ArcGIS and 8 are Socrata. The Socrata half is what brought the largest
systems into reach, because none of them publish an ArcGIS layer at all.

Verified live on 2026-11-02: all 45 jurisdictions answered, zero failures, 9 123 permits
returned for a 7-day window ([measurement notes](../../../docs/permits/measurement-notes.md)).

### What you get — one record per permit

```json
{
  "source": "us-oh-columbus",
  "state": "OH",
  "permitId": "RSWDR2644234",
  "permitIdType": "accela-alt-id",
  "permitType": "1,2,3 Family",
  "permitSubtype": "Roof, Siding, Windows, Doors",
  "workType": "roofing",
  "status": "Permit Issued",
  "jurisdiction": "City of Columbus",
  "countyName": "Franklin",
  "countyFips": "39049",
  "issuedDate": "2026-10-31",
  "lastStatusDate": "2026-10-31",
  "valuation": 8000,
  "valuationBasis": "job-value",
  "situsAddress": {
    "street1": "3728 PENDLESTONE DR",
    "street2": null,
    "city": "Columbus",
    "state": "OH",
    "zip": "43230"
  },
  "parcelId": "600188600",
  "contractorName": "DTE ROOFING LLC",
  "scopeOfWork": "Additions, Alterations and Conversions - Residential",
  "sqft": 900,
  "units": 1,
  "completeness": "full",
  "omitted": [
    { "field": "ownerName", "reason": "not-published", "note": null },
    { "field": "appliedDate", "reason": "source-lacks-field", "note": "the layer publishes the issue date only" }
  ]
}
```

| Group | Fields |
|---|---|
| Identity | `permitId`, `permitIdType`, `permitType`, `permitSubtype` |
| Trade | `workType`, `scopeOfWork` |
| Money | `valuation`, `valuationBasis` |
| Dates | `appliedDate`, `issuedDate`, `finalizedDate`, `lastStatusDate` |
| Status | `status` |
| Place | `situsAddress` (`street1`, `street2`, `city`, `state`, `zip`), `parcelId`, `countyName`, `countyFips`, `jurisdiction`, `state` |
| People | `contractorName`, `contractorLicense`, `contractorPhone`, `ownerName` |
| Size | `sqft`, `units` |
| Provenance | `source`, `sourceApi`, `sourceLayerId`, `sourceUrl`, `scrapedAt`, `lookupType` |
| Honesty | `completeness`, `omitted[]` |

Two dataset views ship with it: **permits** (one row per permit) and **portfolios**
(one row per contractor, free of charge).

#### `omitted` — why a field is empty

A missing value is never ambiguous. Every `null` carries an entry saying *why*:

| Reason | Meaning |
|---|---|
| `source-lacks-field` | the jurisdiction publishes no such column |
| `not-published` | the column exists but is empty for this permit |
| `derived` | built from several columns (a glued address, a split ZIP) |
| `zero-means-unknown` | the source writes `0` where it means "not recorded" |

So Nashville's `status` is `null` with `source-lacks-field`, not `"cancelled"`. Anaheim
publishes a contractor phone on under 9 % of rows. Both facts are in the dataset.

### Inputs — cities, states, and dates

You choose **which jurisdictions** and **which dates**. All three selectors work together:

| Input | What it does |
|---|---|
| `cities` | pick exact jurisdictions by ID (`us-tx-austin`, `us-ny-nyc`, …); empty means all 45 |
| `states` | pick whole states (`CA`, `TX`, `FL`, …); combines with `cities`, a route listed twice is not fetched twice |
| `lastNDays` | window size in days, default **7** |
| `issuedAfter` / `issuedBefore` | exact window, `YYYY-MM-DD`; either alone is honoured, and they win over `lastNDays` |

```json
{ "states": ["TX", "FL"], "lastNDays": 7, "maxResultsPerCity": 1000 }
```

```json
{ "cities": ["us-ca-san-francisco", "us-il-chicago"], "issuedAfter": "2026-10-01", "issuedBefore": "2026-10-31" }
```

Four search modes run alongside the window:

| Input | Question it answers |
|---|---|
| `permitIds` | what exactly was filed under this permit number |
| `contractorNames` | everything this contractor built, plus a free portfolio row |
| `addresses` | every permit ever filed on this address |
| `mode: "new"` | what was issued since the last run (default) |

### Window and limits

The ceiling of the window is **today**, and it is not cosmetic: several layers carry rows
dated into the future, and a permit dated tomorrow has not been issued today — returning
it on every run would bill the same permit twice.

`maxResultsPerCity` (default 1000) is per city, so adding cities never shrinks the others.
A city that reaches it is marked `truncated` in the summary, which is how a cut-off stays
distinguishable from a quiet city. Two hard ceilings sit above it: `maxResultsPerCity`
cannot exceed 5000, and the whole run stops at 20 000 records.

The run summary lists, for every city, `returned`, `inWindow` and `truncated` — so a
buyer can always tell "this suburb issued nothing" from "we cut you off at 1000".

### Incremental mode

`mode: "new"` asks each layer for permits issued inside the window, newest first, and
filters out everything an earlier run already pushed. The seen-set lives in the named
store `us-building-permits-state`, one key per route, and the key deliberately does not
contain the window — putting `lastNDays` in the key makes every new window look like a
new search and re-charges the same permits.

Filtering happens before `maxResultsPerCity` is applied, so a page full of already-seen
permits cannot starve a run of the new ones behind them.

### Cost

**$5 per 1000 permits** — that is $0.005 per permit, and Apify adds its 20 % margin at
checkout. One charged event (`permit-scraped`) per distinct permit pushed. A lookup that
matches nothing is not charged, the contractor portfolio row is free, and a permit already
returned by an earlier run is never charged twice.

A 7-day run across all 45 jurisdictions returns roughly 9 000 permits, which is about $45.

### Honest limits

- Nine of the 45 feeds had nothing inside a 7-day window on 2026-11-02. That is the feed,
  not the actor: Boulder County's application feed lags six months, Aspen publishes closed
  permits only, Boise covers new housing and demolitions only. Each one still answers.
- Some columns are fees, not valuations (Bellevue `TOTALBILLED`, Coconino `TotalFees`,
  New Orleans `totalfees`, Cincinnati `fee`, Somerville `application_amount`) — they are
  **not** published as `valuation` and say so in `omitted`.
- A column named `owner_contractor` (Placentia) is published as neither, because the layer
  never says which of the two it is.
- New Orleans `bldgarea` holds only `0` or `4000`, so it is a class flag, not an area, and
  is not published as `sqft`.
- Somerville publishes building permits, farmers-market licences and block-party licences
  in one table. The route carries an internal filter for `department = 'ISD Building'`, so
  a permit lookup cannot return a licence that shares its digits.

### Search terms

`building permits`, `building permit scraper`, `construction permits`, `permit data`,
`US building permits`, `permit leads`, `contractor leads`, `roofing permits`,
`solar permits`, `HVAC permits`, `electrical permits`, `plumbing permits`,
`new construction permits`, `remodel permits`, `demolition permits`, `permit valuation`,
`job value`, `construction cost`, `issued permits`, `permit number lookup`,
`contractor license`, `contractor name`, `owner name`, `parcel number`, `APN`,
`property address`, `situs address`, `county FIPS`, `census tract`,
`New York City permits`, `NYC DOB permits`, `Austin permits`, `Chicago permits`,
`San Francisco permits`, `Los Angeles County permits`, `Seattle permits`,
`Columbus Ohio permits`, `Cleveland permits`, `Cincinnati permits`, `Miami-Dade permits`,
`Baltimore permits`, `Nashville permits`, `Memphis permits`, `Detroit permits`,
`Sacramento permits`, `Anaheim permits`, `Raleigh permits`, `Tacoma permits`,
`Bellevue permits`, `Boulder permits`, `Tempe permits`, `Maricopa County permits`,
`Coconino County permits`, `Douglas County permits`, `Alachua County permits`,
`lead generation`, `real estate data`, `construction intelligence`,
`building activity`, `permit feed`, `daily permit feed`, `incremental scraping`

### Run locally

```bash
npm run stage -- us-building-permits
cd packages/actors/us-building-permits
mkdir -p storage/key_value_stores/default
## then write storage/key_value_stores/default/INPUT.json
APIFY_LOCAL_STORAGE_DIR=./storage node src/main.js
```

`npm run stage` inlines `packages/core` into `src/core`; without it the import
`./core/index.js` does not resolve outside the workspace.

The input directory is `key_value_stores` — plural. The SDK v3 creates it itself and
silently ignores a hand-made `key_value_store`, so a run with a singular-name directory
starts on the defaults and looks exactly like "nothing matched": zero records, zero
misses, zero errors. That symptom means the input was not read; it never means the layers
are empty.

### Adding a jurisdiction

Adding a city is a row in [`src/routes.json`](src/routes.json), not a new parser, and a
test checks the registry. The mapper enforces three rules: only mapped columns are
requested (a missing column makes ArcGIS reject the whole query), `0` in a valuation
column is "unknown", and every `null` gets an entry in `omitted`.

### Tests

```bash
node --test packages/actors/us-building-permits/test/*.test.js
```

408 tests. The fixtures are real rows from the layers, including the awkward ones: the
zero valuation, the glued Anaheim address, the Nashville row with no status, the
Somerville licence that shares digits with a permit.

# Actor input Schema

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

Which jurisdictions to fetch. Leave empty for all 45. This is the precise control: a state can hold several unrelated permit systems, so picking a state is not the same as picking a city.

## `states` (type: `array`):

Limits the run by state and expands to every city served in that state. Use Cities when you want one jurisdiction. Leave both empty to query all 45.

## `lastNDays` (type: `integer`):

Window size when issuedAfter/issuedBefore are empty. 7 is the default because that is how fresh a permit lead has to be to be worth calling. A missed scheduled run is covered by permits already returned earlier: they are filtered out and never charged twice.

## `issuedAfter` (type: `string`):

Exact start of the window, YYYY-MM-DD. Overrides lastNDays when set. Use this and Issued before together to fetch one specific period.

## `issuedBefore` (type: `string`):

Exact end of the window, YYYY-MM-DD. Empty means today. Permits dated after today exist in some layers as data-entry typos, so the ceiling is applied even when you do not set one.

## `maxResultsPerCity` (type: `integer`):

How many permits one city may return. This is per city, not split between them, so adding cities never shrinks the others. A city that hits the limit is marked truncated in the run summary, so a cut-off is never mistaken for a quiet city.

## `permitIds` (type: `array`):

Search mode only. Permit numbers exactly as the issuing city writes them, e.g. PMLSM1602159 (Columbus) or BL320942 (Anaheim). Case is normalised automatically: the layers are case-sensitive and both forms are tried.

## `contractorNames` (type: `array`):

Search mode only. Full or partial contractor name, e.g. WATER HEATERS PLUS. Partial names are matched with LIKE, so SCHUMACHER finds SCHUMACHER ROOFING LLC. Each contractor search also returns one free portfolio row: how many permits, in which cities, how many still active, and the value of the work.

## `addresses` (type: `array`):

Search mode only. Street address of the work site. A city and ZIP may be included but are not required; the search falls back to the street line alone when the full address does not match the way the city stores it.

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

Leave empty and the mode is inferred: lists of permits, contractors or addresses mean search, an empty input means new. Set it explicitly if you want to be sure. new: every permit in the date window, ignoring the search lists. search: answer the search lists.

## `maxRecordsPerLookup` (type: `integer`):

Search mode only. How many permits one permit number, contractor or address may return. A contractor name can match hundreds of permits; raise this to see the history, lower it to keep the run small.

## `contractorPortfolio` (type: `boolean`):

For each contractor search, add one summary row with permit count, cities, active and closed counts, total valuation of the work, trades and the first and last permit date. This row is never charged and does not count towards any limit.

## `includeRaw` (type: `boolean`):

Keep the untouched attributes of the source layer on every record. Useful for debugging a jurisdiction; makes each record several times larger.

## `maxConcurrency` (type: `integer`):

Parallel requests within one city. Higher is faster and more likely to be rate limited by the city's server.

## `requestTimeoutSecs` (type: `integer`):

Timeout for a single request. Deep pages on a large layer can be slow.

## `chargePerRecord` (type: `boolean`):

Charge one event per permit pushed to the dataset. A search that matched nothing is never charged, and the contractor portfolio row is free.

## `runBudgetSecs` (type: `integer`):

How long this run may take before it stops and returns what it has. Leave it empty (recommended) and the run uses the whole timeout you set for it, so asking for every city and every state is not cut short. Set a number only to stop earlier. Stopping early is not a failure: the summary lists the cities it did not reach under `citiesSkippedForBudget`, and you can re-run for them.

## Actor input object example

```json
{
  "cities": [
    "us-tx-austin",
    "us-fl-miami",
    "us-wa-seattle"
  ],
  "states": [],
  "lastNDays": 7,
  "maxResultsPerCity": 1000,
  "permitIds": [],
  "contractorNames": [],
  "addresses": [],
  "maxRecordsPerLookup": 10,
  "contractorPortfolio": true,
  "includeRaw": false,
  "maxConcurrency": 2,
  "requestTimeoutSecs": 45,
  "chargePerRecord": true,
  "runBudgetSecs": 205
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per permit, plus one free summary row per contractor search (rowType = contractor_portfolio).

## `summary` (type: `string`):

What was queried, what each source answered, a per-jurisdiction capability and field audit, and the fields that were null on every record with the reason.

# 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": [
        "us-tx-austin",
        "us-fl-miami",
        "us-wa-seattle"
    ],
    "states": [],
    "lastNDays": 7,
    "issuedAfter": "",
    "issuedBefore": "",
    "maxResultsPerCity": 1000,
    "permitIds": [],
    "contractorNames": [],
    "addresses": [],
    "runBudgetSecs": 205
};

// Run the Actor and wait for it to finish
const run = await client.actor("ai-lead-gen-tools/us-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 = {
    "cities": [
        "us-tx-austin",
        "us-fl-miami",
        "us-wa-seattle",
    ],
    "states": [],
    "lastNDays": 7,
    "issuedAfter": "",
    "issuedBefore": "",
    "maxResultsPerCity": 1000,
    "permitIds": [],
    "contractorNames": [],
    "addresses": [],
    "runBudgetSecs": 205,
}

# Run the Actor and wait for it to finish
run = client.actor("ai-lead-gen-tools/us-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 '{
  "cities": [
    "us-tx-austin",
    "us-fl-miami",
    "us-wa-seattle"
  ],
  "states": [],
  "lastNDays": 7,
  "issuedAfter": "",
  "issuedBefore": "",
  "maxResultsPerCity": 1000,
  "permitIds": [],
  "contractorNames": [],
  "addresses": [],
  "runBudgetSecs": 205
}' |
apify call ai-lead-gen-tools/us-building-permits-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ai-lead-gen-tools/us-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/QFhwtDZDwJFNLa2Nb/builds/7M6KY3H3Rd3jOpqfi/openapi.json
