# Craigslist Scraper: Listings, Prices & Alerts (`automation_craft/craigslist-scraper`) Actor

Scrape Craigslist postings from any of its 707 areas, a whole country or a ZIP code radius, no login, past the 10,000 row cap. Rows hold title, price, place, coordinates, posted time, images, description, VIN, odometer, bedrooms and pay. New post alerts; JSON, CSV, Excel or API; pay per posting.

- **URL**: https://apify.com/automation_craft/craigslist-scraper.md
- **Developed by:** [Automation Craft](https://apify.com/automation_craft) (community)
- **Categories:** E-commerce, Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.47 / 1,000 listings

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

### Craigslist Scraper: Listings, Prices & Alerts

**Craigslist Scraper** turns Craigslist postings into clean rows: one row per posting with the title, price, place,
coordinates, posted and updated time, category, every image link and, with details (on by default, same price), the full
description and every attribute Craigslist shows (cars: make and model, year, odometer, VIN, title status, fuel,
transmission; housing: bedrooms, bathrooms, size, laundry, parking, pets; jobs: pay, employment type). Search any of
Craigslist's 707 areas and 103 sub areas, whole countries (the United States in six searches), or a ZIP code or coordinates
with a radius of up to 1,000 miles, in any of 228 categories, with every Craigslist filter. Paste search links or posting
links if you have them. No login, no browser.

Searches larger than Craigslist's 10,000 row limit are split into price slices on their own, and every search ends with a
coverage row that says how many postings Craigslist counted and how many were read (a run of very many searches that find
little writes the coverage rows past its free row budget as counts in the summary). Name a **monitor** and schedule the
Actor: each run delivers and bills only postings it has not delivered before, tracks price changes, reposts and removed
postings, and can send them to Telegram, Slack, Discord, WhatsApp or your webhook.

#### Who uses it

- **Car dealers, flippers and buyers** watching cars and trucks by owner or by dealer, with make and model, year,
  odometer, title status and VIN, across one area or the whole US.
- **Landlords, property managers and renters** comparing rents, bedrooms, size, pets, laundry and parking in a city or
  within a radius of a ZIP code.
- **Lead and sales teams** reading dealer and company postings, which come with their phone numbers and addresses as
  posted, and ranking postings with a lead score.
- **Analysts and developers** who need Craigslist as rows in JSON, CSV or Excel through the Apify API, with the same field
  names in every category and country.

#### Features

| Feature | Settings | Event billed |
|---|---|---|
| Areas by hostname, name or sub area ("sfbay", "SF bay area", "sfbay/sby", "south bay area"); all 707 areas and 103 sub areas | `areas` | Listing |
| Whole countries: "US" is six searches of 1,000 miles that cover every US area (each posting once, other countries left out); any other country is every area of it | `countries` | Listing |
| US ZIP codes or coordinates with a radius of 1 to 1,000 miles, across every area | `locations`, `radiusMiles` | Listing |
| 228 categories by code or name (sections, groups, "by owner" and "by dealer" leaves); for sale when none is given | `categories` | Listing |
| Keywords in Craigslist's own syntax (`iphone -case`, `(honda | toyota)`, quotes), each its own search | `keywords` | Listing |
| Pasted search links of every form (area, sub area, city with a ZIP code or coordinates), every parameter checked | `searchUrls` | Listing |
| Posting links (classic and `/view/d/` links) or posting ids of 22 letters and digits, read with their details | `postingUrls` | Listing |
| Every Craigslist filter: price, posted today, with images, titles only, hide duplicates, free stuff, sold by owner or dealer, condition, delivery, crypto; cars (make and model, year, odometer, title status, transmission, fuel, body type, drive, cylinders, paint, monthly payment); housing (bedrooms, bathrooms, size, housing type, laundry, parking, rent period, availability, cats, dogs, furnished, no smoking, wheelchair access, air conditioning, EV charging, no application fee, no broker fee, private room, private bath); jobs (employment type, telecommuting, internship, non profit); paid gigs; event, garage sale and open house dates | see Input | no charge |
| Any other Craigslist filter by its own name, with a value by code or label | `filters` | no charge |
| Row filters: words to keep or leave out, a posted window, postings you already have, custom rules on output fields, nearby area postings in or out | `includeKeywords`, `excludeKeywords`, `postedWithinHours`, `postedWithinDays`, `postedAfter`, `postedBefore`, `excludePostingIds`, `customFilters`, `includeNearbyAreas` | no charge |
| 7 sorts; caps per run and per search; how deep each search is read | `sort`, `maxItems`, `maxItemsPerSearch`, `maxRowsPerSearch` | no charge |
| Past 10,000 rows a search: whole dollar price slices | `splitLargeSearches` (on) | no charge |
| Continue an interrupted run without paying for a posting twice | `resumeFromDatasetId` | no charge |
| Full details: description, every image, every attribute, decoded VIN data, repost mark, updated time, map accuracy | `includeDetails` (on) | included in Listing |
| A monitor: only postings new to it, run after run | `monitorName` | Listing (new postings only) and Results page |
| Price and field changes, removed and reappeared postings | `trackChanges`, `trackFields` (with a monitor) | Listing update |
| Every known posting again; reposts marked or left out | `emitUnchanged`, `markReposts`, `skipReposts` | Listing |
| Notifications: Telegram, Slack, Discord, WhatsApp Cloud API, webhook (HMAC SHA-256 signature, extra headers) | see The monitor and notifications | free |
| Labels in 15 languages; image size, description format and length, compact rows, empty fields left out, raw Craigslist data | `language`, `imageSize`, `descriptionFormat`, `descriptionMaxLength`, `compactOutput`, `excludeEmptyFields`, `includeRawData` | included |
| A lead score from 0 to 100 with the signals behind it | always | included |
| A private poster's phone numbers, e-mail addresses, street address and exact coordinates (off by default) | `includePersonalData` | free |
| A coverage row per search, free status rows for misses, a run summary and an HTML run report (key `REPORT`) | always | free |

### Quick start

1. Pick one or more Craigslist areas (for example `sfbay`, `newyork` or `houston`), or type `US` in **Whole countries**.
2. Pick categories (for example `cta` cars and trucks, `apa` apartments, `jjj` jobs; for sale when left empty) and, if you
   like, keywords.
3. Optional: pick filters (price, sold by owner or dealer, car year and odometer, bedrooms, pets).
4. Set **Maximum postings** and press **Start**.
5. Download the rows as JSON, CSV or Excel, or read them through the API.

Example input, the Actor's own prefill (cars and trucks in the San Francisco bay area):

```json
{
    "areas": ["sfbay"],
    "categories": ["cta"],
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": true }
}
```

Cars by owner in Houston, 2015 or newer, at most 100,000 miles, automatic:

```json
{
    "areas": ["houston"],
    "categories": ["cto"],
    "yearMin": 2015,
    "odometerMax": 100000,
    "transmission": ["automatic"],
    "maxItems": 100
}
```

Two bedroom apartments in New York at 4,000 or less that take dogs:

```json
{
    "areas": ["newyork"],
    "categories": ["apa"],
    "bedroomsMin": 2,
    "bedroomsMax": 2,
    "dogsOk": true,
    "maxPrice": 4000,
    "maxItems": 100
}
```

Every Corvette for sale in the United States, search rows only:

```json
{
    "countries": ["US"],
    "categories": ["cta"],
    "keywords": ["corvette"],
    "includeDetails": false,
    "maxItems": 1000
}
```

Bicycles within 10 miles of a San Francisco ZIP code:

```json
{
    "locations": ["94103"],
    "radiusMiles": 10,
    "categories": ["bia"],
    "maxItems": 100
}
```

### What you get

One row per posting (`type: "listing"`), one coverage row per search (`type: "search"`), free status rows for anything
that did not end as a posting (`type: "status"`), with change tracking a removed row per posting a monitor no longer finds
(`type: "removed"`), and one run summary at the end (`type: "summary"`).

#### Fields and how often Craigslist fills them

Measured on real platform runs of 2026-10-16 (`includePersonalData` off, the default):

- **Search only:** 1,000 New York "for sale" rows with details off (run `apJfW3J8kLZhF929K`; 995 by owner, 1 by dealer,
  4 in categories without a seller type).
- **Cars with details:** 300 Houston "cars & trucks" rows with details (run `cBQaUvrLBTa2OUZIe`; 93 by owner, 207 by
  dealer).
- **Housing with details:** 15 New York apartment rows with details (run `iRJvtGJl2KZCJgAYA`), filtered to 2 bedrooms, dogs
  OK and at most 4,000, so `bedrooms`, `price` and `dogsOk` are full by the filter.

| Field | What it holds | Search only (1,000) | Cars with details (300) | Housing with details (15) |
|---|---|---|---|---|
| `postingId`, `postingUuid`, `url` | Craigslist's posting id, its 22 character id, the posting's link | 100 % | 100 % | 100 % |
| `title` | the posting's title | 100 % | 100 % | 100 % |
| `price`, `priceText`, `currency` | the price as a number, as Craigslist writes it, and the area's currency | 99.3 % | 92.0 % | 100 % |
| `category`, `categoryName`, `section`, `sellerType` | the category code and name, its section, `owner` or `dealer` (from the "by owner" and "by dealer" categories) | 100 %; seller type 99.6 % | 100 % | 100 %; no seller type in housing |
| `area`, `areaName`, `subArea`, `subAreaName`, `region`, `country` | the Craigslist area and sub area, the state or region, the country | 100 % | 100 %; sub area 0 % (Houston) | 100 % |
| `place`, `neighborhood` | the place text of the posting, the neighbourhood | 100 %, 4.7 % | 74.7 %, 0 % | 100 %, 0 % |
| `latitude`, `longitude`, `coordinatesRounded` | the posting's map point (rounded to 2 decimals for private postings, see Privacy) | 100 % | 100 % | 100 % |
| `locationType` | `pin` (the poster placed the pin) or `area` (Craigslist's approximate place), with details | | 100 % (pin 162, area 138) | 100 % |
| `postedAt`, `updatedAt`, `hoursOnline`, `daysOnline` | when the posting went up and was last updated; its age at the run | posted 100 % | 100 % | 100 % |
| `images`, `firstImage`, `imageCount` | every image link (search rows carry the images Craigslist lists there) | 99.4 % (median 8) | 98.0 % (median 18) | 100 % |
| `description` | the poster's full text, with details | | 100 % (median 690 characters) | 100 % |
| `attributes`, `attributeLabels` | every attribute as Craigslist shows it, by key and with its label | | 100 % (median 11) | 100 % |
| `makeModel`, `year`, `titleStatus`, `fuel`, `transmission` | car attributes | | 100 % | |
| `odometer` | miles (from the search row, or the posting's attribute) | 99.7 % on 1,100 search only car rows (San Francisco bay area, run `bjhibWCvbvPftItJJ`) | 100 % | |
| `bodyType`, `cylinders`, `paintColor`, `drive`, `condition` | car attributes | | 93.7 %, 85.7 %, 82.7 %, 80.7 %, 63.7 % | |
| `vin`, `vinData` | the VIN and Craigslist's decoded VIN data (make, plant, engine) | | 36.0 % (owner 12 of 93, dealer 96 of 207), 35.3 % | |
| `bedrooms`, `sqft`, `bathrooms`, `housingType`, `laundry`, `parking`, `rentPeriod` | housing attributes (size in square feet, square meters in metric areas) | | | 100 % |
| `moveInDate`, `catsOk`, `dogsOk`, `furnished` and the other housing flags | housing attributes | | | move in date 60 % |
| `compensation`, `jobTitle`, `companyName`, `employmentType` | jobs and gigs | not in these samples | not in these samples | not in these samples |
| `repostOf` | the id of an earlier posting this one reposts (Craigslist's own mark), with details | | 45.3 % | 60 % |
| `hasContactInfo` | whether the posting offers a way to reply | | 100 % (a flag) | 100 % |
| `streetAddress`, `phoneNumbers`, `emails` | the posted street address and the phone numbers and e-mail addresses written in the texts: dealers and companies always, private posters only with `includePersonalData` | | address 150, phones 181 of 207 dealer rows; 0 of 93 owner rows (off); e-mails 0 | 0 (off) |
| `leadScore`, `leadSignals` | a score from 0 to 100 and the signals behind it | 100 % | 100 % | 100 % |
| `missingFields` | the core fields (title, price, place, coordinates, posted time, first image) Craigslist left empty on this posting | set by the Actor | set by the Actor | set by the Actor |
| `searchKey`, `searchLabel`, `source`, `isNearby`, `duplicateGroup`, `detailsIncluded`, `monitorName`, `actorRunId`, `actorTaskId`, `deliveryId`, `scrapedAt` | what found the posting (`search`, or `postingUrl` for posting links), whether it came from a nearby area, Craigslist's duplicate bundle, whether details were read, the run | set by the Actor | set by the Actor | set by the Actor |
| `change`, `possibleRepostOf`, `isRepost` | with a monitor: what changed since the last delivery; an earlier posting of the same item | monitor runs | monitor runs | monitor runs |

Rows with details also carry `descriptionHtml` (the description as HTML, with `descriptionFormat`), `notices` (Craigslist's own
notices on the posting), `seeMyOther` (whether the poster links to their other postings) and `createdAt` (when Craigslist
created the record). A `removed` row carries `type`, `postingId`, a null `url`, `reason`, `message`, `lastPrice`, `firstSeenPrice`,
`priceChangeCount`, `firstSeenAt`, `daysTracked`, `monitorName` and `scrapedAt`; monitors of posting links and runs with a
posted window write no removed rows (they do not see every posting of a search).

The lead score adds up plain facts of the row: sold by owner (25), a way to reply (20), a price (15), posted within 24
hours (15), images (10), a description of 80 characters or more (10), not a repost (5).

#### Sample row (a real row of the prefill run on 2026-10-16; some fields left out)

A private posting, so its link carries no title slug and its coordinates are rounded (see Privacy).

```json
{
    "type": "listing",
    "postingId": "7984331352",
    "postingUuid": "1Sh6LvHnmb9De8zQPmLsna",
    "url": "https://www.craigslist.org/view/d/posting/1Sh6LvHnmb9De8zQPmLsna",
    "title": "2011 Honda Civic Coupe * Gas Saver * Runs Excellent",
    "price": 3950,
    "priceText": "$3,950",
    "currency": "USD",
    "category": "cto",
    "categoryName": "cars & trucks - by owner",
    "sellerType": "owner",
    "area": "sfbay",
    "areaName": "SF bay area",
    "subArea": "eby",
    "subAreaName": "east bay area",
    "region": "CA",
    "country": "US",
    "place": "vallejo / benicia",
    "latitude": 38.11,
    "longitude": -122.24,
    "coordinatesRounded": true,
    "locationType": "pin",
    "postedAt": "2026-10-16T00:56:44.000Z",
    "updatedAt": "2026-10-16T00:56:45.000Z",
    "imageCount": 15,
    "firstImage": "https://images.craigslist.org/00L0L_bpz4YBw1osH_0CI0hT_1200x900.jpg",
    "odometer": 203000,
    "detailsIncluded": true,
    "makeModel": "Honda Civic LX",
    "year": 2011,
    "titleStatus": "clean",
    "fuel": "gas",
    "transmission": "automatic",
    "drive": "fwd",
    "cylinders": "4 cylinders",
    "paintColor": "blue",
    "bodyType": "coupe",
    "condition": "good",
    "repostOf": "7954770517",
    "hasContactInfo": true,
    "streetAddress": null,
    "phoneNumbers": null,
    "emails": null,
    "leadScore": 95,
    "searchLabel": "cta (cars & trucks) in sfbay (SF bay area)",
    "source": "search",
    "missingFields": [],
    "scrapedAt": "2026-10-16T00:57:31.145Z"
}
```

#### Coverage row (one per search)

`searchKind`, `searchLabel`, `searchCategory`, `searchKeyword`, `searchPlace`, `searchRadiusMiles`, `sort`,
`postingsOnCraigslist` (Craigslist's own count), `searchCapRows`, `rowsRead`, `slices`, `slicesWalked`, `countCalls`,
`postingsBeyondReach`, `callsMade`, `rowsSeen`, `distinctPostingsSeen`, `postingsDelivered`, `alreadyDelivered`,
`knownToMonitor`, `filteredOut` and `filteredOutBy` (per filter), `nearbyAreaRows`, `deletedOrGone`, `detailsFailed`,
`newPostingsMayLieBeyond` (a monitor), `complete`, `stoppedBy`, `moreRowsOffered` and a `note`.

#### Removed rows (`trackChanges`)

`postingId`, `reason` (`notFoundByCompleteSearches`), `message`, `lastPrice`, `firstSeenPrice`, `priceChangeCount`,
`firstSeenAt`, `daysTracked`, `monitorName`, `scrapedAt`.

#### Free status rows

A setting or an item the Actor refused (`invalid`), an area or category name that matches several entries (`ambiguous`,
with the candidates), a posting link Craigslist does not know or a search it does not serve (`not_found`), a posting deleted
by its author (`deleted`), a posting or a search that could not be read (`failed`), a search whose filters Craigslist did not
apply as sent (`search_not_applied`), a posting link a monitor already delivered (`skipped`), a notification that failed
(`notification_failed`), a run the platform restarted (`resumed`), and the reason a run stopped (`limit_reached`,
`source_unavailable`, `monitor_unavailable`). A run writes 20 such rows plus one per billed event, at most 200 of one kind;
past that it counts the rest in the summary. Coverage rows share that budget.

#### How searches are read

- **Past 10,000 rows.** Craigslist serves at most the newest 10,000 rows of one search. When a search holds more and your
  caps want more, it is split into whole dollar price slices (each planned with a call of 360 rows, halved while a slice
  holds more than 9,000), and each slice is read from the top. Postings without a price fall in no slice: they are reached
  only among the newest 10,000, and the coverage row counts every posting left beyond reach. In a test of 2026-10-16 the San
  Francisco bay area cars search counted 15,588 postings and was read in 2 price slices.
- **Every filter checked.** Craigslist ignores a parameter it does not know and answers an option value outside its list
  with an empty page, without an error. The Actor sends only Craigslist's own filter names and values, refuses a pasted link
  with any other, and compares Craigslist's echo of the search with what it sent on every answer; a search that differs ends
  with a free `search_not_applied` row and nothing billed from that answer. Every row is also checked against the filters
  its own fields show (area, sub area, country, price, seller type, images, the car and housing attributes), again on the
  posting when details are read, and a posting edited between the search and the read is checked against the keyword again.
- **Nearby areas.** When a search has few local postings, Craigslist adds postings of nearby areas after them and does not
  count them. They are left out free unless you turn on `includeNearbyAreas`, and delivered ones carry `isNearby: true`.
- **The whole US.** Craigslist has no country search. `countries: ["US"]` runs six searches of 1,000 miles (Craigslist's
  largest radius) around Louisville, Provo, New Orleans, Detroit, Anchorage and Honolulu, which hold every US area within
  800 miles of a centre; a posting found by two circles is delivered once, and postings of Canadian or Mexican areas inside
  a circle are left out free.
- **One row per posting.** A posting found by several searches in one run is delivered and billed once.
- **Fresh answers.** Craigslist keeps each search answer for 15 minutes for every client; the Actor asks for a fresh one on
  every call. In a test of 2026-10-15 the newest "for sale" posting was 17 to 49 seconds old in three cities.
- **Access.** Requests leave through one Apify datacenter exit per run (a new one only when the proxy itself fails; when the
  proxy's address cannot be checked three times, or every session lands in a network Craigslist refuses, the run reads from
  its own container's address and the summary says so), two at a time, 2 a second by default, and only
  through doors Craigslist's robots.txt allows. A refusal is asked once more on the same exit after a pause; a second
  refusal ends the run's reading with a free `source_unavailable` row. No captcha is ever solved.

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

You pay per posting row delivered, with the full details included, plus a small start fee per run. Platform usage and the
datacenter proxy are included.

| Event | Free | Bronze | Silver | Gold |
|---|---|---|---|---|
| Listing (per 1,000 postings, details included) | $0.66 | $0.60 | $0.55 | $0.47 |
| Results page (per 1,000 units of 360 rows a monitor checks; monitor runs only) | $0.70 | $0.64 | $0.58 | $0.50 |
| Listing update (per 1,000 changes and removed rows; monitor runs with `trackChanges` only) | $0.66 | $0.60 | $0.55 | $0.47 |
| Actor start (per run) | $0.007 | $0.007 | $0.007 | $0.007 |

Platinum and Diamond plans pay the Gold price. The Actor start event is a flat $0.007, charged once per run, with no plan
discount. The Store pricing card shows these same prices per 1,000 events: "$0.60 / 1,000" on the Listing row means one
posting costs $0.0006, and "$0.64 / 1,000" on the Results page row means each 360 rows a monitor checks cost $0.00064.

- **1,000 postings with details** cost $0.66 + $0.007 = **$0.667** on Free, $0.60 + $0.007 = **$0.607** on Bronze,
  $0.55 + $0.007 = **$0.557** on Silver and $0.47 + $0.007 = **$0.477** on Gold.
- **The prefill run** (20 postings with details) costs 20 x $0.00066 + $0.007 = $0.0202 on Free and 20 x $0.00047 +
  $0.007 = $0.0164 on Gold.
- **An hourly monitor** over one search on Bronze reads the newest 360 rows each run: 24 runs a day cost 24 x ($0.007 +
  $0.00064) = $0.18336 a day, plus $0.0006 per new posting. A run that finds 12 new postings costs $0.007 + $0.00064 + 12 x
  $0.0006 = $0.01484.
- **What is never billed:** postings left out by your filters, nearby area postings left out, a posting found twice in one
  run, postings a monitor already delivered (unless change tracking finds a change, or you ask for every known posting),
  deleted and unknown postings, a search Craigslist does not serve or whose filters it did not apply, refused inputs, search
  calls and posting reads themselves (outside a monitor; in a monitor run each search call is a Results page), coverage,
  status and summary rows, the run report and the notifications.
- **Limits:** set **Maximum postings** (100 when left empty) and, in the run options, a maximum charge for the run: the
  Actor stops before it would pass either, and a row whose events do not all fit is withheld whole. Maximum postings counts
  postings, changes and removed rows together. A run also spends a small allowance on work that delivers nothing billable
  (searches whose rows your filters leave out, failed reads), USD 0.0008 plus a share of every paid event it delivers; past
  it the run stops with a free row that says so.

### Input

| Setting | What it does |
|---|---|
| `areas` | Craigslist areas by hostname (`sfbay`, `newyork`, `houston`), name (`SF bay area`) or sub area (`sfbay/sby`, `sby`, `south bay area`); up to 1,000 |
| `countries` | two letter codes; `US` is the whole United States in six searches; any other country (CA, GB, AU, DE, FR, MX, IN and the rest) is every area of it |
| `locations`, `radiusMiles` | US ZIP codes (`94103`) or coordinates (`37.77, -122.42`), searched across every area within the radius (1 to 1,000 miles, 25 by default) |
| `categories` | 228 codes or names (`sss` for sale, `cta` cars and trucks, `cto` by owner, `ctd` by dealer, `apa` apartments, `jjj` jobs, `ggg` gigs, `bbb` services, `eee` events); `sss` when none is given |
| `keywords` | what to search for, in Craigslist's own syntax; each keyword is its own search |
| `searchUrls` | Craigslist search links copied from your browser; each keeps its own filters and sort; a parameter that is not Craigslist's own refuses the link with a free row |
| `postingUrls` | posting links or 22 character posting ids, up to 5,000, each read with its details; a deleted or unknown posting is a free row |
| `resumeFromDatasetId` | the dataset of an earlier run of this Actor: its postings are left out, free (up to 200,000 rows read) |
| named filters | `sort`, `minPrice`, `maxPrice`, `postedToday`, `hasImage`, `titlesOnly`, `hideDuplicates`, `freeOnly`, `sellerType`, `conditions`, `deliveryAvailable`, `cryptoAccepted`; cars `makeModel`, `yearMin` to `yearMax`, `odometerMin` to `odometerMax`, `titleStatus`, `transmission`, `fuel`, `bodyTypes`, `drive`, `cylinders`, `paintColors`, `monthlyPaymentMin` to `monthlyPaymentMax`; housing `bedroomsMin` to `bedroomsMax`, `bathroomsMin` to `bathroomsMax`, `sqftMin` to `sqftMax`, `housingTypes`, `laundry`, `parking`, `rentPeriods`, `availability`, `catsOk`, `dogsOk`, `furnished`, `noSmoking`, `wheelchairAccess`, `airConditioning`, `evCharging`, `noApplicationFee`, `noBrokerFee`, `privateRoom`, `privateBath`; jobs `employmentTypes`, `telecommuting`, `internship`, `nonProfit`; gigs `gigPay`; `date` and `dateTo` (events, garage sales and open houses, one search per day, at most 31 days) |
| `filters` | any other Craigslist filter by its own name and value, such as `{"mobile_os": "apple iOS"}`; an unknown name refuses the run with a free row |
| row filters | `includeNearbyAreas`, `includeKeywords`, `excludeKeywords` (whole words in the title and place, and the description when read), `postedWithinHours`, `postedWithinDays`, `postedAfter`, `postedBefore` (need the newest or oldest sort), `customFilters` (up to 20 rules), `excludePostingIds` (up to 5,000) |
| `sort` | newest (default), oldest, price_low, price_high, distance (needs a ZIP code or coordinates), relevance (needs a keyword), upcoming (events, garage sales, open houses) |
| `maxItems`, `maxItemsPerSearch`, `maxRowsPerSearch`, `splitLargeSearches` | caps, and how far each search is read (Craigslist answers 360, 1,080, 3,240 or 10,000 rows from the top) |
| `includeDetails` | each posting's description, every image and attribute (on by default, same price; off is faster) |
| `language`, `imageSize`, `descriptionFormat`, `descriptionMaxLength`, `includeRawData`, `compactOutput`, `excludeEmptyFields` | how rows are written; `language` sets Craigslist's labels (15 languages), codes stay the same |
| `includePersonalData` | a private poster's phone numbers, e-mail addresses, street address and exact coordinates (off by default; see Privacy) |
| monitor and notifications | `monitorName`, `trackChanges`, `trackFields`, `emitUnchanged`, `markReposts`, `skipReposts`, `notificationLimit`, `notifyOnlyChanges` and the channel settings below |
| `failIfIncomplete`, `maxFailedRequestsPercent`, `requestsPerSecond`, `proxyConfiguration` | fail the run when a search was not read to its end or too many reads failed; the pace (0.5 to 3 a second, 2 by default); Apify datacenter (default) or your own proxy links |

A run holds up to 300 searches (places x categories x keywords x days, plus one per search link) and up to 5,000 posting
links. A filter that does not fit a searched category (bedrooms on cars, for example) refuses the run with a free row.

### The monitor and notifications

Name a monitor (`monitorName`) and schedule the Actor (every hour, for example). The monitor remembers every posting it
delivered in a named store this Actor creates for itself, up to 60,000 postings, so each run delivers and bills only
postings it never delivered before; known postings are skipped for free. A monitor reads the newest 360 rows of each search
by default (set `maxRowsPerSearch` to read 1,080, 3,240 or 10,000), does not split searches, and pays a Results page for
each 360 rows it reads (at least one per search); when the coverage row says `newPostingsMayLieBeyond`, read more rows or
run more often. One monitor name belongs to one set of settings: a run with other settings delivers nothing and gets a free
row saying so.

- **Change tracking** (`trackChanges`): a known posting whose price changed, or whose price was taken away or put back,
  comes back with `change` (`changeType: "priceChanged"`, previous price, difference, first seen price, price history, drop in percent since first seen), billed
  as a Listing update. With `trackFields` a change of the title, the images, the location (place, neighbourhood, map point),
  the odometer, bedrooms, square feet, pay, monthly payment or the sale, event or open house dates counts too
  (`changeType: "updated"`, `changedFields`): the facts every search row shows, compared on it, so tracking them reads nothing
  more. A change of the description or the attributes alone is not tracked: it would need every known posting read again on
  every run. A posting the monitor reported as removed that shows up again is `reappeared`.
- **Removed postings:** when every search of the run was read to its end (for a monitor, a search that holds no more
  postings than its window), a posting the monitor tracks that the run did not meet gets a `removed` row. When more than
  half of the tracked postings (and more than 5) are missing at once, none are reported and the run says so in a free row.
- **Snapshots and reposts:** `emitUnchanged` delivers known postings again (billed as Listing); `markReposts` marks a new
  posting that reposts one the monitor delivered (Craigslist's own "repost of" mark, or the same area, category, title and
  price under a new id) with `possibleRepostOf`, and `skipReposts` leaves it out free. Both need details.
- **Notifications** (free, with or without a monitor) after the rows were delivered and billed: Telegram (your own bot, one
  message per posting), Slack and Discord incoming webhooks and WhatsApp Cloud API (one message per run), each naming up to
  `notificationLimit` postings (10 by default, up to 30); a webhook (`webhookUrl`) gets JSON batches of 100 postings in a
  short form (id, link, title, price, currency, price text, place, area, category, seller type, miles, bedrooms, posted
  time, image, search label, change), up to 1,000 a run, signed in the header
  `x-signature-sha256` with `webhookSecret`, with your own `webhookHeaders`. With a monitor, unchanged postings are
  left out of the messages (`notifyOnlyChanges`, on by default). A failed call never fails
  the run: it is a free `notification_failed` row, without your address or token.

#### A feed of new postings (in place of Craigslist RSS)

Craigslist's RSS feeds refuse automated readers, so this Actor never uses them. To get a stream of new postings:

1. Pick your search (areas, categories, keywords, filters) and give it a `monitorName`, such as `sfbay-tacoma`.
2. Add a `webhookUrl` (Zapier, Make, n8n or your own endpoint), or a Telegram, Slack, Discord or WhatsApp channel.
3. Save the input as a task and schedule it in Apify (every 15 minutes or every hour).

Each run then sends only postings the monitor has not seen. If a tool of yours reads RSS, Apify's API serves any dataset in
RSS format (`format=rss` on the dataset items endpoint); the feed then holds the run's coverage, status and summary rows
next to the postings.

#### Past the 10,000 row cap

Leave `splitLargeSearches` on (the default) and set `maxItems` to what you want: a search that Craigslist counts above
10,000 is read slice by slice by price, each slice from the top, and the coverage row gives Craigslist's count, the slices
read and anything left beyond reach. A monitor does not split; give it narrower searches (a sub area, a category, a price
range) instead.

### Privacy and personal data

Many Craigslist postings come from private persons. With `includePersonalData` off (the default), every posting that is
not in a "by dealer" category and names no company is treated as private: phone numbers, e-mail addresses, street addresses
written with a house number and a street word (`4512 Main St`, `Hauptstraße 12`, `Calle Mayor 12`) or with a house number
and a state code (`123 Broadway, New York, NY 10001`), exact coordinates written into the text (`37.123456, -122.654321`, in either order, also inside a map link), and names written right after a contact word (in lower case, the common first names) (`ask for
John`) or before a phone number are replaced in its title, description, place, neighbourhood and attribute values (with
`[phone number removed]`, `[e-mail removed]`, `[address removed]`, `[postal code removed]`, `[coordinates removed]`, `[name removed]`); an address or a name written in
another way (`123 Broadway` alone, without a street word or a state) stays as the poster wrote it. A phone number is
found by its shape when it has a country code or ten digits or more; a shorter local number (`284-1234`) is found when a
contact word (`call`, `text`, `contact`...) stands before it in the same few lines, and may stay when none does. A number
written partly in words and glued to its digits (`832-four nine six-0367`) is masked whole; one written only in words is
not detected. Near a
contact word the rule leans to privacy: a number of 6 digits or more there without a unit (`miles`, `sq ft`), a label
(`Mileage:`, `Stock #:`) or a currency sign may be masked as a phone number too. Before the masks, a private
posting's texts are written in plain characters (every kind of space as a space, dashes as hyphens, curly apostrophes straight,
digits of any script as 0 to 9), and are delivered that way. Its `streetAddress`, `phoneNumbers`,
`emails` and `raw` fields are null, its coordinates are rounded to 2 decimals (about 1 km), and its link
is written without the title slug. In the Houston cars run above, 18 of 93 owner postings carried such a mask. With it on,
nothing is masked or rounded. Postings of dealers and companies are business data and are always delivered as written.
Craigslist shows no poster name, and the reply contact behind its reply button is never read. Images are delivered as
links; they belong to the people who posted them. You are responsible for a lawful use of personal data, privacy laws
included.

### Legal

This Actor is not affiliated with or endorsed by Craigslist. Craigslist is a trademark of its owner. It is an unofficial
tool that reads public postings without logging in, through the doors Craigslist's robots.txt allows. Craigslist's terms
of use forbid collecting its content with scrapers, crawlers or scripts. You are responsible for a lawful use of the data,
for complying with privacy laws for any personal data, and for keeping a scheduled monitor to what you need.

### What this Actor does NOT do

- **No reply e-mail or phone number from behind the reply button.** Craigslist shows the reply contact only after a captcha;
  the Actor never opens it. `hasContactInfo` says whether a posting offers one; the posting's link (`url`) is where a buyer
  replies.
- **No tracking of description or attribute changes alone.** Change tracking compares the facts every search row shows;
  the description and the attributes would need every known posting read again on every run.
- **No posting, flagging, messaging, saved searches or anything else that needs a Craigslist account.** No login.
- **No private poster's phone numbers, e-mail addresses, street address field or exact coordinates unless you turn on
  `includePersonalData`.** Addresses and names written in the texts are masked in the forms Privacy names. Dealers' and
  companies' contacts are always delivered.
- **No captcha solving and no new exit to get past a refusal.** A second refusal in a row ends the run's reading with a free
  `source_unavailable` row.
- **No RSS feeds and no sitemaps.** Searches go through Craigslist's search, postings through its posting pages.
- **No more than 10,000 rows from one Craigslist search answer.** Past that the search is split by price (not in a
  monitor); postings without a price are reached only among the newest 10,000, and the coverage row counts what is left.
- **No postal codes outside the US.** Give coordinates for a radius search elsewhere, or the areas themselves.
- **No view counts.** Craigslist shows none, and reading a posting counts nothing.
- **No image downloads.** Images are links in the size you pick.
- **No translation.** `language` sets Craigslist's labels; descriptions come as the poster wrote them.
- **No export into Notion, Airtable or other apps from inside the run.** Those exports need your own account at each app;
  connect the run's dataset with Apify's integrations or MCP connectors, authorised in your own Apify settings.
- **No preloaded snapshot.** Every run reads Craigslist live; the data is as fresh as the run.

### FAQ

#### Does Craigslist have an API?

Craigslist offers no API to read or search its postings: its official interfaces serve the list of areas and categories
and the bulk posting of ads. This Actor reads the same public postings any visitor sees, with no login, and gives them to
you as rows you can read through the Apify API (examples below).

#### Is it legal to scrape Craigslist?

This Actor reads only public postings without logging in, never uses the reply button, flagging or posting, and masks the
phone numbers, e-mail addresses and street addresses it finds in private postings unless you turn personal data on. Craigslist's terms of use
forbid collecting its content with scrapers, so you are responsible for a lawful use of the data, privacy laws included
(see Legal).

#### How do I search all of Craigslist at once?

Put `US` in **Whole countries**: the Actor runs six searches of 1,000 miles that together cover every US area, delivers
each posting once and leaves out postings of other countries. Other countries are searched area by area, and a ZIP code or
coordinates with a radius of up to 1,000 miles searches every area around a point.

#### How do I get alerts for new Craigslist listings?

Name a monitor and schedule the Actor, every hour for example. Each run delivers and bills only postings the monitor has
not delivered before, and can send them to Telegram, Slack, Discord, WhatsApp or your webhook; no Craigslist account is
needed.

#### What can I use instead of Craigslist RSS feeds?

Craigslist refuses its RSS feeds to automated readers (HTTP 403 in our tests), so this Actor never uses them. A scheduled
monitor with a webhook gives a stream of new postings, and Apify's API can serve a run's dataset in RSS format for a feed
reader (see A feed of new postings).

#### Why does this Actor run with limited permissions?

It needs nothing beyond its own storages: it reads Craigslist's public postings and writes to its own dataset and
key-value store, plus the named store of a monitor you set with `monitorName`, which the Actor creates for itself. Limited
permissions keep it from touching anything else in your account.

### API

Each example starts a run and waits for its rows (the run-sync endpoint suits short runs; use `maxItems` to keep them short).

```bash
curl -X POST "https://api.apify.com/v2/acts/automation_craft~craigslist-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"areas": ["sfbay"], "categories": ["cta"], "maxItems": 20}'
```

```js
const res = await fetch('https://api.apify.com/v2/acts/automation_craft~craigslist-scraper/run-sync-get-dataset-items', {
    method: 'POST',
    headers: { Authorization: 'Bearer <YOUR_API_TOKEN>', 'Content-Type': 'application/json' },
    body: JSON.stringify({ areas: ['sfbay'], categories: ['cta'], maxItems: 20 }),
});
const rows = await res.json();
console.log(rows.filter((r) => r.type === 'listing'));
```

```python
import requests

res = requests.post(
    "https://api.apify.com/v2/acts/automation_craft~craigslist-scraper/run-sync-get-dataset-items",
    headers={"Authorization": "Bearer <YOUR_API_TOKEN>"},
    json={"areas": ["sfbay"], "categories": ["cta"], "maxItems": 20},
    timeout=330,
)
postings = [r for r in res.json() if r["type"] == "listing"]
```

### Changelog

See the Changelog tab. 0.1: first release.

### More data tools by Automation Craft

- [OfferUp Scraper: Local Listings, Prices, Sellers](https://apify.com/automation_craft/offerup-scraper)
- [Facebook Marketplace Scraper with Sold Tracker](https://apify.com/automation_craft/facebook-marketplace-scraper)
- [Kijiji Scraper: Listings, Cars, Rentals & Prices](https://apify.com/automation_craft/kijiji-scraper)
- [Mercari US Scraper: Listings, Sold Prices](https://apify.com/automation_craft/mercari-us-scraper)
- [Kleinanzeigen Scraper & API: Ads, Prices, Places](https://apify.com/automation_craft/kleinanzeigen-scraper)
- [Leboncoin Scraper: Ads, Prices, Real Estate & Cars](https://apify.com/automation_craft/leboncoin-scraper)
- [AutoScout24 Scraper: EU Car Listings & Prices](https://apify.com/automation_craft/autoscout24-scraper)
- [Mobile.de Scraper: Cars, Prices, Dealers & Ratings](https://apify.com/automation_craft/mobile-de-scraper)

# Changelog

This Actor's version history is a separate document: https://apify.com/automation_craft/craigslist-scraper/changelog.md

# Actor input Schema

## `areas` (type: `array`):

Craigslist areas by their hostname ("sfbay", "newyork", "houston"), their name ("SF bay area"), or a sub area ("sfbay/sby", "sby" or "south bay area"). All 707 areas of every country are known, and their 103 sub areas.

## `countries` (type: `array`):

Two letter country codes. "US" searches all of the United States through six 1,000 mile searches that cover every US area (each posting once, postings of other countries left out). Any other country is searched area by area.

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

US ZIP codes ("94103") or coordinates ("37.77, -122.42"), each searched within the radius below across every Craigslist area (Craigslist searches by the postings' own coordinates).

## `radiusMiles` (type: `integer`):

The radius around each ZIP code or point, 1 to 1,000 miles (Craigslist's own limit). Default 25.

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

Craigslist categories by their code ("sss" for sale, "cta" cars and trucks, "cto" cars by owner, "apa" apartments, "jjj" jobs, "ggg" gigs, "bbb" services, "eee" events) or their name ("cars & trucks"). Default: "sss", everything for sale.

## `keywords` (type: `array`):

What to search for, as you would type it on Craigslist ("iphone 15", "toyota tacoma", "studio"); Craigslist's own search syntax works ("iphone -case", "(honda | toyota)"). Each keyword is its own search.

## `searchUrls` (type: `array`):

Search links copied from your browser (https://sfbay.craigslist.org/search/cta?min_price=5000, https://www.craigslist.org/search/city/houston-tx?postal=77002\&search_distance=50\&cat=sss). Each keeps its own filters and sort; a parameter that is not one of Craigslist's own refuses the link (Craigslist would ignore it and search wider).

## `postingUrls` (type: `array`):

Posting links (https://sfbay.craigslist.org/sby/mob/d/san-jose-iphone/7983092835.html or https://www.craigslist.org/view/d/.../<id>) or posting ids of 22 letters and digits, each read with its full details. A deleted or unknown posting is a free row.

## `resumeFromDatasetId` (type: `string`):

The dataset id of an earlier run of this Actor in your account (its "defaultDatasetId"). Postings delivered in it are left out, free, so an interrupted run can be continued without paying for a posting twice. At most 200,000 rows are read.

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

The order of every built search. Default: newest first. A search link keeps its own sort.

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

Lowest price, whole units of the area's currency (postings without a price are left out by Craigslist).

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

Highest price, whole units of the area's currency.

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

Only postings of the last 24 hours (Craigslist's own filter).

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

Only postings with at least one image.

## `titlesOnly` (type: `boolean`):

Search the keywords in titles only.

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

Craigslist's own "hide duplicates": postings it bundles as copies of one another are delivered once (the first that passes every filter, in the order of the sort: the newest with the newest sort).

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

Only postings offered for free (for sale section).

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

Owners, dealers, or both (default). For sale section.

## `conditions` (type: `array`):

Any of these conditions (for sale section).

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

Only postings that offer delivery (for sale section).

## `cryptoAccepted` (type: `boolean`):

Only postings that accept cryptocurrency (for sale section).

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

Craigslist's make and model search, such as "toyota camry" or "ford f-150" (for sale section).

## `yearMin` (type: `integer`):

Earliest model year.

## `yearMax` (type: `integer`):

Latest model year.

## `odometerMin` (type: `integer`):

Lowest odometer reading.

## `odometerMax` (type: `integer`):

Highest odometer reading.

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

Any of these title statuses.

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

Any of these transmissions.

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

Any of these fuels.

## `bodyTypes` (type: `array`):

Any of these body types.

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

Any of these drives.

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

Any of these cylinder counts.

## `paintColors` (type: `array`):

Any of these paint colours.

## `monthlyPaymentMin` (type: `integer`):

Lowest estimated monthly payment (cars and trucks by dealer).

## `monthlyPaymentMax` (type: `integer`):

Highest estimated monthly payment (cars and trucks by dealer).

## `bedroomsMin` (type: `integer`):

Fewest bedrooms.

## `bedroomsMax` (type: `integer`):

Most bedrooms.

## `bathroomsMin` (type: `integer`):

Fewest bathrooms.

## `bathroomsMax` (type: `integer`):

Most bathrooms.

## `sqftMin` (type: `integer`):

Smallest size in square feet (square meters in metric areas).

## `sqftMax` (type: `integer`):

Largest size.

## `housingTypes` (type: `array`):

Any of these housing types.

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

Any of these.

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

Any of these.

## `rentPeriods` (type: `array`):

Any of these rent periods.

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

When the place is available.

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

Only postings marked "cats ok".

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

Only postings marked "dogs ok".

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

Only postings marked "furnished".

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

Only postings marked "no smoking".

## `wheelchairAccess` (type: `boolean`):

Only postings marked "wheelchair accessible".

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

Only postings marked "air conditioning".

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

Only postings marked "ev charging".

## `noApplicationFee` (type: `boolean`):

Only postings marked "no application fee".

## `noBrokerFee` (type: `boolean`):

Only postings marked "no broker fee".

## `privateRoom` (type: `boolean`):

Only postings marked "private room".

## `privateBath` (type: `boolean`):

Only postings marked "private bath".

## `employmentTypes` (type: `array`):

Any of these.

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

Only jobs that allow working remotely.

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

Only internships.

## `nonProfit` (type: `boolean`):

Only jobs at non-profit organisations.

## `gigPay` (type: `string`):

Paid or unpaid gigs.

## `date` (type: `string`):

Events, classes, garage sales and tickets on this day, or apartments with an open house on it (YYYY-MM-DD).

## `dateTo` (type: `string`):

With "Date": every day from it to this day, at most 31 days, one search per day (each posting once per run).

## `filters` (type: `object`):

Any other Craigslist filter by its own name, with a value by code or label as Craigslist writes it: {"language": \["english", "español"], "crypto_currency_ok": true, "delivery_available": true} (filters of the "for sale" section). Every filter must fit the searched categories; a name this Actor does not know refuses the run (Craigslist would ignore it and search wider).

## `includeNearbyAreas` (type: `boolean`):

When a search has few local postings Craigslist adds postings of nearby areas after them; they are left out (free) unless this is on. Delivered ones are marked isNearby.

## `includeKeywords` (type: `array`):

Whole words looked for in the title and place (and the description when details are read). A row needs one of them.

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

Whole words that leave a row out, looked for where the words above are.

## `postedWithinHours` (type: `integer`):

Only postings posted within this many hours of the run's start (read from the search rows, no details needed).

## `postedWithinDays` (type: `integer`):

Only postings posted within this many days of the run's start.

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

Only postings posted on or after this day, YYYY-MM-DD in UTC.

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

Only postings posted on or before this day, YYYY-MM-DD in UTC.

## `customFilters` (type: `array`):

Rules on output fields, all of which must hold: \[{"field": "bedrooms", "op": "gte", "value": 2}, {"field": "place", "op": "notIncludes", "value": "oakland"}]. Ops: includes, notIncludes, equals, notEquals, gt, gte, lt, lte.

## `excludePostingIds` (type: `array`):

Posting ids (digits) or classic posting links (ending in the id and .html) never delivered; a /view/d/ link carries no id and is refused.

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

The most posting rows the run delivers (searches, links and updates together). Default 100.

## `maxItemsPerSearch` (type: `integer`):

The most posting rows one search delivers.

## `maxRowsPerSearch` (type: `integer`):

How many of each search's newest postings are read at most: Craigslist answers 360, 1,080, 3,240 or 10,000 rows from the top, and any number is read as the next of those. A monitor reads this many every run (default 360). Without it a run reads as deep as its caps need, past 10,000 with price slices.

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

On by default: a search with more than Craigslist's 10,000 rows is split into price slices, so postings past the cap are read too (postings without a price only among the newest 10,000). Off for monitors.

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

On by default (part of the Listing price): each posting's description, every image, every attribute (cars: make and model, year, VIN, title status, fuel, transmission; housing: bathrooms, laundry, parking, pets, move-in date; jobs: compensation, employment type), decoded VIN data, repost mark and updated time. Off: the search row's fields only, same price, faster.

## `imageSize` (type: `string`):

The size of the image links (the size is part of the link; no image is downloaded). Default large.

## `descriptionFormat` (type: `string`):

The format of the description (rows with details).

## `descriptionMaxLength` (type: `integer`):

Cut descriptions to this many characters.

## `language` (type: `string`):

The language of Craigslist's labels in the rows (category names and attribute values such as the condition); codes and filters are the same in every language. Default English.

## `includeRawData` (type: `boolean`):

Add the raw search row and posting answer under "raw" (for a private posting only with personal data on, since they carry its texts unmasked).

## `compactOutput` (type: `boolean`):

Only the core fields on each row (what a row paid for stays).

## `excludeEmptyFields` (type: `boolean`):

Drop fields that are empty on a row.

## `includePersonalData` (type: `boolean`):

Off by default. On, a private poster's rows carry: phone numbers, e-mail addresses, street addresses and names in their own texts as Craigslist shows them, the fields phoneNumbers, emails, streetAddress and raw, exact coordinates, and the link with its title slug (off, those are replaced, null, rounded to about 1 km, or left out). Postings of dealers and companies are business data and always delivered as written. Personal data is protected by privacy laws such as the GDPR and the CCPA; turn this on only for a lawful purpose, and you are responsible for that use.

## `monitorName` (type: `string`):

A short name ("sfbay-cars"). The monitor remembers every posting it delivered; known postings are free. One name holds one set of settings.

## `trackChanges` (type: `boolean`):

Also deliver known postings whose price (or a tracked field) changed or that came back, and postings that left every search (removed), billed as Listing update.

## `trackFields` (type: `array`):

The fields whose change makes a Listing update: the facts every search row shows (price, title, images, location, odometer, bedrooms, square feet, pay, monthly payment, dates). The description and the attributes are not tracked: they would need every known posting read again each run. Default: price.

## `emitUnchanged` (type: `boolean`):

Deliver known postings without a change as well (billed as Listing), each marked unchanged.

## `markReposts` (type: `boolean`):

Mark a new posting that reposts one the monitor delivered (Craigslist's own "repost of" mark, or the same area, category, title and price under a new id).

## `skipReposts` (type: `boolean`):

Leave such reposts out (free).

## `webhookUrl` (type: `string`):

Rows are POSTed here in batches of 100 (JSON).

## `webhookSecret` (type: `string`):

Signs each batch (HMAC SHA-256, header X-Signature-Sha256).

## `webhookHeaders` (type: `object`):

Extra headers, such as {"Authorization": "Bearer ..."}.

## `telegramBotToken` (type: `string`):

The token BotFather gave your bot.

## `telegramChatId` (type: `string`):

Your chat id, or a channel name starting with @.

## `slackWebhookUrl` (type: `string`):

An incoming webhook of your Slack workspace.

## `discordWebhookUrl` (type: `string`):

A webhook of your Discord channel.

## `whatsappAccessToken` (type: `string`):

Your WhatsApp Cloud API token.

## `whatsappPhoneNumberId` (type: `string`):

The id of your WhatsApp Cloud API number.

## `whatsappTo` (type: `string`):

The number to message, international form without +.

## `notificationLimit` (type: `integer`):

The most postings named in each chat message. Default 10.

## `notifyOnlyChanges` (type: `boolean`):

With a monitor, leave unchanged postings out of the messages (default on).

## `failIfIncomplete` (type: `boolean`):

Mark the run failed when a search or posting link was not read completely (the rows stay delivered).

## `maxFailedRequestsPercent` (type: `integer`):

Mark the run failed when more than this share of its search calls and posting reads failed (the rows stay delivered).

## `requestsPerSecond` (type: `number`):

The pace of requests to Craigslist (default 2).

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

Apify's datacenter proxy (the default: Craigslist answers it), or your own proxy links.

## Actor input object example

```json
{
  "areas": [
    "sfbay"
  ],
  "categories": [
    "cta"
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (type: `string`):

No description

## `report` (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 = {
    "areas": [
        "sfbay"
    ],
    "categories": [
        "cta"
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation_craft/craigslist-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 = {
    "areas": ["sfbay"],
    "categories": ["cta"],
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("automation_craft/craigslist-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 '{
  "areas": [
    "sfbay"
  ],
  "categories": [
    "cta"
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call automation_craft/craigslist-scraper --silent --output-dataset

```

## MCP server setup

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