# Craigslist Scraper — Listings, Cars, Housing, Jobs (`nice_dev/craigslist-listings-scraper`) Actor

Scrape Craigslist in any of its 707 cities: for sale, cars & trucks, apartments, real estate, jobs, gigs, services. Keywords × cities × categories, every Craigslist filter, full listing details, phones, photos. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/craigslist-listings-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

### 🔎 What is Craigslist Scraper?

**Craigslist Scraper** extracts **listings from [Craigslist](https://www.craigslist.org)** in any of its **707 cities** (US, Canada, UK, Europe, Asia…): **for sale, cars & trucks, apartments and rooms, real estate, jobs, gigs, services and community posts** — with **title, price, photos, GPS, full description, every attribute (odometer, VIN, bedrooms, pets, salary…), the phone numbers and e-mails written in the ad, and the creation and update dates**.

Type a **keyword** (`bike`), pick a **city** (`sfbay`, `London`, `state:TX`) and a **category**, click **Start**, and download the listings in JSON, CSV or Excel. Several keywords × cities × categories in one run, every Craigslist filter in the form, past the 10,000 results Craigslist shows per search, and only the new listings on each scheduled run. No login, nothing to set up, and it is **fast: 100 listings with full details in 1 to 2 minutes on Apify**.

### 📋 What data can you extract from Craigslist?

One item per listing, 64 fields:

| Category | What you get |
| --- | --- |
| 🏷️ **Listing** | title, link, post id, category and its name, sold by owner or dealer — `2011 jeep grand Cherokee Laredo 4X4` |
| 💰 **Price** | price, currency of the city, price as shown, monthly payment of a dealer car — `$2,400` |
| 📍 **Place** | Craigslist city, its name, state and country, sub-area, neighborhood, GPS, map accuracy, street address and ZIP code |
| 🏠 **Housing** | bedrooms, bathrooms, area and unit, housing type, rent period, available from, amenities (laundry, parking, pets…) |
| 🚗 **Vehicles** | model year, make and model, odometer, VIN, condition, and every attribute: fuel, transmission, title status, color… |
| 💼 **Jobs and gigs** | compensation and the salary read from it (min, max, per hour / year), employer, job title, employment type, experience level |
| 📝 **Text and photos** | full description, every photo in large size, photo count, notices of the poster |
| 📞 **Contact** | phone numbers (option) and e-mails written in the ad, web links, reply page, whether Craigslist hides a contact |
| 🕒 **Dates** | creation date, last update (renewal) date — what the date filters read |

Every field, with an example, is listed in the **Output** section below.

Fields read on the listing page (description, attributes, address, creation date…) are filled when **Open each listing** is on (off by default). Left off, you get a search-results-only scrape: title, price, place, GPS, photos, update date, and the compensation, job title and odometer shown in the results (the employer too, with **Include the employer's name**).

### ✅ Why use Craigslist Scraper?

- 🌎 **707 cities, one run**: a city by its site name or its name, a sub-area (`sfbay/eby`), every city of a state (`state:CA`) or of a country (`country:GB`).
- 🗂️ **Keywords × cities × categories**: up to 500 searches per run, with a cap per search so that one big city cannot eat the whole budget; a listing found by several searches is saved once.
- 🎛️ **Every Craigslist filter**: price, bedrooms, pets, housing type, make and model, year, odometer, title status, employment type, remote, paid gigs… and any other filter of the site's search page. A filter that does not exist in the chosen category is refused before the run.
- 📈 **Past the 10,000-results cap**: Craigslist shows 10,000 listings per search; a bigger search is split by sub-area and price range until (nearly) every listing is reached.
- 📞 **Contact details and full text**: e-mails and, with **Include phone numbers**, phone numbers written in the ad, links, the reply page — plus the creation date, which the search results do not show.
- 🔔 **Monitoring built in**: tick **Only new listings**, schedule the Actor, and each run returns (and charges) only what it has never delivered before.
- 🔌 API, scheduling, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape Craigslist

1. Create a free Apify account.
2. Open **Craigslist Scraper**, type a **Search keyword** (e.g. `bike`), a **Location** (e.g. `sfbay`) and pick a **Category**.
3. Or paste your own Craigslist URLs into **Start URLs**: any search page (every filter set on the site is kept, all pages are read) or single listing pages.
4. Set **Max listings** (100 by default, 0 = no limit) — and **Max listings per search** when you run several searches — then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape Craigslist?

This Actor uses **pay per event** pricing (see the **Pricing** tab). You pay for the listings saved, at one of two prices, plus the options you tick:

| What you pay for | Event | Free plan | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- | --- |
| 1,000 listings with the search-result fields (Open each listing off, or a listing deleted since the search) | `result` | $0.032 | $0.028 | $0.022 | $0.017 |
| 1,000 listings with their page read: description, attributes, address, creation date | `result-with-details` | $1.19 | $1.18 | $1.17 | $0.89 |
| 1,000 listings dropped on their search-result line by one of the Actor's own filters (dates on the last update, excluded words) | `filter-check` | $0.032 | $0.028 | $0.019 | $0.01 |
| 1,000 listing pages read, then dropped by a filter decided on them (dates on the creation date, phone number required) | `detail-filter-check` | $0.14 | $0.13 | $0.12 | $0.11 |
| 1,000 employer's names, only with **Include the employer's name** (once per listing delivered that names one) | `company-found` | $0.68 | $0.67 | $0.66 | $0.65 |
| 1,000 listings with phone numbers, only with **Include phone numbers** (once per listing delivered with at least one) | `phone-found` | $1.79 | $1.78 | $1.77 | $1.76 |
| 1,000 listings already delivered that come back because their price moved, only with **Report price changes** (once per such listing, on top of the listing) | `price-change` | $0.72 | $0.71 | $0.70 | $0.69 |
| 1,000 listings already delivered, read again at the same price past the point where **Only new listings** alone stops (300 listings in a row you already have; with a cap, 80 % of it), only with **Report price changes**; a listing without a price is never charged one | `price-check` | $0.039 | $0.029 | $0.019 | $0.01 |
| 1,000 AI labels, only with **AI labels** (once per label you wrote, for each listing evaluated, whether the label applies or not) | `ai-label` | $4.49 | $4.48 | $4.47 | $4.46 |
| 1,000 zones of the market statistics, only with **Add market statistics by zone** (once per zone delivered) | `market-stats` | $9.79 | $9.78 | $9.77 | $9.76 |
| Run start | `apify-actor-start` | $0.01 per run | $0.01 per run | $0.01 per run | $0.01 per run |

A listing you keep never costs a filter fee. The employer's name, the phone numbers, the price changes, the AI labels and the market statistics are options, unticked by default: nothing is charged for them unless you tick them. Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

```json
{
    "query": "mountain bike",
    "location": "sfbay",
    "category": "sss",
    "maxItems": 200
}
```

Several searches, a cap per search, Craigslist filters, only the listings not delivered before:

```json
{
    "category": "apa",
    "locations": ["sfbay/sfc", "seattle", "state:OR"],
    "minBedrooms": 2,
    "maxPrice": 3500,
    "dogsOk": true,
    "maxItemsPerQuery": 50,
    "postedAfter": "3 days",
    "onlyNew": true,
    "stateKey": "west-coast-rentals"
}
```

Or with your own URLs:

```json
{
    "startUrls": [
        { "url": "https://sfbay.craigslist.org/search/cta?auto_make_model=toyota+tacoma&min_auto_year=2015" },
        { "url": "https://sfbay.craigslist.org/sfc/apa/d/sunny-1br/7973142960.html" }
    ],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `query`, `searchQueries` | Free-text search, exactly as typed on Craigslist (`mountain bike`); `searchQueries` adds more keywords (one search each). Ignored when `startUrls` is set. |
| `location`, `locations` | A Craigslist city: its site name (`sfbay`, `newyork`, `london`), its name (`San Francisco`, `Toronto`), a sub-area (`sfbay/eby`), every city of a state (`state:CA`) or of a country (`country:GB`). Empty = `sfbay`. Every keyword is searched in every city and category (max 500 searches per run). |
| `category` | The section to search: `sss` for sale, `cta` cars & trucks, `apa` apartments, `jjj` jobs, `ggg` gigs… (34 sections and groups). Empty = `sss`, unless `categories` is filled: then only those. |
| `categories` | More categories: any Craigslist category code, the one in the site's search URL (`bik`, `cto`, `sof`…). |
| `startUrls` | Craigslist search pages (filters kept, every page read) or single listing pages. Max 1,000 URLs. |
| `maxItems` | Stop after this many listings for the whole run (`0` = unlimited). |
| `maxItemsPerQuery` | Cap for EACH search (keyword × city × category, or search URL). `0` = no per-search cap. |
| `includeDetails` | Open each listing page for the description, every attribute, the address and the creation date (default off, charged at the higher listing-with-page price). |
| `splitLargeSearches` | Go past the 10,000 listings Craigslist shows per search by splitting it by sub-area and price range (default on). |
| `sort` | Newest (last update), oldest, price up or down, distance (with a ZIP code), relevance (with a keyword). |
| `minPrice`, `maxPrice`, `postalCode`, `searchDistance`, `titleOnly`, `hasImage`, `postedToday`, `hideDuplicates` | Craigslist filters of every category (price: the priced ones). |
| `sellerType`, `condition`, `freeOnly`, `deliveryAvailable`, `cryptoOk`, `makeModel` | For sale and vehicles: sold by owner or dealer, condition, free items, delivery, crypto, make and model. |
| `minBedrooms`, `maxBedrooms`, `minBathrooms`, `maxBathrooms`, `minSqft`, `maxSqft`, `catsOk`, `dogsOk`, `furnished`, `noSmoking`, `wheelchairAccessible`, `airConditioning`, `evCharging`, `housingType`, `laundry`, `parking`, `rentPeriod`, `availability` | Housing: rooms, area, pets, amenities, type, availability. |
| `minYear`, `maxYear`, `minMiles`, `maxMiles`, `titleStatus`, `transmission`, `fuel`, `drive`, `bodyType`, `paintColor`, `cylinders`, `minMonthlyPayment`, `maxMonthlyPayment` | Cars & trucks, motorcycles, RVs, boats. |
| `employmentType`, `telecommuting`, `internship`, `nonprofit`, `paidGigsOnly` | Jobs and gigs. |
| `siteFilters` | Any other filter of the Craigslist search page, as `name: value` pairs taken from the site's URL (`{"language": "5"}`). A filter Craigslist does not apply is reported in the log. |
| `postedAfter`, `postedBefore` | Creation date range: `2026-09-01`, or a period before now (`7 days`, `2 weeks`, `1 month`, `24 hours`). |
| `excludeKeywords` | Drop the listings whose title contains one of these words, also inside a longer word (`car` drops `carport`); case and accents ignored. |
| `requirePhone` | Keep only the listings whose text shows a phone number (needs the listing page; works with `includePhones` on or off, the numbers are only returned when it is on). |
| `includeCompany` | Add the employer named in the search results (job postings) to the employer column (default off, charged as **Company name found** for each listing that has one). |
| `includePhones` | Add the phone numbers written in the listing to the phones column (default off, charged as **Phone number found** for each listing that has at least one). Read in the title and the page text with the listing page, in the title alone without it. |
| `includeMarketStats` | Add the market figures of each zone of the run (default off, charged as **Zone market statistics** for each zone delivered): see the Output section. |
| `includePriceChanges` | With `onlyNew`: a listing already delivered comes back, whole, when its price moved, with the previous price, the change, the change in % and the date it was noticed (see Output). Charged as **Price change** per such listing (default off; refused without `onlyNew`). Also charged as **Price check** for each listing already known and re-checked for a price change, read past the point where `onlyNew` alone stops (after 300 listings in a row you already have; with a cap, 80 % of it); a listing without a price is never charged one, and an older listing that no run delivered stays out past that point. The search is read to its end (10,000 listings per search at most), so a run takes longer than `onlyNew` alone. |
| `aiLabels` | Up to 20 labels, one per line, each a concrete subject to look for in the listing: `clean title`, `brand new`, `low mileage`. `aiLabelMatches` lists the labels that apply, each with a score from 0 to 1. Writing the name, a colon, then a sentence says what you mean and works best (`utilities included: utilities are included in the rent`). A concrete subject works well; an abstract judgement (`scam`) is recognised badly. The title alone gives few results: tick **Open each listing** so the text of the listing page is read. Some subjects are still missed even when the listing says them plainly (for example a negotiable price written "OBO"): try your labels on a small run first. Charged as **AI label**, once per label you wrote for each listing evaluated, whether the label applies or not (default empty = off, nothing charged). Requires 2 GB memory: set it in the run options, with less the option is skipped and the log says so. |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the listings never delivered under this memory key; `resetState` forgets the memory. |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

A real listing (description and photo list shortened: `imageCount` counts every photo):

```json
{
    "id": "7973182530",
    "url": "https://newyork.craigslist.org/brk/apa/d/brooklyn-new-large-two-bedsc2345bq/7973182530.html",
    "title": "NeW LaRGe TWO BED..S,C,2,3,4,5,B,Q TRAIN..OUTDOOR SPACE..",
    "price": 2900,
    "currency": "USD",
    "priceText": "$2,900",
    "category": "apa",
    "categoryName": "apartments / housing for rent",
    "sellerType": null,
    "city": "newyork",
    "cityName": "new york city",
    "region": "NY",
    "country": "US",
    "subarea": "brk",
    "neighborhood": "Prospect heights",
    "latitude": 40.674398,
    "longitude": -73.957802,
    "mapAccuracy": 20,
    "mapAddress": "Park pl near Franklin Ave",
    "bedrooms": 2,
    "bathrooms": 1,
    "area": null,
    "areaUnit": null,
    "housingType": "apartment",
    "rentPeriod": "monthly",
    "availableFrom": null,
    "amenities": ["cats are OK - purrr", "dogs are OK - wooof", "laundry on site", "no parking"],
    "images": [
        "https://images.craigslist.org/00n0n_hoosYrRCATM_0lM0t2_1200x900.jpg",
        "https://images.craigslist.org/00Q0Q_hY9dC5wqHGS_0lM0t2_1200x900.jpg"
    ],
    "imageCount": 9,
    "updatedAt": "2026-09-26T13:26:34.000Z",
    "postedAt": "2026-09-26T13:26:34.000Z",
    "description": "WILL NOT LAST !!!\n\n$2900 ..\n\nAVAILABLE FOR October 15th or November 1st move in…",
    "address": { "street": "Park pl", "city": "Brooklyn", "postalCode": "11238", "region": "NY", "country": "US" },
    "postalCode": "11238",
    "attributes": { "application fee details": "20$ application fee", "rent period": "monthly" },
    "phones": ["3475967000"],
    "emails": [],
    "contactObfuscated": false,
    "links": [],
    "replyUrl": "https://newyork.craigslist.org/reply/nyc/apa/7973182530",
    "notices": [],
    "isDeleted": false,
    "searchUrl": "https://newyork.craigslist.org/search/apa",
    "searchQuery": null,
    "scrapedAt": "2026-09-26T13:33:52.110Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 64 fields

| Fields | What you get |
| --- | --- |
| `id`, `url`, `title` | post id, public listing page, title |
| `price`, `currency`, `priceText` | `2900`, `USD` (the city's currency), `$2,900` |
| `previousPrice`, `priceChange`, `priceChangePct`, `priceChangedAt` | **Price change** (option **Report price changes**, `null` on every other row) — `2600`, `-150`, `-5.77`, `2026-10-15T08:00:00.000Z` (when this run noticed it: Craigslist does not say when the seller changed the price) |
| `category`, `categoryName`, `sellerType` | `cto`, `cars & trucks - by owner`, `owner` or `dealer` when the category tells it |
| `city`, `cityName`, `region`, `country`, `subarea`, `neighborhood` | `newyork`, `new york city`, `NY`, `US`, `brk`, the place written by the poster |
| `latitude`, `longitude`, `mapAccuracy`, `mapAddress` | map position, its accuracy (higher = vaguer), the address line under the map |
| `bedrooms`, `bathrooms`, `area`, `areaUnit`, `housingType`, `rentPeriod`, `availableFrom`, `amenities` | housing: `2`, `1`, `650`, `ft2`, `apartment`, `monthly`, `2026-10-04`, laundry / parking / pets |
| `images`, `imageCount` | photo URLs in large size (1200×900), their count |
| `updatedAt`, `postedAt` | last update (renewal) from the results, creation date from the listing page |
| `description`, `address`, `postalCode`, `attributes` | full text, street / city / ZIP / state / country, every `label: value` of the listing page |
| `phones`, `emails`, `contactObfuscated`, `links`, `replyUrl`, `notices` | phones (with `includePhones`) and e-mails written in the text, whether Craigslist hides a contact, web links, reply page, poster notices |
| `year`, `makeModel`, `odometer`, `vin`, `condition`, `monthlyPayment` | vehicles: `2017`, `mercedes-benz s550`, `130000`, VIN, `excellent`, dealer's monthly payment |
| `compensation`, `employmentType`, `jobTitle`, `experienceLevel`, `companyName`, `salaryMin`, `salaryMax`, `salaryPeriod` | jobs and gigs: `$17.50 - $22`, `part-time`, `Housekeeper`, `entry level`, employer (with `includeCompany`), `17.5`, `22`, `hour` |
| `isDeleted` | `true` when the listing was deleted, flagged or expired between the search and its page (the search-result fields are saved) |
| `aiLabelMatches` | **AI label** (option **AI labels**, `null` on every other row) — the labels you wrote that apply to the title and the text, each with its score: `[{"label": "clean title", "score": 0.91}]`; `[]` when none applies, `null` when the listing was not evaluated (no text, or the run's maximum cost or end came first) |
| `searchUrl`, `searchQuery`, `scrapedAt` | the Craigslist search it was found by, its keyword, ISO timestamp |

#### Market statistics by zone

Tick **Add market statistics by zone** and the run adds ONE record to its key-value store, `MARKET_STATS` (linked in the Output tab), worked out from the listings the run delivered: no page is read for it and nothing is added to the dataset. A zone is a city, a category, a housing type (rentals and real estate) and a currency: two currencies are never mixed. A zone needs at least 5 listings with a price (a price of 0 or 1 is not a price); up to 20 zones, the biggest first; each zone delivered is charged once.

Each zone gives the number of listings and of priced listings, the median, minimum, maximum and quartiles of the price, and the 5 cheapest listings (the best deals) with their gap in % to the median of what they are ranked on. In housing zones the deals are ranked by price per unit of area, one unit at a time (square feet and square metres are never mixed: each unit has its own quartiles, and the ranking uses the unit that has the most listings, with at least 5); a listing without an area is not ranked there. Everywhere else, or when a housing zone has too few areas, the deals are ranked by total price. `rankedBy` tells which. With **Only new listings**, only the listings delivered by this run count.

### 💡 Tips

#### How to get more results

Set `maxItems` to `0`, leave the keyword empty and keep **Go past 10,000 results per search** on: a search Craigslist caps at 10,000 listings is split by sub-area, then by price range (measured: 17,037 of 17,037 SF bay cars by sub-area, 10,178 of 10,202 Sacramento cars by price range). Several cities at once: `state:CA`, `country:US` (413 cities).

#### How to reduce costs

The price is per listing, so the levers are `maxItems`, `maxItemsPerQuery`, the Craigslist filters (applied by Craigslist itself, free), the Actor's own filters and `onlyNew` for recurring runs (you never pay twice for the same listing). **Open each listing** is off by default: the lower search-result price is charged; turning it on charges the listing-with-page price.

#### Several searches in one run

Fill `searchQueries`, `locations` and / or `categories`: the Actor runs one search per keyword × city × category (3 keywords × 4 cities × 2 categories = 24 searches, up to 500 per run). The single `query`, `location` and `category` fields still work and are added to the lists. A listing found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap: without it the first searches can use up the whole `maxItems` budget. You can also paste several search URLs into `startUrls`: each one is a search of its own, with the same cap.

#### Monitoring: only the new listings

Tick **Only new listings** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the listings already delivered: they are not saved, not charged, and their page is not even opened. The memory lives in a named key-value store of your account (`craigslist-listings-scraper-seen`, up to 150,000 listings per key) and is only updated with listings that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's listings), and tick `resetState` once to start over. The searches you build in the form are then sorted by newest (a pasted search URL keeps its own sort). A search stops once it meets 300 listings in a row you already have — with a cap (`maxItems`, `maxItemsPerQuery`), once it has met 80 % of that cap in listings you already have, since the run before delivered no more than the cap. A listing its poster renewed since the last run comes back to the top of Craigslist: it is skipped, not charged, and not counted in those. A listing your filters drop is checked (and charged the filter check) once: the next runs with the same filters skip it for free; change a filter, or tick `resetState`, to check them again. One exception: a listing dropped for being newer than `postedBefore` may pass later (a relative bound such as `30 days` moves forward), so each run checks it, and charges the filter check, again.

**Price changes.** Tick **Report price changes** (`includePriceChanges`) next to **Only new listings** and the memory also keeps the last price of every listing. A listing already delivered whose price moved comes back as a full row with `previousPrice`, `priceChange`, `priceChangePct` and `priceChangedAt`, charged as a **Price change**; a hidden price, a price of 0 or 1 is never a change, and a listing in another currency is learned again, not reported. The first run after you tick it learns the prices (no change is reported); from the next one on, changes are reported. Because an old listing can change its price, such a search is read to its end (up to 10,000 listings per search) instead of stopping where **Only new listings** alone stops (300 listings in a row that you already have; with a cap, 80 % of the cap): it takes longer, each known listing it re-checks beyond that point is charged a **Price check** (a listing without a price is read, never charged), an older listing that no run delivered stays out beyond that point, as with **Only new listings** alone, and `maxItems` counts the listings delivered (new or changed). The stop on `postedAfter` stays.

#### Filter by creation date

`postedAfter` and `postedBefore` take a date (`2026-09-01`, the whole day is included, UTC) or a period before now (`7 days`, `2 weeks`, `1 month`; via the API also `24 hours` or a full ISO date-time). With **Open each listing** the filter reads the creation date of the listing page; without it, the last update date of the search results (a listing renewed recently counts as new). Filtered-out listings are not saved and do not count in `maxItems`; the run summary tells how many were filtered. With `postedAfter`, searches are sorted by newest and stop at the first batch of results that is entirely too old.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per Craigslist listing. Actor id: `nice_dev/craigslist-listings-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/craigslist-listings-scraper`.

Smallest input, for a cheap first call:

```json
{
    "query": "bike",
    "location": "sfbay",
    "maxItems": 10
}
```

Key output fields: `url`, `title`, `price`, `currency`, `city`, `latitude`, `longitude`, `updatedAt`, and with `includeDetails` `description`, `attributes`.

Cost: see the pricing section above (one price per listing with the search-result fields, one with the listing page read). Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Craigslist?

The Actor only reads what Craigslist shows publicly to any anonymous visitor. It logs in to nothing, solves no captcha and never opens « show contact info » or the e-mail relay: a phone number or e-mail hidden there is returned only when the public page itself already carries it. Listings are posted by private persons: results can contain personal data (names, phone numbers and e-mails written in an ad), which is protected by laws such as the GDPR and the CCPA — do not store or use it without a legitimate reason, and do not send unsolicited messages. You are responsible for using the data in compliance with Craigslist's Terms of Use and applicable law. This Actor is not affiliated with craigslist, inc.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request the site turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds).

#### Is the data safe to open in Excel or to show on a web page?

Titles and descriptions are the posters' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@`: Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. Phone numbers are digits only (`3475967000`, `+442079460958`): import them as text to keep their leading zeros. Every URL field (`url`, `images`, `links`, `replyUrl`, `searchUrl`) holds an http(s) URL or null. On a web page, escape every text field like any text written by a stranger.

#### Known limitations

- Craigslist shows at most 10,000 listings per search; past it the search is split (see Tips). A search that cannot be split (a single price, jobs in a city without sub-areas) gives its newest 10,000.
- With `onlyNew` and a cap (`maxItems`, `maxItemsPerQuery`) smaller than a search over 10,000 listings, the search is not split: each run reads its newest 10,000, so a run right after never returns older listings the cap had left. Leave the cap empty for a first run that reaches every listing. A `postedBefore` that leaves out some of those newest 10,000 (`7 days` on a busy city) still splits the search: its listings are further down.
- Split by price range, the listings without a price come from one more search that keeps only them: its newest 10,000 (a price range of your own leaves them out, as on Craigslist).
- `description`, `attributes`, `address` and `postedAt` come from the listing page: they are empty when **Open each listing** is off. `phones` is empty unless **Include phone numbers** is on, and then holds the numbers of the title alone when the listing page is not read.
- A phone number or e-mail Craigslist hides behind « show contact info » is only returned when the page itself still carries it (common on for-sale and car listings).
- `onlyNew` remembers listing ids, not their content: a listing whose price changed is not returned again, unless **Report price changes** is ticked (then only a change of price brings a listing back).
- Two runs sharing the same `stateKey` at the same time may both return the same new listing.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the listings already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the listings it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the listings of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the listings saved, filtered out and no longer on Craigslist (removed while the run was reading them), and the requests that failed after every retry. Those requests and the removed listings are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause (a search Craigslist refuses says so, instead of "run it again"). A Craigslist filter the site does not apply in the chosen category is reported in the log.

If Craigslist changes its pages, you are told instead of paying for blank rows. A search that counts listings but gives none the Actor can read is an error (listed in `FAILED_REQUESTS`), never a quiet "No listings found". If the first 20 listings read all lack their title, update date, city, category, price (when the result shows one) or — with **Open each listing** — creation date, description or attributes (cars, apartments, jobs, gigs), the run saves nothing more, stops and fails, and its last message names the missing field: at most those first listings are charged. A listing that `postedAfter` / `postedBefore` drops because it has no date at all counts among those 20. A run of fewer listings (a scheduled `onlyNew` run that finds a few new ones, a small **Max listings**) is checked at its end, from 5 listings: its listings are already saved, and the run fails with the missing field instead of ending green.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

## `startUrls` (type: `array`):

Craigslist search-result URLs (`https://sfbay.craigslist.org/search/apa?min_price=2000…` — every filter set on the site is kept, all pages are read) or single listing URLs (`https://sfbay.craigslist.org/sfc/apa/d/sunny-1br/7973142960.html`, `https://www.craigslist.org/view/d/sunny-1br/b82qR4cCT97BWdKwmPhjBA`). When this list is not empty, the search fields below (keywords, locations, categories) are ignored; filters, caps and monitoring still apply. Max 1 000 URLs.

## `query` (type: `string`):

Free-text search, exactly as typed on Craigslist (e.g. `mountain bike`). Empty = every listing of the category.

## `searchQueries` (type: `array`):

Several keywords in one run: one search per keyword (times each location below). Added to **Search keyword**; listings found by several searches are saved once.

## `category` (type: `string`):

Craigslist section to search. `— all` groups the by-owner and by-dealer listings. Empty = `sss` (all for sale), unless **More categories** is filled: then only those.

## `categories` (type: `array`):

Several categories in one run, as Craigslist abbreviations (`apa`, `cto`, `bik`, `sof`… — the code in the site's URL `/search/<code>`). Added to **Category** (leave it empty to search only these); each keyword is searched in each category and each location (max 500 searches).

## `location` (type: `string`):

Craigslist city: its site name (`sfbay`, `newyork`, `london`), its name (`San Francisco`, `Toronto`) or a sub-area (`sfbay/eby`, `newyork/brk`). Also `state:CA` (every city of a US state / Canadian province) or `country:GB` (every city of a country). Empty = `sfbay`.

## `locations` (type: `array`):

Several cities in one run, same formats as **Location** (`seattle`, `sfbay/sfc`, `state:TX`, `country:US`). Every keyword is searched in every city and category (`country:US` alone = 413 cities; max 500).

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

Maximum number of listings to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (keyword × location, or search URL), so that the first search cannot use up the whole **Max listings** budget. 0 = no per-search cap.

## `includeDetails` (type: `boolean`):

Open every listing page for the full description, all photos, every attribute (odometer, VIN, bedrooms, pets, compensation…), the street address, the creation date and the e-mails written in the text (phone numbers: see **Include phone numbers**). Off by default = search-result fields only (title, price, GPS, first photo, update date), faster and cheaper (see Pricing).

## `splitLargeSearches` (type: `boolean`):

Craigslist stops a search at 10 000 listings. When a search has more, split it automatically by price range (and by sub-area) until every listing is reached. Off = the first 10 000 only.

## `sort` (type: `string`):

Order of the results. `Newest` = last update (reposted listings come back to the top). Default: newest, or most relevant with a keyword.

## `minPrice` (type: `integer`):

Lowest price, in the city's currency.

## `maxPrice` (type: `integer`):

Highest price, in the city's currency.

## `postalCode` (type: `string`):

Search around this ZIP / postal code (with **Search distance**).

## `searchDistance` (type: `integer`):

Radius around the ZIP code, in the city's distance unit (miles in the US).

## `titleOnly` (type: `boolean`):

Match the keyword in the title only (`srchType=T`).

## `hasImage` (type: `boolean`):

Only listings with at least one photo.

## `postedToday` (type: `boolean`):

Only listings posted or updated today (Craigslist's own filter).

## `hideDuplicates` (type: `boolean`):

Craigslist's `bundle duplicates`: one listing per group of near-identical posts.

## `sellerType` (type: `string`):

For-sale, cars and real-estate categories: owner (private seller) or dealer / broker.

## `condition` (type: `array`):

For-sale and vehicle categories.

## `freeOnly` (type: `boolean`):

For-sale categories: only listings given away for free (Craigslist's `free` filter).

## `deliveryAvailable` (type: `boolean`):

For-sale categories.

## `cryptoOk` (type: `boolean`):

For-sale categories.

## `minBedrooms` (type: `integer`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `maxBedrooms` (type: `integer`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `minBathrooms` (type: `integer`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `maxBathrooms` (type: `integer`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `minSqft` (type: `integer`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category. In the city's area unit (ft² in the US).

## `maxSqft` (type: `integer`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category. In the city's area unit (ft² in the US).

## `catsOk` (type: `boolean`):

Housing-for-rent categories only (apa, roo, sub, hhh…); refused in any other category.

## `dogsOk` (type: `boolean`):

Housing-for-rent categories only (apa, roo, sub, hhh…); refused in any other category.

## `furnished` (type: `boolean`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `noSmoking` (type: `boolean`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `wheelchairAccessible` (type: `boolean`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `airConditioning` (type: `boolean`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `evCharging` (type: `boolean`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `housingType` (type: `array`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `laundry` (type: `array`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `parking` (type: `array`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `rentPeriod` (type: `array`):

Housing-for-rent categories only (apa, roo, sub, hhh…); refused in any other category.

## `availability` (type: `string`):

Housing categories only (apa, roo, sub, hhh, rea…); refused in any other category.

## `makeModel` (type: `string`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category. E.g. `toyota tacoma`.

## `minYear` (type: `integer`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `maxYear` (type: `integer`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `minMiles` (type: `integer`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `maxMiles` (type: `integer`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `titleStatus` (type: `array`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `transmission` (type: `array`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `fuel` (type: `array`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `drive` (type: `array`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `bodyType` (type: `array`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `paintColor` (type: `array`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `cylinders` (type: `array`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `minMonthlyPayment` (type: `integer`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `maxMonthlyPayment` (type: `integer`):

Cars & trucks / motorcycles / RVs / boats only; refused in any other category.

## `employmentType` (type: `array`):

Jobs only.

## `telecommuting` (type: `boolean`):

Jobs only.

## `internship` (type: `boolean`):

Jobs only.

## `nonprofit` (type: `boolean`):

Jobs only.

## `paidGigsOnly` (type: `string`):

Gigs only.

## `siteFilters` (type: `object`):

Any other filter of the Craigslist search page, as `name: value` pairs taken from the site's URL (e.g. `{"language": "5", "auto_cylinders": ["2", "4"]}`). Filters Craigslist does not echo back are reported in the log.

## `postedAfter` (type: `string`):

Only listings created on or after this date: `2026-09-01`, or a period before now such as `7 days`, `2 weeks`, `1 month` (API: `24 hours` and full ISO date-times work too). Reads the creation date of the listing page when details are on, otherwise the last update date shown in the search results (a reposted listing counts as new).

## `postedBefore` (type: `string`):

Only listings published on or before this date (the whole day is included), or older than a period such as `30 days`.

## `excludeKeywords` (type: `array`):

Drop the listings whose title contains one of these words, also inside a longer word (`car` drops `carport`); case and accents ignored.

## `requirePhone` (type: `boolean`):

Keep only the listings whose text shows at least one phone number (read on the listing page: needs **Open each listing**). It works with **Include phone numbers** on or off; the numbers themselves are only returned when it is on.

## `includeCompany` (type: `boolean`):

Fill `companyName` with the employer named in the search results (job postings; most other categories name none). Charged per listing as **Company name found**, once per listing delivered with a name, on top of the listing and charged separately, see Pricing; a listing without a name costs nothing extra. No page is opened for it. Off (default) = `companyName` is always null and nothing is charged.

## `includePhones` (type: `boolean`):

Fill `phones` with the phone numbers written in the listing. Charged per listing as **Phone number found**, once per listing delivered with at least one number (however many it has), on top of the listing and charged separately, see Pricing; a listing without a number costs nothing extra. With **Open each listing** on, the numbers are read in the title and in the text of the page; with it off, only in the title (a few listings write their number there). The Actor never opens « show contact info »: a number hidden there is returned only when the page itself already carries it (common on for-sale and car listings). Off (default) = `phones` is always empty and nothing is charged; **Only listings with a phone number** still works.

## `includeMarketStats` (type: `boolean`):

Add one record, `MARKET_STATS`, to the run's key-value store (linked in the Output tab): the market figures of each zone — a city, a category, a housing type (rentals and real estate) and a currency, never two currencies in one zone — worked out from the listings this run delivers: number of listings and of priced listings, median, minimum, maximum and quartiles of the price, and the 5 cheapest listings with their gap in % to the median. In housing zones they are ranked by price per unit of area, one unit at a time (square feet and square metres are never mixed); elsewhere, or when a housing zone has too few areas, by total price. A price of 0 or 1 is not counted as a price. Nothing is added to the dataset and no page is read. Charged per zone delivered as **Zone market statistics**, charged separately, see Pricing. A zone needs at least 5 priced listings; up to 20 zones, the biggest first. Off (default) = no record and nothing is charged. With **Only new listings**, only the listings delivered in this run count: raise **Max listings** for a sample worth a zone.

## `includePriceChanges` (type: `boolean`):

Needs **Only new listings**. A listing already delivered under this **Memory key** comes back, whole, when its price has changed since the run that delivered it: `previousPrice`, `priceChange`, `priceChangePct` and `priceChangedAt` are filled. Charged per listing as **Price change**, on top of the listing and charged separately, see Pricing; a listing whose price did not move is skipped and costs nothing, a hidden price (or a price of 0 or 1) is never a change. The memory also keeps the last price of every listing: the first run after you tick it learns the prices and reports no change. The search is read to its end (10,000 listings per search at most) because an older listing can change its price: a run takes longer than with **Only new listings** alone, which stops after 300 listings already delivered in a row (with a cap, after 80 % of the cap). Past that point, each listing already known and re-checked for a price change is charged a **Price check** (see Pricing; a listing without a price is read, never charged), an older listing that no run delivered stays out, as with **Only new listings** alone, and the run stops if its maximum cost cannot pay one. Off (default) = the four columns are always null, the search stops as before and nothing is charged.

## `aiLabels` (type: `array`):

Topics to look for in each listing, one per line (up to 20 of 300 characters). The title and the text are read and `aiLabelMatches` lists the topics that apply, each with a score from 0 to 1. Give CONCRETE subjects, for example `clean title`, `brand new` or `low mileage`: an abstract judgement such as `scam` is recognised badly. Writing the name, a colon, then a sentence says what you mean and works best (`utilities included: utilities are included in the rent`). The title alone gives few results: tick **Open each listing** so the text of the listing page is read. Some subjects are still missed even when the listing says them plainly (for example a negotiable price written "OBO"): try your labels on a small run first. Charged as **AI label**, once per label you wrote for each listing it was asked on, whether the label applies or not, on top of the listing and charged separately, see Pricing; a listing with no text, one whose labels the run's maximum cost cannot all pay, or one written while the run is ending, is not evaluated and costs nothing extra. The option needs 2 GB of memory: with less it is skipped, with a warning, and nothing is charged. Off (default) = no label written, `aiLabelMatches` is always null and nothing is charged.

## `onlyNew` (type: `boolean`):

Skip the listings that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and their page is not even opened. First run = everything is new.

## `stateKey` (type: `string`):

Name of the memory used by **Only new listings**. Give each schedule / task its own key (e.g. `paris-rentals`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every listing again. Untick it afterwards.

## `proxyConfiguration` (type: `object`):

Apify Proxy or your own proxies. Keep the default: it is included in the price. The residential Apify proxy is not available in this Actor.

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

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Most requests to the site in any 60 seconds, all of them counted: results pages, listing pages and the extra page read for a listing. A budget, not an even pace: up to this many can leave at once when the minute starts (spread them with the minimum delay below). Lower it if the site answers HTTP 429 / 403 in the log.

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. Unlike the per-minute rate, this spreads the requests evenly instead of letting them go out in a burst. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed. Behind a proxy, a request the site turns away is also retried on a new proxy session up to 10 times without using up these retries.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "query": "bike",
  "searchQueries": [],
  "categories": [],
  "location": "sfbay",
  "locations": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "includeDetails": false,
  "splitLargeSearches": true,
  "titleOnly": false,
  "hasImage": false,
  "postedToday": false,
  "hideDuplicates": false,
  "sellerType": "all",
  "condition": [],
  "freeOnly": false,
  "deliveryAvailable": false,
  "cryptoOk": false,
  "catsOk": false,
  "dogsOk": false,
  "furnished": false,
  "noSmoking": false,
  "wheelchairAccessible": false,
  "airConditioning": false,
  "evCharging": false,
  "housingType": [],
  "laundry": [],
  "parking": [],
  "rentPeriod": [],
  "availability": "all",
  "titleStatus": [],
  "transmission": [],
  "fuel": [],
  "drive": [],
  "bodyType": [],
  "paintColor": [],
  "cylinders": [],
  "employmentType": [],
  "telecommuting": false,
  "internship": false,
  "nonprofit": false,
  "paidGigsOnly": "all",
  "siteFilters": {},
  "excludeKeywords": [],
  "requirePhone": false,
  "includeCompany": false,
  "includePhones": false,
  "includeMarketStats": false,
  "includePriceChanges": false,
  "aiLabels": [],
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 8,
  "maxRequestsPerMinute": 600,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# Actor output Schema

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

No description

## `marketStats` (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 = {
    "query": "bike",
    "location": "sfbay",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/craigslist-listings-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 = {
    "query": "bike",
    "location": "sfbay",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/craigslist-listings-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 '{
  "query": "bike",
  "location": "sfbay",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/craigslist-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/craigslist-listings-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/m0OlTASMh3kqEceAY/builds/zJFgoaXVhf7gw1HWc/openapi.json
