# Craigslist Scraper & Tracker - Cars, Housing, Price Drops & MCP (`akhil_rajesh/craigslist-scraper`) Actor

Craigslist scraper, tracker and API for cars, apartments, housing, jobs and for sale by keyword, city, state or link: price, owner or dealer, photos, car and rental details. Only changes returns new listings, price drops and gone listings, with Slack, Discord or Teams alerts and MCP.

- **URL**: https://apify.com/akhil_rajesh/craigslist-scraper.md
- **Developed by:** [Akhil rajesh](https://apify.com/akhil_rajesh) (community)
- **Categories:** E-commerce, Real estate, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 listings

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 & Tracker

Scrape **Craigslist** listings (**cars and trucks, apartments and housing, jobs, and everything for sale**) by keyword, city and category, or from Craigslist search links: title, **price and currency**, posted date, place with **map point**, **owner or dealer**, photos, a car's **odometer** and a rental's **bedrooms and size**. Read listings' own pages too for the **full text, first-posted and updated dates, and every attribute**: a car's year, make, model, condition, title status and transmission; a rental's bathrooms and features. Search **one city, several, or a whole state** in one run.

**One price, no extras: $1.00 per 1,000 listings, with platform usage included and no row cap on Apify's free plan.** (Apify's Store page lists platform usage too, because the live API below adds its own; ordinary runs never do.)

It is also a **Craigslist tracker and monitor**. Schedule it with **Only changes** and each run returns only what moved since the last one: **new listings, price drops, price increases, listings gone** (sold or deleted) and **ads posted again**, each with **every price the tracker has seen for the listing**, posted to **Slack, Discord or Microsoft Teams** if you like. It runs as a **live API and MCP server** for AI agents too.

### Why this scraper

- **It tracks changes, not just listings.** Most Craigslist scrapers return the same listings every run, and you work out what changed. This one keeps a history per search and returns only new listings, price drops and rises, and listings that left, each one once.
- **Reposts aren't new listings.** Craigslist sellers delete an ad and post it again to bring it back to the top. A new listing with the same title at the same map point as one that left is one `relisted` change, with the old listing's id and price, not a new listing plus a gone one. Copies of the same ad that Craigslist itself groups are shown once.
- **Gone means gone.** A listing missing from a search is checked on its own page first: Craigslist's "page not found" reports it gone at once, while one that was only edited out of your search isn't reported at all.
- **Craigslist's own filters, not a guess.** Price range, owner or dealer, has photos, titles only, distance from a ZIP code, make and model, year, odometer and bedrooms go to Craigslist with the search, so a run reads only what matches.
- **Every listing, fast.** The Actor reads Craigslist's own search feed: up to 10,000 listings per search, 1,080 at a time. A 1,000-listing search takes a few seconds.
- **Whole states in one run.** Name `CA` or `Texas` and each Craigslist area in it is searched (up to 100 searches a run).
- **No contact details, no runaway bills.** Phone numbers and emails are never read. Every run stops at Max results (1,000 unless you set it) or at your maximum cost per run, and searches Craigslist can't run are free.

| | Typical Craigslist scrapers | This Actor |
|---|---|---|
| New listings, price drops, gone listings since the last run | You compare runs yourself (some small monitors do new listings) | Built in (Only changes), with reposts paired |
| Alerts to Slack, Discord, Teams | Rare | Yes |
| Price history | None | Every change row carries the prices your tracker saw |
| Craigslist's own filters (price, owner/dealer, distance, make and model, year, miles, bedrooms) | Some | Yes, sent with the search |
| Full text, attributes, first-posted and updated dates | Some | Yes, from listing pages (up to 200 a run) |
| A whole state in one run | Some | Yes |
| MCP server for AI agents | Rare | Its own (4 tools) |
| Price per 1,000 listings | About $1.30 to $6.00, some plus a fee per run or per email | $1.00, less on paid plans, listing pages included |

### Use cases

- **Craigslist alerts**: a schedule every hour for "Toyota Tacoma" or "road bike" in your city that posts each new listing and price drop to Slack or Discord, before anyone else calls.
- **Used car deals**: cars by owner under a price, by make and model, year and odometer, and every price drop.
- **Rental watch**: new apartments and rooms in your area with bedrooms, size and features, and rent drops over time.
- **Resellers and flippers**: underpriced furniture, electronics or free stuff in your area, the moment they're posted.
- **Market research**: how many listings a search has and their median price, across a city or a whole state (in the run summary).
- **AI agents**: ask an assistant "which Honda Civics under $8,000 are for sale by owner in the SF bay area?" through the MCP server.

### How to use it in 4 steps

1. Add **Searches**: keywords such as `honda civic` or `couch`, or Craigslist search links such as `https://sfbay.craigslist.org/search/cta?query=civic`.
2. Add **Locations**: Craigslist areas (`SF bay area`, `New York`, `Los Angeles`, `Toronto`), a part of one (`Manhattan, NY`), or a state (`CA`) for every area in it. Pick a **Category** if you like (`cars`, `apartments`, `furniture`); a category without a keyword reads the whole category.
3. Set **Max results** and any **filters**, and run it.
4. To track changes, switch on **Only changes since the last run**, add an **Alert webhook URL** if you want chat alerts, and schedule it.

### What you get

One row per listing (`type: "listing"`). Example (a real row, Honda Civics by owner in the SF bay area, with its listing page read; shortened):

```json
{
    "type": "listing",
    "id": "7988741523",
    "search": "'honda civic' in cars & trucks, SF bay area",
    "title": "2013 Honda Civic ( Smogged/ Back up Camera)",
    "price": 3700,
    "currency": "USD",
    "postedAt": "2026-10-23T20:44:04Z",
    "location": "Concord",
    "areaName": "SF bay area",
    "subareaName": "east bay area",
    "latitude": 37.989,
    "longitude": -122.005,
    "category": "cars & trucks - by owner",
    "sellerType": "owner",
    "odometer": 201952,
    "odometerUnit": "mi",
    "imageCount": 5,
    "year": 2013,
    "makeModel": "honda civic",
    "condition": "excellent",
    "titleStatus": "clean",
    "transmission": "automatic",
    "fuel": "gas",
    "drive": "fwd",
    "bodyType": "sedan",
    "postalCode": "94519",
    "firstPostedAt": "2026-10-23T20:44:04Z",
    "description": "Run excellent no mechanical issues, clean interior and exterior, very well maintained ...",
    "summary": "2013 Honda Civic ( Smogged/ Back up Camera) (201,952 mi; $3,700), by owner, Concord, posted 2026-10-23.",
    "url": "https://www.craigslist.org/view/d/concord-2013-honda-civic-smogged-back/gx51JGtT6XCmS7akibHVix"
}
```

Every row from a search has the title, price and currency, posted date, place and map point, the Craigslist area and its part, the category and seller type, photo links, and for cars the odometer, for housing the bedrooms and size. A **listing page** (Listing pages to read, or the Listings input) adds the full text, first-posted and updated dates, the postal code, every labelled attribute (`attributes`) and, for a car, the year, make and model, condition, cylinders, drive, fuel, colour, title status, transmission and type; for housing the bathrooms, availability, rent period and features (`features`: "cats are OK", "laundry in bldg", "off-street parking"...). A map address is kept for housing and dealers, and left out for private sellers' items.

**Prices**: `price` is in the area's currency (`currency`: USD, CAD, GBP...; monthly rent for rentals), and empty when the ad gives none. **Dates**: `postedAt` is the listing's last posting (a renewal moves it); `firstPostedAt`, from its page, is its first.

With **Only changes**, each row also has `change`; price changes have `previousPrice`, `changeAmount` and `changePercent`; and every change row has `trackedPrices` (the prices the tracker saw, `[{"date", "price"}]`, the latest 6) and `firstSeenDate`:

| change | meaning |
|---|---|
| `newListing` | a listing that wasn't in the search at the last run |
| `priceDrop` / `priceIncrease` | the price moved since the last run |
| `removed` | gone: its page answers "not found" (deleted or expired), or it was missing two runs in a row |
| `relisted` | the same ad posted again under a new id (the same title at the same map point; `relistOf` is the old id, `previousPrice` its price) |
| `firstRun` | the first run's baseline: the listings there now |

The dataset has views for **Listings**, **Changes**, **Cars and trucks**, **Housing** and **Listing pages** (a view picks columns: every view lists every row, so filter on `type` or `change` if you need to).

### How to use it

#### Searches, locations and category

A keyword is searched the way Craigslist's search box does (in titles and text; **Search titles only** narrows it), in each **Location** and the **Category** you set. Each keyword in each area is one search: `["honda civic", "toyota corolla"]` in `["SF bay area", "Sacramento"]` is four searches. A location is a Craigslist area by its name (`SF bay area`, `new york city`, `los angeles`, `chicago`, `toronto`, `london, UK`) or its host (`sfbay`), a part of an area (`Manhattan, NY`, `city of san francisco`, `east bay area`), a city with its state (`Bakersfield, CA`), a US state or Canadian province (`CA`, `Texas`, `Ontario`: every area in it), or a country (`Canada`). A category is Craigslist's name (`cars & trucks`, `cars & trucks - by owner`, `apartments / housing for rent`) or a plain word (`cars`, `apartments`, `rooms`, `furniture`, `free stuff`, `jobs`); empty means everything for sale.

A pasted Craigslist search link keeps its own area, category, keyword and filters (any of Craigslist's filter parameters), with this Actor's filters added on top.

Results come **newest first** unless you pick another **Order** (relevance, price low to high, price high to low). Craigslist lists at most 10,000 listings per search; **Max listings read per search** (1,000 by default) sets how far each search is read.

#### Listings

Craigslist listing links (`craigslist.org/view/d/<title>/<id>`, or an older link such as `sfbay.craigslist.org/sfc/cto/d/<title>/7954213800.html`), each read from its own page. A listing that is gone gets a free `notFound` row. (A post id alone can't be opened: Craigslist needs the link.)

#### Filters

Sent to Craigslist with each search: minimum and maximum price, owner or dealer, only listings with photos, search titles only, distance from a ZIP or postal code, and for cars the make and model, minimum and maximum year and maximum odometer, for housing the minimum and maximum bedrooms. Applied by the Actor: posted (or renewed) within N days, and words to leave out of titles (`wanted`, `parts`, `broken`). **Hide duplicates** (on) shows one listing per group of copies Craigslist itself groups; **Include nearby areas** (off) keeps the listings Craigslist adds to a small search from neighbouring areas.

### Tracking changes on a schedule (Craigslist tracker)

1. Switch on **Only changes since the last run**. Optionally pick the **Changes to return**.
2. Run it once: the first run returns the listings there now as `firstRun` (up to Max results) and saves them as the baseline.
3. Schedule it (Apify Console, Schedules). Each later run returns only the changes.

How it decides:

- Each search is read whole, up to **Max listings read per search**, and every listing is compared with the last run.
- A listing missing from a search read whole is checked on its own page (up to 50 a run): Craigslist's "page not found" reports it gone at once; a listing still up there (edited out of your search) is left alone. Without a page check it is reported gone when it is missing two runs in a row.
- A search read only in part (past Max listings read per search) can't tell a gone listing from one past the cut, so it reports no gone listings, and an older listing that moves into its window is recorded quietly rather than reported new.
- In a search read whole, a listing that wasn't there last run is new, however old: an ad edited into your search, or whose price dropped under your maximum, is a new match. A new listing with the title and map point of a listing that left is reported once as `relisted`.
- Each listing's prices are kept as the tracker sees them (the latest 6).
- Histories are kept per search and filters (two trackers with different filters don't mix), in the key-value store `craigslist-tracker-history`, or one you name in **History name**.
- Only what a run returns moves the history: if Max results or your spending limit stops a run, the changes it didn't return come with the next run. The most important come first: price drops, new listings, gone listings, reposts, price increases.

### Alerts in Slack, Discord or Microsoft Teams

Paste an incoming-webhook URL in **Alert webhook URL** (Slack, Discord, Microsoft Teams, or any HTTPS endpoint that takes JSON, such as Zapier, Make or n8n). It switches on Only changes. The first run posts one short "alerts are set up" message; each later run posts its changes (new listings and price drops by default, or those you pick in **Alert on**), up to 50 per run, most important first, each with the photo, price move, place and link. The URL is treated as a secret and masked in logs. If posting fails, the run still succeeds and its status says why.

### Run summary for pipelines

Each run saves a `RUN_SUMMARY` record in its key-value store: per search, Craigslist's count, when Craigslist made its list (`listedAt`), listings read, whether it was read whole, the median price, duplicates hidden and nearby listings left out; plus the changes, what didn't load and why, and the filters used. Every listing row also has a one-sentence `summary`, ready for a report, an alert or an AI agent.

### Craigslist API: use it from code or from an AI assistant

Start a run and get the rows in one call (replace `<YOUR_TOKEN>`; a call that waits gives up after 5 minutes, so start bigger runs without waiting and read the dataset afterwards):

```bash
curl -X POST "https://api.apify.com/v2/acts/akhil_rajesh~craigslist-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searches": ["road bike"], "locations": ["SF bay area"], "maxResults": 100, "maxPrice": 800}'
```

Or from Python with `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("akhil_rajesh/craigslist-scraper").call(run_input={
    "searches": ["honda civic"], "locations": ["Los Angeles", "Orange County, CA"], "category": "cars",
    "maxResults": 300, "minYear": 2015, "maxMiles": 120000, "sellerType": ["owner"],
})
for row in client.dataset(run.default_dataset_id).iterate_items():
    print(row["summary"])
```

#### For AI agents: MCP server and live API

The Actor also runs as a **live endpoint** (Apify Standby): an **MCP server** that agents call directly, and plain HTTP endpoints. Once a Standby run is up, a request is answered in seconds; the first request starts one. Authenticate with your Apify API token in the `Authorization: Bearer <token>` header.

**MCP server** (Streamable HTTP): `https://akhil-rajesh--craigslist-scraper.apify.actor/mcp`

```json
{"mcpServers": {"craigslist": {"type": "http", "url": "https://akhil-rajesh--craigslist-scraper.apify.actor/mcp",
  "headers": {"Authorization": "Bearer <YOUR_APIFY_TOKEN>"}}}}
```

In Claude Code:

```bash
claude mcp add --transport http craigslist https://akhil-rajesh--craigslist-scraper.apify.actor/mcp --header "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

A client that can't send headers can use `https://akhil-rajesh--craigslist-scraper.apify.actor/mcp?token=<YOUR_APIFY_TOKEN>`; keep that URL private, as it carries your token. Through **Apify's MCP server** (`https://mcp.apify.com`, for Claude, ChatGPT, Cursor and other clients), add the tools with `https://mcp.apify.com?tools=akhil_rajesh/craigslist-scraper` (they appear as `akhil_rajesh--craigslist-scraper--search_listings` and so on), then ask in plain words, for example *"find road bikes under $500 from owners in the SF bay area"*. Add them that way rather than through Apify's generic `call-actor` tool, which returns the rows only as text. Apify's MCP server doesn't offer Actors with a live endpoint to agents that pay per call with x402 or Skyfire: connect with an Apify account or API token.

| Tool | What it returns |
|---|---|
| `search_listings` | Listings for up to 3 searches (each keyword in each area) or Craigslist links, with price, posted date, place, owner or dealer, a car's odometer or a rental's bedrooms and a one-line summary; Craigslist's filters on price, seller, distance, make and model, year, miles and bedrooms |
| `get_listings` | Listings read from their own pages by link: full text, both dates, attributes |
| `find_areas` | Craigslist's areas for a place, state or country (free) |
| `run_actor` | The Actor's whole input in one call |

**HTTP endpoints**, such as `GET https://akhil-rajesh--craigslist-scraper.apify.actor/listings?searches=road bike|gravel bike&locations=SF bay area&max=100&maxPrice=800` (separate several searches, locations or listings with `|`; defaults in brackets). The Actor's **Endpoints** tab in Apify Console lists them too, and lets you try them.

| Endpoint | Parameters |
|---|---|
| `GET /listings` | `searches`, `locations`, `category`, `max` (100), `minPrice`, `maxPrice`, `seller`, `sort`, `postedWithinDays`, `exclude`, `photos`, `titlesOnly`, `postal`, `distance`, `makeModel`, `minYear`, `maxYear`, `maxMiles`, `minBeds`, `maxBeds`, `nearby`, `listingPages` (0) |
| `GET /listing` | `listings` (links) |
| `GET /areas` | `q` (a place, state or country; free) |
| `POST /run` | The Actor's whole input as the JSON body |

**Answers** carry the `rows`, `rowsByType`, `notFound` (searches or listings Craigslist can't show: free), `notLoaded` (what Craigslist didn't answer), `notRead` (what a limit left out: not charged) and `notes`. A request that reads nothing gets an `error` and nothing is charged: HTTP 400 for an argument it can't read (a tool error over MCP), 503 when Craigslist didn't answer or the run was busy (send it again), 402 when your spending limit for the run is reached (it lifts when the run ends, after 2 minutes without requests). A 503 can also carry rows, when the platform stopped the run while a request was being read: those were stored and charged, and `notRead` names the rest.

**Limits per request**: an MCP tool call reads up to 3 searches, 200 listings and 10 listing pages, about 350 kB of rows, within 50 seconds, and asks for 50 listings unless told otherwise (MCP clients stop waiting at 60 seconds, and Apify's MCP server drops answers over about 1 MB); an HTTP request reads up to 10 searches, 2,000 listings, 40 listing pages and about 6 MB within 170 seconds. A state is one search per area, so it is cut to these limits too. What a limit leaves out is named in `notRead` and isn't charged. **A request that has started runs to its end even if you stop waiting**: Apify's Standby proxy keeps it open, so its rows are read and charged. Ask for what you need. Tracking (Only changes) and alerts belong to scheduled runs: the live endpoint answers each request on its own, so it ignores those fields.

### How much does it cost?

You pay **$1.00 per 1,000 listings**, less on paid Apify plans, plus a start fee of $0.00005 per run. **Platform usage is included** in runs of the Actor: there is no compute charge on top, and no row cap on Apify's free plan. (The Store page's pricing box mentions platform usage because of the live endpoint, which charges its own; ordinary runs never do.) Listing pages are included in the listing's price. Not-found rows are free. With Only changes you pay only for the changes returned. The live endpoint (MCP server and HTTP API) charges listings at the same prices, plus the Standby run's platform usage while it serves you and until it has been idle for 2 minutes (a short session costs a fraction of a cent).

| Apify plan | Per 1,000 listings |
|---|---|
| Free | $1.00 |
| Starter | $0.90 |
| Scale | $0.80 |
| Business and above | $0.70 |

| Run | Listings | Cost (Free plan) |
|---|---|---|
| A keyword in one city (about 300 listings) | 300 | about $0.30 |
| A category's newest 1,000 listings | 1,000 | about $1.00 |
| A tracker of 3 searches every hour, about 10 changes a run | 240 a day | about $0.24 a day |
| 50 listings read from their own pages | 50 | about $0.05 |

Apify's free plan ($5 of monthly credit) covers about 5,000 listings. To cap spending, lower Max results or set a maximum cost per run in the run options; the Actor stops cleanly at the limit (and with Only changes, the rest comes with the next run).

### Good to know

- **Speed**: a search's whole list comes in one request, and its details 1,080 listings at a time: a 1,000-listing search reads in a few seconds. A listing page takes about 2 seconds.
- **Craigslist's own limit**: Craigslist lists at most 10,000 listings per search. Narrow a bigger one with a category, a filter or a part of the area.
- **Freshness**: Craigslist serves a search's results from its cache for up to 15 minutes, as it does on its own site: the same search asked again in that time gets the same answer. So a tracker finds nothing new more often than every 15 minutes; each search's `listedAt` in the run summary says when Craigslist made its list.
- **Refusals**: a run that meets a refusal asks again a few times, then fails without charging; listing pages refused once aren't asked again in that run.
- **What isn't collected**: phone numbers, emails, and private sellers' map addresses. Contact a seller through the listing's own page.

### FAQ

**Can I get Craigslist alerts for new listings?** Yes: switch on Only changes, paste a Slack, Discord or Teams webhook in Alert webhook URL, and schedule it (every 15 to 60 minutes is typical: Craigslist caches a search for up to 15 minutes). Each new listing and price drop is posted once.

**Can it track price drops on Craigslist?** Yes. Craigslist doesn't show an earlier price, so the tracker remembers each listing's price and reports every move, with `trackedPrices` (every price it saw).

**Does it count reposted ads as new?** No. An ad deleted and posted again (the same title at the same map point) comes once as `relisted`, with the old id and price, and Craigslist's own duplicate copies are shown once.

**How do I know a listing sold?** Craigslist doesn't say "sold"; the ad is deleted or expires. With Only changes, a listing that leaves the search is checked on its own page and reported `removed` once Craigslist says it's not found.

**Can I search a whole state or several cities?** Yes: put `CA`, `Texas` or several areas in Locations. Each area is searched on its own (up to 100 searches a run).

**Does it work for cars and rentals?** Yes: `cars` (or `cars & trucks - by owner`) with Craigslist's make, model, year and odometer filters, and `apartments` or `rooms` with the bedrooms filter. Listing pages add a car's condition, title status and transmission, and a rental's bathrooms and features.

**Can I paste a Craigslist search link?** Yes, old-style (`sfbay.craigslist.org/search/cta?...`) or new (`craigslist.org/search/area/sfbay?cat=cta&...`): its area, category, keyword and filters are kept.

**Does it get the seller's phone number or email?** No, this Actor never collects contact details. The listing's link leads to Craigslist's own reply button.

**Does it work outside the US?** Yes, in all of Craigslist's 707 areas, including Canada, the UK and Europe, with each area's currency.

**Do I need a proxy?** No: Craigslist answers Apify's servers directly. You can use your own proxies; Apify's residential proxies aren't available in this Actor, as they're billed per GB on top of the result price.

**Is it legal?** The Actor reads public listings through the same search Craigslist's own pages use, at a modest pace, without logging in, and never collects contact details. Craigslist's terms restrict automated access and commercial use of its content: use the data in line with them and the laws that apply to you.

### Other Actors by this developer

- [Kijiji Scraper & Tracker](https://apify.com/akhil_rajesh/kijiji-scraper): Kijiji listings by keyword, city and category with prices and price drops, cars and rentals, plus Slack, Discord or Teams alerts
- [Zillow Price Tracker & Scraper](https://apify.com/akhil_rajesh/zillow-price-tracker): US homes for sale, sold and for rent, with price cuts, agents and change tracking
- [Amazon.ca Product & Trending Tracker](https://apify.com/akhil_rajesh/amazon-ca-product-trending-tracker): Amazon Best Sellers, prices, search positions and reviews on 23 marketplaces, with change tracking
- [AliExpress Price Tracker & Scraper](https://apify.com/akhil_rajesh/aliexpress-price-tracker): AliExpress search results with store prices, units sold and price history

### Disclaimer

This Actor is an independent tool. It is not affiliated with, endorsed by or sponsored by craigslist, Inc. It reads publicly available listings at a modest pace and never collects contact details. Use the data in line with Craigslist's terms and the laws that apply to you.

# Actor input Schema

## `searches` (type: `array`):

Keywords to search ('iphone 15', 'honda civic', '2 bedroom'), or Craigslist search links (craigslist.org/search/area/sfbay?cat=cta\&query=civic, or sfbay.craigslist.org/search/cta). A link keeps its own area, category and filters. Each keyword is searched in each location. Leave empty to read a whole category.

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

Craigslist areas to search: a city or region as Craigslist names it ('SF bay area', 'New York', 'Los Angeles', 'Chicago', 'Toronto'), a part of one ('Manhattan, NY', 'city of san francisco'), its host ('sfbay'), or a US state or Canadian province ('CA', 'Texas', 'Ontario') for every area in it. Each area is searched on its own, up to 100 searches a run.

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

Craigslist's category, in its words or plain ones: 'cars & trucks' ('cars'), 'cars & trucks - by owner', 'apartments', 'rooms', 'real estate', 'furniture', 'electronics', 'free stuff', 'jobs', 'gigs', 'services'. Leave empty for everything for sale.

## `listings` (type: `array`):

Specific listings to read from their own pages: Craigslist listing links (craigslist.org/view/d/<title>/<id>, or an older link such as sfbay.craigslist.org/sfc/cto/d/<title>/7954213800.html). Each gives the full text, both dates and the attributes. A listing that is gone is answered as not found (free).

## `maxResults` (type: `integer`):

Most listings (rows) a run returns, shared between searches. With Only changes: most changes a run returns (price drops first; the rest come with the next run).

## `onlyChanges` (type: `boolean`):

Track your searches: each run returns only what changed since the last run with the same searches and filters (new listings, price drops and increases, listings gone, ads posted again). The first run saves the listings there now as the baseline and returns them as firstRun. Each search is read whole, up to Max listings per search, so no change is missed.

## `changeTypes` (type: `array`):

Which changes Only changes returns. Leave empty for all.

## `alertWebhookUrl` (type: `string`):

Post each run's changes to Slack, Discord or Microsoft Teams (their incoming-webhook URL), or to any HTTPS endpoint that takes JSON (Zapier, Make, n8n). Switches on Only changes. The first run posts a short 'alerts are set up' message.

## `alertChangeTypes` (type: `array`):

Which changes are posted. Leave empty for new listings and price drops.

## `historyName` (type: `string`):

Keep a separate history for this tracker (a-z, 0-9 and hyphens). Leave empty to share the default one: histories are kept per search and filters anyway.

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

In the area's currency. Leave empty or 0 for no minimum. Craigslist applies it to the search.

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

Leave empty or 0 for no maximum. For rentals: monthly rent.

## `sellerType` (type: `array`):

Only owners' or only dealers' listings (Craigslist's 'sold by'). Leave empty for both.

## `withPhotosOnly` (type: `boolean`):

Craigslist's 'has image'.

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

Match the keywords in titles only (Craigslist otherwise matches the text too: 'iphone' finds phone mounts and cases that mention it).

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

Only listings within Distance of this ZIP or postal code ('94103', 'M5V').

## `distance` (type: `integer`):

Miles (km in Canada) from the ZIP or postal code. 10 when left empty.

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

Cars: make and model as Craigslist matches them ('honda civic', 'toyota').

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

Cars: this model year or newer. Leave empty or 0 for any.

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

Cars: this model year or older. Leave empty or 0 for any.

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

Cars: at most this odometer reading (miles; km in Canada). Leave empty or 0 for any.

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

Housing: this many bedrooms or more. Leave empty or 0 for any.

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

Housing: this many bedrooms or fewer. Leave empty or 0 for any.

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

Craigslist's order, which decides the listings kept when a search has more than Max results.

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

Only listings posted or renewed at most this many days ago. Leave empty or 0 for any.

## `excludeWords` (type: `array`):

Leave out listings whose title has any of these words ('wanted', 'parts', 'broken').

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

Craigslist groups copies of the same ad (posted again or in two categories). On: one listing per group, as Craigslist's 'hide duplicates' shows. Off: every copy, each marked with duplicateOf.

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

Craigslist tops up a small search with listings from nearby areas. Off: only the areas you named.

## `maxListingDetails` (type: `integer`):

Read each listing's own page for this many listings per run (at most 200, about 2 seconds each): the full text, first posted and updated dates, a car's year, make, model, condition and title status, a rental's bathrooms and features. With Only changes, the changes are read first, most important first. 0 for the fastest runs.

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

Each search is read up to this many listings, in the order above. Craigslist lists at most 10,000 per search: narrow a bigger one with a category, a filter or a part of the area.

## `proxy` (type: `object`):

Not needed: Craigslist answers Apify's servers directly. You can use your own proxies; residential proxies aren't available in this Actor.

## Actor input object example

```json
{
  "searches": [
    "iphone"
  ],
  "locations": [
    "SF bay area"
  ],
  "maxResults": 100,
  "sort": "newest",
  "hideDuplicates": true,
  "maxListingDetails": 0,
  "maxListingsPerSearch": 1000
}
```

# Actor output Schema

## `listings` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `cars` (type: `string`):

No description

## `housing` (type: `string`):

No description

## `details` (type: `string`):

No description

## `notFound` (type: `string`):

No description

## `allData` (type: `string`):

No description

## `runSummary` (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 = {
    "searches": [
        "iphone"
    ],
    "locations": [
        "SF bay area"
    ],
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("akhil_rajesh/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 = {
    "searches": ["iphone"],
    "locations": ["SF bay area"],
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("akhil_rajesh/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 '{
  "searches": [
    "iphone"
  ],
  "locations": [
    "SF bay area"
  ],
  "maxResults": 100
}' |
apify call akhil_rajesh/craigslist-scraper --silent --output-dataset

```

## MCP server setup

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