# Craigslist Scraper — Listings, Prices, Deal Alerts (`dottti/craigslist-listings`) Actor

Craigslist listings from any city, state or country: price, location, map point, dates, car and housing details, images and full text. Search any category with price, date and owner filters. New-listings alert mode. Free status row per search. No login.

- **URL**: https://apify.com/dottti/craigslist-listings.md
- **Developed by:** [Dottti](https://apify.com/dottti) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 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.

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, Deal Alerts

Get Craigslist listings from any city, any state or province, or a whole country. Each listing comes with its title, price and currency, area, map point, dates, category, owner or dealer, photos, and the numbers Craigslist shows in search: odometer for cars, bedrooms and square feet for housing, pay and employer for jobs. Turn on one switch to also get the full listing text and every attribute (make and model, year, title status, laundry, pets and more).

The actor reads the same public data the Craigslist website loads in your browser. You need no account and no API key.

### Who uses it

- **Car dealers and flippers** who watch owner listings for one make and price range in many cities.
- **Resellers and deal hunters** who want an alert when a new listing matches their search.
- **Property managers and housing researchers** who compare rents, bedrooms and square feet across areas.
- **Recruiters and labor analysts** who track job and gig posts with pay and employer.
- **Market researchers** who need clean price data across hundreds of local markets.

### What you can do

- Search one site (`sfbay`), part of a site (`sfbay/sby` for the South Bay), every site in a state (`US-TX`) or province (`CA-ON`), or every site in a country (`GB`).
- Pick any categories: for sale, cars & trucks, housing, apartments, jobs, gigs, services, community, events and all their sub-categories. Codes (`cta`) and plain words (`cars`) both work.
- Filter by search words, price range, posted today, posted or renewed within N days, has images, owner or dealer, and distance from a ZIP code.
- Paste Craigslist search links from your browser. Every filter in the link is kept.
- Get up to tens of thousands of listings per search.
- Open each listing for the full text, all attributes, first-posted date and exact map point.
- Turn on deal alert mode. Later runs return only listings you have not seen.

### Input examples

Owner-sold Toyotas between $3,000 and $9,000 with photos, in four cities:

```json
{
  "sites": ["chicago", "losangeles", "seattle", "denver"],
  "categories": ["cars"],
  "searchQueries": ["toyota"],
  "sellerType": "owner",
  "minPrice": 3000,
  "maxPrice": 9000,
  "hasImages": true,
  "maxListingsPerSearch": 100
}
```

Every gig posted in Texas in the last 2 days:

```json
{
  "regions": ["US-TX"],
  "categories": ["ggg"],
  "postedWithinDays": 2,
  "maxListingsPerSearch": 200
}
```

Apartments in the city of Chicago with full text and attributes:

```json
{
  "sites": ["chicago/chc"],
  "categories": ["apa"],
  "maxPrice": 2500,
  "includeDetails": true,
  "maxListingsPerSearch": 50
}
```

A search link copied from the browser:

```json
{
  "searchUrls": ["https://www.craigslist.org/search/area/sfbay?cat=bik&query=trek&min_price=200"]
}
```

Deal alert for two bikes in two places (run it on a schedule):

```json
{
  "sites": ["sfbay/sby", "portland"],
  "categories": ["bia"],
  "searchQueries": ["trek", "specialized"],
  "onlyNew": true,
  "stateName": "bike-deals",
  "maxListingsPerSearch": 30
}
```

### Output

Every row has a `type` field.

| type | One row is | Charged |
|---|---|---|
| `listing` | One Craigslist listing | Yes |
| `search` | One search (site x category x search words) and its status | No |

#### Listing row with details (real output, 2026-10-26, 9 image links left out)

```json
{
  "type": "listing",
  "id": 7989992169,
  "url": "https://www.craigslist.org/view/d/daly-city-2009-audi-a5-line-61k/tPnFxHnsCVGL5rt7fEE4aN",
  "title": "2009 Audi A5 S Line, 61k original miles, V6",
  "price": 8900,
  "priceText": "$8,900",
  "currency": "USD",
  "site": "sfbay",
  "siteName": "SF bay area",
  "subarea": "pen",
  "subareaName": "peninsula",
  "region": "CA",
  "country": "US",
  "neighborhood": null,
  "location": "Daly City",
  "lat": 37.6735,
  "lon": -122.4822,
  "listedAt": "2026-10-26T11:39:37.000Z",
  "postedAt": "2026-10-26T11:39:37.000Z",
  "updatedAt": "2026-10-26T11:42:27.000Z",
  "category": "cto",
  "categoryName": "cars & trucks - by owner",
  "section": "for sale",
  "sellerType": "owner",
  "odometer": 61000,
  "bedrooms": null,
  "bathrooms": null,
  "sqft": null,
  "year": 2009,
  "makeModel": "Audi A5",
  "attributes": {
    "auto_make_model": "Audi A5",
    "auto_year": "2009",
    "auto_fuel_type": "gas",
    "auto_miles": "61,000",
    "auto_title_status": "clean",
    "auto_transmission": "automatic"
  },
  "imageCount": 11,
  "imageUrls": [
    "https://images.craigslist.org/00606_7sc4PAKnGfC_0CI0t2_600x450.jpg",
    "https://images.craigslist.org/00606_bdXmmCkm2g9_0CI0t2_600x450.jpg"
  ],
  "body": "2009 Audi A5 S line Premium Plus Prestige, 61k original miles. V6, automatic with optional paddle shift, navigation, sunroof and Audi door illumination. Clean title, never been in any type of accident. ...",
  "detailStatus": "ok",
  "searchSite": "sfbay",
  "searchCategory": "cto",
  "searchQuery": null,
  "uuid": "tPnFxHnsCVGL5rt7fEE4aN"
}
```

Without `includeDetails`, the same row has everything above except `body`, `postedAt`, `updatedAt`, `year`, `makeModel` and the posting-page attributes. `detailStatus` is then `not_requested`.

A job row from search alone (real output, some fields left out):

```json
{
  "type": "listing",
  "title": "Dedicated CDL-A Lane: Midwest-East - $0.75 CPM - Home Weekends",
  "price": null,
  "site": "chicago",
  "subareaName": "south chicagoland",
  "location": "Downers Grove, IL",
  "categoryName": "transportation",
  "section": "jobs",
  "attributes": {
    "compensation": "75 cents per mile",
    "jobTitle": "Hiring CDL-A OTR Drivers: Midwest-East Lane"
  }
}
```

#### Search row (real output, free)

```json
{
  "type": "search",
  "input": "sfbay",
  "site": "sfbay",
  "subarea": null,
  "category": "apa",
  "query": null,
  "status": "ok",
  "totalAvailable": 21042,
  "listingsDelivered": 3,
  "listingsScanned": 3,
  "requests": 1,
  "detailRequests": 3,
  "stopReason": "max_listings",
  "error": null,
  "charged": false
}
```

#### Listing fields

| Field | Meaning |
|---|---|
| `id`, `url`, `uuid` | Craigslist post id, the public link, and the key used in the link |
| `title`, `price`, `priceText` | `price` is a number in the site's currency. `null` when the listing shows no price. |
| `currency` | `USD`, `CAD`, `GBP`, `EUR` and so on, from the site |
| `site`, `siteName`, `subarea`, `subareaName`, `region`, `country` | Where the listing is posted |
| `location`, `neighborhood` | The place text the poster typed, and the neighborhood when Craigslist has one |
| `lat`, `lon` | Map point. Posters choose how exact it is. |
| `listedAt` | Last posted or renewed time (UTC). Craigslist sorts and filters by this date. |
| `postedAt`, `updatedAt` | First posted and last edited time. Details only. |
| `category`, `categoryName`, `section`, `sellerType` | Leaf category, its name, the section (for sale, housing, jobs...) and `owner` or `dealer` when the category says so |
| `odometer`, `year`, `makeModel` | Cars and other vehicles |
| `bedrooms`, `bathrooms`, `sqft` | Housing |
| `attributes` | Everything else Craigslist shows. With details, keys are Craigslist's own names, like `auto_title_status`, `laundry`, `pets_dog`, `employment_type`. A tick mark becomes `true`. |
| `imageCount`, `imageUrls` | Photo links at 600x450. Change `_600x450` to `_1200x900` in a link for the large size. |
| `body` | Full listing text with line breaks. Details only. Phone numbers and email addresses are replaced with `[phone removed]` and `[email removed]`. |
| `detailStatus` | `ok`, `not_requested`, `deleted` (the post was removed between search and opening), or `blocked` |
| `searchSite`, `searchCategory`, `searchQuery` | The search that found the row |
| `isNew` | `true` in alert mode |

#### Search status values

A bad input never stops the run. Every search gets one free `search` row:

- `ok`: listings were delivered.
- `no_results`: Craigslist has no listings for this search.
- `no_matching`: listings exist, but none passed your date, part-of-site or duplicate filters.
- `no_new`: alert mode found nothing that earlier runs had not returned.
- `invalid_input`: unknown site, region, category or link. The `error` field says what to fix.
- `blocked`: Craigslist refused every retry and every proxy tier.
- `cut_by_cost_limit`, `skipped_cost_limit`, `skipped_max_listings`: your cost limit or total listing limit was reached.

`stopReason` says why a search ended: `max_listings`, `end_of_results`, `date_limit`, `reached_last_run`, `max_total_listings` or `cost_limit`.

The `RUN_SUMMARY` record in the key-value store lists every search with its status and requests. It also lists status codes, retries, the proxy tier used and the bytes downloaded per 1,000 listings.

### How searches add up

One search is one site (or part of a site) x one category x one search word list entry. Four sites, three categories and two search words make 24 searches. `maxListingsPerSearch` caps each search. `maxListings` caps the whole run.

A listing that two of your searches find is delivered and charged once per run.

Craigslist marks repeated posts of the same item. With `skipDuplicates` on (the default), only the newest post of each group is kept.

Small sites fill their search page with listings from nearby sites. These are left out unless you turn on `includeNearby`, so each listing appears under its own site.

### Deal alerts on a schedule

1. Turn on `onlyNew` and give the list a `stateName`.
2. The first run returns the newest listings as a baseline. It remembers their ids and the newest listing time per search.
3. Each later run returns only listings posted or renewed since the last run that it has not returned before. Renewed listings keep their id, so they do not come back. These rows have `isNew: true`. Their search row has `alert: "new_listings"`.
4. If more than `maxListingsPerSearch` new listings arrived between two runs, you get the newest that many.
5. If a cost limit cuts a run, the next run still returns what was cut.

State lives in the named key-value store `craigslist-listings-state`, under `STATE-<stateName>`. Use different names for different lists.

To schedule it, open the actor in Apify Console, save your input as a task, then add a schedule to the task. Every 15 minutes to every hour works well for deals. When nothing is new, the run outputs only the free search rows and you pay nothing for listings. Connect the task to email, Slack or a webhook in Apify integrations to get the alert.

### Pricing

You pay per listing row: **$0.80 per 1,000 listings** ($0.0008 per listing). Opening each listing for full text costs nothing extra.

- Search status rows are free.
- Listings removed by your filters are free.
- A listing found by two of your searches is charged once.
- Set a maximum cost per run in Apify. The actor stops cleanly at that limit and marks the searches it could not finish.

Platform usage is included in the price.

### Speed and limits

Measured on 2026-10-26 from a home connection, no proxy, 256 MB memory:

| Run | Listings | Requests | Time | Downloaded |
|---|---|---|---|---|
| Empty input (sfbay, for sale) | 20 | 1 | 4 s | 47 KB |
| 4 cities x 3 categories, 400 each, search data | 4,368 | 12 | 33 s | 2.3 MB |
| One category, 2,500 listings | 2,500 | 5 | 15 s | 780 KB |
| All 27 Texas sites, gigs, last 2 days | 76 | 27 | 9 s | 292 KB |
| 4 cities x 3 categories, 25 each, with details | 300 | 312 | 95 s | 1.6 MB |
| 4 cities x 3 categories, 100 each, with details | 1,196 | 1,212 | 367 s | 3.7 MB |

- Search data comes in pages of 360 or 1,080 listings, so search-only runs are fast: about 7 seconds per 1,000 listings.
- Details need one request per listing. The actor waits 0.3 seconds between requests to stay polite, so expect about 5 minutes per 1,000 listings with details.
- Craigslist removes a listing after 30 to 45 days, or when the poster deletes it. A removed listing shows `detailStatus: "deleted"`.
- Photo links point to Craigslist's image server and stop working when the listing is removed.
- `postedWithinDays` uses `listedAt`, the date Craigslist sorts by. A renewed listing counts as recent.
- Proxy: requests start with no proxy. If Craigslist keeps refusing, the actor moves to Apify datacenter proxy, then US residential. No proxy was needed in our tests.

### Privacy

The actor reads only public listing data. It never logs in, never replies to posts, and never collects the hidden phone or email behind Craigslist's reply button. Phone numbers and email addresses that posters type into the listing text are removed before output. The street address of housing listings is not output; the map point and neighborhood are. You are responsible for using the data in line with the laws that apply to you, such as CCPA, GDPR or Saudi PDPL.

### Input reference

| Field | Default | What it does |
|---|---|---|
| `sites` | empty (sfbay when nothing else is set) | Sites, parts of sites (`sfbay/sby`) or site links |
| `regions` | empty | Every site in a state or province: `US-TX`, `TX`, `CA-ON` |
| `countries` | empty | Every site in a country: `US`, `CA`, `GB` |
| `categories` | `["sss"]` | Category codes or words |
| `searchQueries` | empty | One search per entry |
| `titlesOnly` | `false` | Match search words in titles only |
| `minPrice`, `maxPrice` | empty | Price range in the site's currency |
| `postedToday` | `false` | Posted today only |
| `postedWithinDays` | empty | Posted or renewed in the last N days |
| `hasImages` | `false` | Only listings with photos |
| `sellerType` | `any` | `any`, `owner` or `dealer` |
| `postalCode`, `searchDistanceMiles` | empty | Distance from a ZIP or postal code |
| `searchUrls` | empty | Craigslist search links |
| `sort` | `newest` | `newest`, `relevance`, `priceAsc`, `priceDesc`, `oldest` |
| `maxListingsPerSearch` | `20` | Cap per search |
| `maxListings` | `0` | Cap for the whole run, 0 = none |
| `includeDetails` | `false` | Open each listing for text and all attributes |
| `skipDuplicates` | `true` | Keep the newest post of each repeated group |
| `includeNearby` | `false` | Keep listings from nearby sites |
| `onlyNew` | `false` | Deal alert mode |
| `stateName` | `default` | Separate memory per alert list |
| `proxyConfiguration` | off | Your own proxy; turns the fallback off |
| `autoProxyFallback` | `true` | No proxy, then datacenter, then residential |
| `allowResidentialFallback` | `true` | Allow the residential step |
| `maxConcurrency` | `2` | Searches run in parallel |

# Actor input Schema

## `sites` (type: `array`):

Site names from the Craigslist link, for example sfbay (https://sfbay.craigslist.org), newyork, chicago, london, toronto. You can also paste a site link, use the site's full name ("SF bay area"), or add one part of a site with a slash: sfbay/sby (south bay), newyork/brk (Brooklyn). Empty sites, states, countries and links = sfbay.

## `regions` (type: `array`):

Search every Craigslist site in a state or province. Use US-TX or just TX for a US state, CA-ON for a Canadian province. Texas alone has 27 sites, so keep 'Max listings per search' small.

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

Search every Craigslist site in a country. Use 2-letter codes: US, CA, GB, AU, DE. The US has over 400 sites.

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

Craigslist category codes or plain words. For sale: sss (all), cta (cars & trucks), cto (cars by owner), ctd (cars by dealer), mca (motorcycles), boa (boats), rva (RVs), bia (bikes), fua (furniture), ela (electronics), zip (free stuff), tia (tickets). Housing: hhh (all), apa (for rent), roo (rooms), sub (sublets), rea (for sale). Jobs: jjj. Gigs: ggg. Services: bbb. Community: ccc. Events: eee. Words like "cars", "housing", "jobs" also work.

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

Words to search for, one search per line, for example "honda civic" or "standing desk". Leave empty for all listings in the category.

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

Match the search words in the listing title only. Off = title and text.

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

In the site's own currency (USD in the US, CAD in Canada, GBP in the UK).

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

In the site's own currency.

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

Craigslist's own 'posted today' filter.

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

Keep only listings posted or renewed in the last N days. Craigslist sorts by this date. Empty = no limit.

## `hasImages` (type: `boolean`):

Only listings with at least one photo.

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

For 'for sale' categories. owner = private sellers only. dealer = dealers and businesses only.

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

Search around this code. Use with 'Distance from ZIP'.

## `searchDistanceMiles` (type: `integer`):

Only listings within this many miles of the ZIP or postal code.

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

Paste search links copied from your browser, for example https://www.craigslist.org/search/area/sfbay?cat=bik\&query=trek. Every filter in the link is kept. Filters above are added when the link does not set them.

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

Order of results. Alert mode always uses newest first.

## `maxListingsPerSearch` (type: `integer`):

Each site x category x search word is one search. This caps each one.

## `maxListings` (type: `integer`):

Stops the whole run after this many listings. 0 = no total limit.

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

Adds the full listing text, every attribute (make/model, year, title status, bathrooms, laundry, pets and more), the first-posted and updated dates and an exact map point. One extra request per listing, so runs are slower. Phone numbers and emails in the text are removed.

## `skipDuplicates` (type: `boolean`):

Craigslist marks posts that repeat another post. On = keep only the newest one of each group.

## `includeNearby` (type: `boolean`):

Small sites add results from nearby sites to fill the page. Off = only listings from the sites you chose.

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

Remembers the listings each search returned. The first run returns the newest listings as a baseline. Later runs return only listings that were not returned before. Use it on a schedule.

## `stateName` (type: `string`):

Separate names keep separate memory, for example 'bike-deals' and 'apartments-sf'. Stored in the named key-value store 'craigslist-listings-state'.

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

Leave off to use the automatic fallback below. If you set a proxy here, it is used for every request and the fallback is off.

## `autoProxyFallback` (type: `boolean`):

Starts without a proxy. If Craigslist keeps refusing requests, switches to Apify datacenter proxy, then residential.

## `allowResidentialFallback` (type: `boolean`):

Turn off to never use residential proxy.

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

Requests are still spaced out across all searches. Higher is faster and more likely to be rate-limited.

## Actor input object example

```json
{
  "sites": [
    "sfbay",
    "newyork"
  ],
  "categories": [
    "cta"
  ],
  "titlesOnly": false,
  "postedToday": false,
  "hasImages": false,
  "sellerType": "any",
  "sort": "newest",
  "maxListingsPerSearch": 20,
  "maxListings": 0,
  "includeDetails": true,
  "skipDuplicates": true,
  "includeNearby": false,
  "onlyNew": false,
  "stateName": "default",
  "autoProxyFallback": true,
  "allowResidentialFallback": true,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

All rows. Filter by the type field.

## `runSummary` (type: `string`):

Status per search, requests, bytes per 1,000 listings, retries and proxy tier used.

# 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 = {
    "sites": [
        "sfbay",
        "newyork"
    ],
    "categories": [
        "cta"
    ],
    "maxListingsPerSearch": 20,
    "includeDetails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/craigslist-listings").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 = {
    "sites": [
        "sfbay",
        "newyork",
    ],
    "categories": ["cta"],
    "maxListingsPerSearch": 20,
    "includeDetails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("dottti/craigslist-listings").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 '{
  "sites": [
    "sfbay",
    "newyork"
  ],
  "categories": [
    "cta"
  ],
  "maxListingsPerSearch": 20,
  "includeDetails": true
}' |
apify call dottti/craigslist-listings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dottti/craigslist-listings"
        }
    }
}
```

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/LoRk1jAqsqGAO14ZP/builds/hynIfOagtHfW9YThA/openapi.json
