# Craigslist Scraper: New Listings Alerts & Price Drops (`scrapewise/craigslist-scraper`) Actor

Scrape Craigslist listings from any city: cars, apartments, real estate, for sale, jobs, gigs and services. Paste a search link or pick cities, category, keyword and filters. Schedule it: only new listings and price drops. Error rows are free.

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

## Pricing

from $0.68 / 1,000 listing delivereds

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: new listings alerts and price drops

Scrape [Craigslist](https://www.craigslist.org) listings from **any city site, in many cities in one run**, without an
account, cookies or a browser. Paste a search link with your filters, or pick cities (New York, Los Angeles, San
Francisco, Chicago...), a category (cars, apartments, real estate, for sale, jobs, gigs, services...), a keyword and
filters. Every listing comes with title, price, posting date, city, subarea and neighborhood, approximate map
coordinates, odometer for vehicles, bedrooms and square feet for housing, compensation for jobs, and every photo.

Built for **alerts**: schedule the same search every hour or every morning and get **only the listings that are
new**, plus the ones **whose price changed**, with the previous price and the drop in dollars and percent. A run with
nothing new costs nothing.

### At a glance

- **Price per 1,000 listings, Free plan:** US$ 0.80 (down to US$ 0.68 on Gold and above)
- **Full listing page (optional):** US$ 0.533 per 1,000, on top
- **Fee per run start:** None
- **Search by Craigslist link or by cities + category + keyword + filters:** Yes
- **Many cities in one run:** Yes, one search per city
- **Only new listings since the last run (`onlyNew`):** Yes, free when nothing is new
- **Price tracking between runs (`trackPriceChanges`, `onlyPriceDrops`):** Yes
- **Up to 10,000 listings per search:** Yes
- **Advertiser name, phone or e-mail:** Never (see below)
- **Error rows (bad link, unknown city, no results, timeout too short):** Free, with an `errorCode`

### One real row

From a test run on 2026-10-06 (city "San Francisco", apartments for rent, newest first, full listing pages on),
shortened:

```json
{
  "type": "listing",
  "id": "7979141319",
  "url": "https://www.craigslist.org/view/d/san-francisco-sunlit-bernal-heights/uf5goFJDfDKpdzgtw6944s",
  "title": "Sunlit Bernal Heights Cottage in Prime Location",
  "price": 4800,
  "priceFormatted": "$4,800",
  "currency": "USD",
  "category": "apa",
  "categoryName": "apartments / housing for rent",
  "section": "housing",
  "postedAt": "2026-10-06T21:51:35Z",
  "city": "sfbay",
  "cityName": "SF bay area",
  "region": "CA",
  "subarea": "sfc",
  "subareaName": "san francisco",
  "locationText": "San Francisco",
  "latitude": 37.74,
  "longitude": -122.42,
  "bedrooms": 2,
  "bathrooms": 1,
  "housingType": "house",
  "laundry": "w/d in unit",
  "parking": "attached garage",
  "rentPeriod": "monthly",
  "attributes": {"cats are OK - purrr": true, "dogs are OK - wooof": true},
  "imageCount": 3,
  "thumbnail": "https://images.craigslist.org/00z0z_cTkzfI4oaCw_0CI0pR_600x450.jpg",
  "createdAt": "2026-10-06T21:06:35Z",
  "isRepost": false,
  "hasDetails": true
}
```

A car from Los Angeles in the same test came with `"odometerMiles": 120306`, `"sellerType": "dealer"`, and from the
listing page `"year": 2021`, `"makeModel": "Tesla Model 3 AWD Long Range w/Boost"`, `"condition": "excellent"`,
`"fuel": "electric"`, `"titleStatus": "clean"`, `"transmission": "automatic"`, `"bodyType": "sedan"`.

### What you can do with it

- **Car flippers and buyers:** a Task per city that checks cars by owner under a price every hour and sends only new
  listings and price drops to Slack, e-mail or Google Sheets.
- **Renters and relocation teams:** daily alerts of new apartments in New York, Los Angeles or San Francisco that
  match budget, bedrooms and ZIP code.
- **Resellers and collectors:** watch furniture, electronics, tools or free stuff in several cities at once for a
  keyword.
- **Recruiters and job seekers:** new jobs and gigs by city and keyword, with compensation and employment type.
- **Market research:** asking prices of used cars, rents per bedroom or item prices by city and neighborhood, with
  coordinates for maps.
- **Datasets and AI:** clean, typed rows (numbers as numbers, dates as ISO), ready for spreadsheets and models.

### Input

| Field | What it does |
|---|---|
| `startUrls` | Craigslist search links with your filters (every filter in the link is kept), or listing links (returned with the full listing page) |
| `cities` | City names (`New York`, `Los Angeles`, `San Francisco`, `Portland, OR`), boroughs or subareas (`Brooklyn`, `Manhattan`) or site names (`newyork`, `sfbay`); one search each |
| `category` | `allForSale` (default), `cars`, `carsByOwner`, `carsByDealer`, `motorcycles`, `apartmentsForRent`, `realEstateForSale`, `rooms`, `allJobs`, `gigs`, `allServices`, `furniture`, `electronics`, `free` and more |
| `categoryCode` | Any Craigslist category code from a link (`cto`, `sof`, `fuo`...); overrides `category` |
| `query` | Keyword, as in the Craigslist search box |
| `searchTitlesOnly` | Match the keyword in titles only |
| `minPrice`, `maxPrice` | Price range (monthly rent for rentals) |
| `hasPic`, `postedToday` | Only listings with photos; only listings posted today |
| `postalCode`, `searchDistance` | Search around a ZIP or postal code, radius in miles |
| `minYear`, `maxYear`, `minMiles`, `maxMiles`, `makeModel` | Vehicle filters |
| `sellerType` | `any` (default), `owner` or `dealer` |
| `minBedrooms`, `maxBedrooms`, `minBathrooms`, `minSqft`, `maxSqft` | Housing filters |
| `sortBy` | `newest` (default), `oldest`, `priceLowToHigh`, `priceHighToLow`, `relevance` |
| `maxItems` | Listings per search, default 50. 0 = up to 10,000 |
| `includeDetails` | Also read each listing page (full description, attributes, every photo), charged as a separate event |
| `onlyNew` | Deliver only listings not delivered in earlier runs of the same search |
| `trackPriceChanges` | Add the price from the previous run and the change; with `onlyNew`, also deliver seen listings whose price changed |
| `onlyPriceDrops` | With `onlyNew` and `trackPriceChanges`: of the seen listings, only price drops |
| `proxyConfiguration` | Default Apify Proxy (datacenter) |

Example: new Tesla listings under 30,000 dollars in Los Angeles and San Francisco, every hour:

```json
{
  "cities": ["Los Angeles", "San Francisco"],
  "category": "cars",
  "query": "tesla",
  "maxPrice": 30000,
  "minYear": 2019,
  "maxItems": 100,
  "onlyNew": true,
  "trackPriceChanges": true
}
```

Or paste the search from your browser:

```json
{
  "startUrls": ["https://newyork.craigslist.org/search/brk/apa?min_bedrooms=2&max_price=4000"],
  "maxItems": 200
}
```

### How alerts work

The Actor remembers each search (same link, or same city, category, keyword and filters) in a named key-value store
in your own account, `craigslist-scraper-memory`: listing id, last price, first and last time seen.

- **`onlyNew`:** the first run delivers up to `maxItems` and marks the page it read as seen. Later runs read the
  newest listings first and stop when there is nothing new. A run with nothing new delivers nothing and costs nothing.
- Craigslist puts **renewed** listings back on top. A renewed listing you already have is not delivered again (unless
  its price changed and you track prices). Old listings that the first run never reached are recorded as a price
  baseline, not delivered as "new".
- **`trackPriceChanges`:** every row gets `isNewListing`, `previousPrice`, `priceChange`, `priceChangePct`,
  `isPriceDrop`, `firstSeenAt` and `previousSeenAt`. `maxItems` then counts listings checked, so the cost of a daily
  run stays flat.
- **Both on:** you get new listings plus listings seen before whose price changed.
- Delete the key-value store to start over.

### Output

Every listing row: `id` (Craigslist posting id), `uuid`, `url`, `title`, `price`, `priceFormatted`, `currency`,
`category` (Craigslist code), `categoryName`, `section` (`for sale`, `housing`, `jobs`, `gigs`, `services`,
`community`, `events`), `sellerType` (`owner` or `dealer` in for-sale categories), `postedAt` (posted or renewed,
UTC), `city` (site name), `cityName`, `region`, `country`, `subarea`, `subareaName`, `neighborhood`, `locationText`
(the place the advertiser typed), `latitude`, `longitude`, `odometerMiles`, `bedrooms`, `sqft`, `salary`, `company`,
`jobTitle`, `monthlyPayment`, `images` (1200x900), `imageCount`, `thumbnail`, `position`, `searchUrl`, `scrapedAt`.

With price tracking: `isNewListing`, `previousPrice`, `priceChange`, `priceChangePct`, `isPriceDrop`, `firstSeenAt`,
`previousSeenAt`.

With full listing pages (`hasDetails: true`): `description`, `attributes` (every attribute Craigslist shows, as
label and value), `year`, `makeModel`, `condition`, `transmission`, `fuel`, `titleStatus`, `bodyType`, `drive`,
`paintColor`, `cylinders`, `bathrooms`, `housingType`, `laundry`, `parking`, `rentPeriod`, `employmentType`,
`compensation`, `createdAt` (first posted), `updatedAt`, `isRepost`, and every photo. If a listing page cannot be
read, the row still comes with `hasDetails: false` and `detailsError`, and only the listing is charged.

**No advertiser contact data, by design.** Most Craigslist advertisers are private people. The Actor never returns a
name, phone number or e-mail, never returns the street address a listing may carry, and replaces phone numbers and
e-mail addresses inside titles, place names and descriptions with `[contact removed]`. Coordinates are rounded to 2
decimals (about 1 km). Use the listing `url` to reply through Craigslist.

### Error codes (never charged)

| `errorCode` | Meaning |
|---|---|
| `INVALID_INPUT` | A value the Actor does not understand (minimum above maximum, unknown category, a link that is not a Craigslist search or listing, a search Craigslist rejects) |
| `NOT_FOUND` | No Craigslist site for this city name (the row lists the closest names), or the listing was deleted |
| `NO_RESULTS` | No listing for this search and filters in this city |
| `BLOCKED` | Craigslist refused every attempt on new IPs; run again |
| `TIMEOUT_TOO_SHORT` | The run timeout was too short for even the first page; raise it |
| `NOT_REACHED` | The run timeout arrived before this search or listing |
| `UNEXPECTED` | A page came in a shape we did not expect; the rest of the run goes on |

### Pricing

| Event | Per 1,000 |
|---|---|
| Listing delivered | US$ 0.80 |
| Full listing page (only with `includeDetails` or listing links) | US$ 0.533 |

An hourly `onlyNew` alert on a busy search that finds 5 new cars per hour costs about US$ 0.10 a day. Error rows,
duplicates, listings already seen, listings with an unchanged price and listings from nearby areas are free, and
there is no start fee.

### Limitations

- When a city has few listings for a search, Craigslist fills the page with listings from nearby areas. Those are
  left out (they belong to other city sites); add those cities to `cities` if you want them.
- Craigslist lists at most 10,000 listings per search. For more, split by subarea, price range or keyword.
- `postedAt` is the date Craigslist shows, which moves when a listing is renewed; `createdAt` (listing page) is the
  first posting date.
- Descriptions and attributes are as the advertiser wrote them.
- This Actor reads public pages only.

### FAQ

**Do I need a Craigslist account?** No. No login, no cookies, and the default datacenter proxy works.

**How do I write a city?** As you would say it: `New York`, `Los Angeles`, `San Francisco`, `Washington DC`,
`St. Louis`, `Portland, OR`, `Toronto`, `London`. Capitals and extra spaces do not matter, and a borough or subarea
such as `Brooklyn` searches only that part. The site name from the address bar (`sfbay`, `newyork`) also works.

**Can I paste a search from my browser?** Yes. Keyword, price, year, odometer, bedrooms, ZIP and every other filter
in the link are kept.

**How do I get an alert only when a price drops?** Turn on `onlyNew`, `trackPriceChanges` and `onlyPriceDrops`,
schedule the Task and send the dataset to your integration.

**Can I use it from n8n, Make, Zapier or an AI agent?** Yes, through the Apify app or the Apify MCP server. Error
rows are free, so an agent can explore cheaply.

**What if a run is cut by its timeout?** The Actor stops shortly before the limit and ends successfully with what it
delivered, telling you in the status message how to get the rest. If the timeout is too short for even one page, you
get a free `TIMEOUT_TOO_SHORT` row instead of an empty run.

### Changelog

- **0.1 (2026-10-07):** first version. Search by Craigslist link or by cities with category, keyword, price, vehicle,
  housing, seller, photo, posted-today and ZIP filters; many cities per run; up to 10,000 listings per search;
  optional full listing pages; `onlyNew` and price tracking between runs for scheduled alerts; free error rows.

# Actor input Schema

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

Search results links from any Craigslist city site, one per line, for example https://newyork.craigslist.org/search/cta?query=tesla\&max_price=40000. Set your filters on the site, copy the link from the address bar and paste it here: every filter in the link is kept. Listing links (one ad) also work and return the full listing page. Leave empty to search with the fields below. Also accepts 'searchUrls' and 'urls'.

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

Craigslist sites to search, one per line: a city name (New York, Los Angeles, San Francisco, Chicago, Portland, ME), a borough or subarea (Brooklyn, Manhattan) or the site name from the address bar (newyork, losangeles, sfbay). Each city is a separate search with the category, keyword and filters below, and each row says which city it came from. Empty = New York. Also accepts 'city', 'locations' and 'sites'.

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

What to search in each city, from the Craigslist sections: for sale (cars, cars by owner or dealer, motorcycles, furniture, electronics, free stuff...), housing (apartments for rent, rooms, sublets, real estate for sale...), jobs, gigs, services, community and events. Empty = all for sale. For a section not in the list, use 'Category code'.

## `categoryCode` (type: `string`):

Advanced. Any Craigslist category code from a search link (the three letters after /search/), for example cto (cars by owner), sof (software jobs) or fuo (furniture by owner). Overrides 'Category' when filled.

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

Words to search for, as typed in the Craigslist search box, for example tesla model 3, iphone 15, studio or remote. Empty = every listing in the category. Also accepts 'searchTerm' and 'keyword'.

## `searchTitlesOnly` (type: `boolean`):

Off (default): the keyword is matched in the title and the description. On: only in the listing title, which gives fewer and more precise results.

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

Only listings at or above this price, as a whole number in the city's currency (monthly rent for rentals), for example 5000. Empty = no minimum.

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

Only listings at or below this price, as a whole number in the city's currency, for example 40000. Empty = no maximum.

## `hasPic` (type: `boolean`):

Only listings with at least one photo. Off by default.

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

Only listings posted or renewed today, by Craigslist's own filter. Off by default. For a daily feed, 'Only new listings since the last run' is more precise.

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

Search around this ZIP or postal code, for example 10001 (Manhattan) or 94103 (San Francisco). Use it with a single city; the radius is set below.

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

Radius in miles around the ZIP or postal code, for example 25. Default 10. Used only when 'Near ZIP / postal code' is filled.

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

Vehicles: only this model year or newer, for example 2018. Works in cars, motorcycles, RVs and boats.

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

Vehicles: only this model year or older, for example 2022.

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

Vehicles: only listings with at least this many miles on the odometer, for example 10000.

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

Vehicles: only listings with at most this many miles on the odometer, for example 80000.

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

Vehicles: Craigslist's make and model filter, as free text, for example toyota camry or honda.

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

For sale categories: Any (default), Owner (private sellers only) or Dealer (dealers only).

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

Housing: only listings with at least this many bedrooms, for example 2.

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

Housing: only listings with at most this many bedrooms.

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

Housing: only listings with at least this many bathrooms, for example 1.

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

Housing: only listings with at least this many square feet, for example 600.

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

Housing: only listings with at most this many square feet.

## `sortBy` (type: `string`):

Craigslist's own orders: Newest first (default), Oldest first, Price low to high, Price high to low, or Relevance. With a limit it also decides which listings you get, for example Price low to high plus 50 returns the 50 cheapest. 'Only new listings' always uses newest first.

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

Stop each search (each link and each city) after this many listings, for example 50 (default). Craigslist lists at most 10,000 per search. 0 = as many as the site shows. You pay per listing delivered, so this caps the cost of each search.

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

For scheduled runs. Remembers which listings each search (with its filters) already returned, in a named key-value store in your account ('craigslist-scraper-memory'), and delivers only listings never returned before. Seen listings are not delivered and not charged. Uses the newest-first order. A run with nothing new costs nothing. Delete the store to start over.

## `trackPriceChanges` (type: `boolean`):

Remembers the last price of every listing per search (same key-value store) and adds previousPrice, priceChange, priceChangePct and isPriceDrop to each row. Together with 'Only new listings', a run delivers new listings plus listings whose price changed since the last run.

## `onlyPriceDrops` (type: `boolean`):

With both options above on, listings you already have come back only when their price went down (price increases are skipped and not charged).

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

Off (default): rows come from the search results (title, price, location, coordinates, first photo, date). On: also reads each listing's own page and adds the full description, attributes (year, make and model, condition, odometer, fuel, transmission, title status, bathrooms, housing type, laundry, parking, employment type, compensation), every photo and the date first posted. One extra request per listing, charged as a separate 'Listing details page' event.

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

Apify Proxy, default group, is used by default. If Craigslist keeps refusing it, the Actor switches to a stronger proxy group on its own, with no extra charge. Change it only to use your own proxies.

## Actor input object example

```json
{
  "cities": [
    "New York",
    "Los Angeles"
  ],
  "category": "cars",
  "query": "tesla",
  "searchTitlesOnly": false,
  "hasPic": false,
  "postedToday": false,
  "sellerType": "any",
  "sortBy": "newest",
  "maxItems": 50,
  "onlyNew": false,
  "trackPriceChanges": false,
  "onlyPriceDrops": false,
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `resultsCsv` (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 = {
    "cities": [
        "New York",
        "Los Angeles"
    ],
    "category": "cars",
    "query": "tesla"
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/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 = {
    "cities": [
        "New York",
        "Los Angeles",
    ],
    "category": "cars",
    "query": "tesla",
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/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 '{
  "cities": [
    "New York",
    "Los Angeles"
  ],
  "category": "cars",
  "query": "tesla"
}' |
apify call scrapewise/craigslist-scraper --silent --output-dataset

```

## MCP server setup

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