# Craigslist Scraper — Listings, Cars, Housing & Contacts (`brilliant_gum/craigslist-scraper`) Actor

Scrape any Craigslist city and category: for-sale, cars, housing, jobs, services. Price, date, GPS, all photos, full specs (VIN/odometer, beds/baths/sqft), description, body email + anonymized relay email/phone (no login). Plus dedup, monitoring & region price analytics.

- **URL**: https://apify.com/brilliant\_gum/craigslist-scraper.md
- **Developed by:** [Yuliia Kulakova](https://apify.com/brilliant_gum) (community)
- **Categories:** Lead generation, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / listing

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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, Cars, Housing, Jobs & Contacts

![Craigslist Scraper](https://api.apify.com/v2/key-value-stores/IuFu0Cw6PpOkNkDV7/records/craigslist-scraper-banner.png)

Search and scrape **any Craigslist city and category** — for-sale items, cars & trucks,
apartments/housing, jobs, gigs, services and more. Returns clean, structured data with
**price, exact dates, GPS coordinates, every photo, full specs, description and seller info**,
plus built-in **de-duplication, monitoring mode and region price analytics**.

HTTP-only (fast, no browser), **no login and no API key required**.

### Why this scraper

- **Two ways to search**: by city + keyword + category with rich filters, **or** paste ready-made
  Craigslist search / post URLs. Mix both in one run.
- **Real coordinates** (latitude/longitude + geo accuracy) on every listing.
- **Full vehicle specs**: year, make, model, **VIN**, odometer, fuel, transmission, title status,
  drivetrain, cylinders, paint, body type, condition.
- **Full housing specs**: bedrooms, bathrooms, sqft, housing type, laundry, parking, rent period,
  availability, cats/dogs OK, furnished.
- **All photos** at medium and full resolution.
- **Contact data**: e-mail addresses written in the post body (including obfuscated
  "name at domain dot com" forms), plus — optionally — Craigslist's **anonymized relay
  email** (`xxxx@sale.craigslist.org`) and **reveal phone**, unlocked with **no login**.
- **Dealer / bulk-poster detection**, **repost de-duplication**, **monitoring mode** (only new
  listings across scheduled runs — set a monitoring store name so it remembers between runs),
  and **per-region price analytics** (min/median/mean/max).

### Example input

```json
{
  "subdomain": "newyork",
  "category": "cta",
  "searchQueries": ["honda"],
  "maxPrice": 20000,
  "sort": "date",
  "includeDetails": true,
  "dealerDetection": true,
  "maxItems": 100
}
```

Or by URL:

```json
{
  "startUrls": [
    { "url": "https://sfbay.craigslist.org/search/apa?query=loft&max_price=4000" },
    { "url": "https://www.craigslist.org/view/d/some-listing/abcd1234" }
  ]
}
```

### Common category codes

`sss` all for-sale · `cta` cars & trucks · `cto` by owner · `ctd` by dealer · `mcy` motorcycles ·
`apa` apartments/housing · `hhh` all housing · `rea` real estate for sale · `roo` rooms ·
`jjj` jobs · `ggg` gigs · `bbb` services · `ele` electronics · `zip` free stuff.

Find your **city subdomain** in any Craigslist URL — the part before `.craigslist.org`
(e.g. `newyork`, `losangeles`, `sfbay`, `chicago`, `miami`, `seattle`, `london`).

### Output (per listing)

`postId, url, canonicalUrl, title, price, priceText, categoryName, subdomain, neighborhood,
mapAddress, address{street,city,region,postalCode,country}, latitude, longitude, geoAccuracy,
postedAt, updatedAt, images[], imagesFull[], description, condition, attributes{}, auto{…}, housing{…},
jobs{…}, isDealer, sellerSignals[], email, emails[], phoneNumbers[], replyEmail, replyPhone,
priceVsRegionMedianPct`.

`canonicalUrl` is always the working `https://www.craigslist.org/view/d/<slug>/<hash>` form
(Craigslist's own canonical); pasted subdomain `.html` post URLs with a stale sub-area segment
are resolved automatically. `auto{}` is only emitted for actual vehicles; a generic item
`condition` (e.g. "like new") is returned as a top-level `condition` field instead.

With `computeRegionAnalytics` a summary row per region is appended
(`region, listingCount, minPrice, medianPrice, meanPrice, maxPrice`).

### Contact extraction

- `email` / `emails[]` — real e-mail addresses found in the post body, including obfuscated
  forms ("john at gmail dot com", "john \[at] domain"). Always extracted when details are on.
- `phoneNumbers[]` — phone numbers written in the body (enable **Extract contact info**).
- `replyEmail` / `replyPhone` — Craigslist's anonymized relay email
  (`xxxx@sale.craigslist.org`) and reveal phone, revealed **without any login** (enable
  **Unlock anonymized relay email & phone**). This is best-effort: some listings can't be
  unlocked on a given run. Raise **Contact-unlock retries** and lower **Max concurrency**
  for a higher hit-rate.

### Proxy

Proxies are configured **automatically** for reliable runs — you don't need to set anything up.
Just make sure your Apify plan includes proxy access.

### Pricing

Pay-per-event — you only pay for what you get:

| Event | Price |
|---|---|
| Actor start | $0.01 per run |
| Listing | $0.01 per listing returned (with details, specs, all photos, GPS & body contacts) |
| Relay contact unlocked | $0.10 — charged **only** when the anonymized relay email and/or reveal phone is actually unlocked |

You are **never** charged for a relay contact that couldn't be unlocked, and per-region analytics
summary rows are free. If you set a **maximum cost per run**, the actor stops when it is reached and
delivers only the listings that fit within it. Apify platform usage (compute & proxies) is billed to your own Apify
account on top of these events. Turn off **Open each listing for full details** for a faster run
that uses less compute and proxy (search-card fields only).

### Support & feedback

Found a bug, or want another field, category or city supported? **Open an issue** on the actor's
Issues tab, or leave a **rating and review** on the actor page — it genuinely helps and I read
every one.

### Notes & limitations

- Craigslist returns up to ~1080 results per search; use multiple queries, `deepCoverage` or
  `searchNearby` for wider coverage.
- Very large runs stop cleanly shortly before the run timeout and keep everything fetched so far —
  the run's status message says how many listings were delivered. Raise the timeout in Run options
  or lower **Max results** to get them all.
- Vehicle **VIN** and some specs are only returned when the seller includes them in the listing
  (dealers usually do; many private sellers don't).
- The anonymized relay email/phone unlock is best-effort: not every listing yields a relay
  email on a given run. Body emails and phone numbers are captured reliably whenever present.
- Scrapes only publicly available data. Use responsibly and within Craigslist's Terms.

# Actor input Schema

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

Optional. Paste Craigslist URLs, one per line. Both search/category URLs (e.g. https://newyork.craigslist.org/search/sss?query=iphone) and individual post URLs (e.g. https://newyork.craigslist.org/mob/d/.../7961578044.html or a /view/d/... link) are supported. Search URLs are expanded and paginated automatically. Leave empty and use the 'Search by city' section instead if you don't already have URLs.

## `subdomain` (type: `string`):

The Craigslist city/region code — the part before .craigslist.org in the URL. Examples: newyork, losangeles, sfbay, chicago, miami, seattle, london. Required when using 'Search queries' / 'Category'. The full list of valid subdomains is resolved automatically from Craigslist's own area reference.

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

Optional keywords/phrases, one per line. Each becomes a separate Craigslist search in the chosen city + category. Leave empty to scrape the whole category without a keyword.

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

Craigslist category code to search within. Common codes: sss = all for-sale, cta = cars & trucks, apa = apartments/housing for rent, rea = real estate for sale, roo = rooms/shared, jjj = all jobs, ggg = all gigs, bbb = all services, ccc = community, res = resumes, ele = electronics, mcy = motorcycles, hab = arts+crafts. Any valid Craigslist category abbreviation is accepted. Default: sss.

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

How to order search results. 'rel' = most relevant (default), 'date' = newest first, 'priceasc' = cheapest first, 'pricedsc' = most expensive first. Only applies to searches (not to individual post URLs).

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

Only return listings priced at or above this amount. Leave empty for no lower bound.

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

Only return listings priced at or below this amount. Leave empty for no upper bound.

## `onlyFreeListings` (type: `boolean`):

Only return listings priced at $0 (equivalent to Craigslist's free category / max price 0).

## `onlyWithPhotos` (type: `boolean`):

Only return listings that include at least one photo.

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

Only return listings posted in the last 24 hours.

## `maxAgeDays` (type: `integer`):

Skip listings older than this many days since they were posted. Leave empty to keep all ages. (Applied client-side after fetching.)

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

Match the query against listing titles only, not the full body text (Craigslist 'srchType=T'). Produces fewer, more precise results.

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

Filter by who is posting. 'all' = no filter, 'owner' = private/for-sale-by-owner only, 'dealer' = dealers/businesses only.

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

Ask Craigslist to bundle near-duplicate reposts of the same item into one result (Craigslist 'bundleDuplicates').

## `searchNearby` (type: `boolean`):

Also include results from neighbouring Craigslist regions around the chosen city.

## `condition` (type: `string`):

For-sale only: filter by the seller-declared item condition. 'any' = no filter. Maps to Craigslist's condition filter (new / like new / excellent / good / fair / salvage).

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

For-sale only: only return listings where the seller offers delivery (Craigslist 'delivery\_available').

## `deepCoverage` (type: `boolean`):

Craigslist's search API returns at most ~1080 results per search. When ON, the actor automatically splits each priced search into disjoint price bands and crawls each one, so you can pull many thousands of listings for a busy city/category. Ignored for price-less categories (jobs, gigs, community). Combine with 'Max results' to bound the run.

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

Center the search on a specific ZIP/postal code. Use together with 'Search distance'.

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

Radius in miles around the ZIP/postal code. Requires 'ZIP / postal code'.

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

Housing only: minimum number of bedrooms.

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

Housing only: maximum number of bedrooms.

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

Housing only: minimum number of bathrooms.

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

Housing only: maximum number of bathrooms.

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

Housing only: minimum area in square feet.

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

Housing only: maximum area in square feet.

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

Housing only: only listings that allow cats.

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

Housing only: only listings that allow dogs.

## `minAutoYear` (type: `integer`):

Cars/vehicles only: minimum model year.

## `maxAutoYear` (type: `integer`):

Cars/vehicles only: maximum model year.

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

Cars/vehicles only: maximum odometer reading in miles.

## `autoTitleStatus` (type: `string`):

Cars/vehicles only: filter by title status.

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

When ON (default), the actor opens every listing's detail page to extract the full description, all photos, complete specs/attributes (VIN, odometer, fuel, transmission, title status for cars; beds/baths/sqft/housing type/laundry/parking for housing), exact posted & updated timestamps, map address and contact info. Turn OFF for a much faster, cheaper run that returns only the search-result summary (title, price, coordinates, thumbnail, url).

## `extractContactInfo` (type: `boolean`):

When ON, additionally extract phone numbers found in the listing body and the reply/contact URL. (E-mail addresses written in the body — including obfuscated 'name at domain dot com' forms — are always extracted into the 'email' field regardless of this toggle.) Requires 'Open each listing for full details'.

## `fetchContactInfo` (type: `boolean`):

When ON, the actor reveals each listing's anonymized relay email (xxxx@sale.craigslist.org) into 'replyEmail', plus the reveal phone into 'replyPhone' when the poster provided one — no login required. Best-effort and slower/heavier: some listings can't be unlocked on a given run, so the actor retries (see 'Contact-unlock retries'). Requires 'Open each listing for full details'.

## `contactInfoMaxRetries` (type: `integer`):

How many attempts to make per listing when unlocking the relay email/phone (only used when 'Unlock anonymized relay email & phone' is ON). Higher values raise the success rate but cost more requests. Default 5.

## `maxImages` (type: `integer`):

Cap the number of image URLs stored per listing. 0 = no images, empty = all images.

## `monitoringMode` (type: `boolean`):

When ON, the actor remembers every post ID it has already delivered and, on subsequent runs, outputs ONLY listings it has never seen before. Ideal for scheduled runs that watch a search for fresh posts. Set a Monitoring store name so the memory persists across runs.

## `monitoringStoreName` (type: `string`):

Named key-value store where Monitoring mode remembers already-delivered listings across runs. Use the SAME name on every scheduled run that watches one search; use different names for different searches. Required for cross-run 'new listings only' monitoring.

## `deduplicateReposts` (type: `boolean`):

Detect the same item re-listed under multiple post IDs (same title + price + seller area) within this run. The first occurrence is kept and flagged with repostCount; later duplicates are dropped from the dataset.

## `dealerDetection` (type: `boolean`):

Add an 'isDealer' boolean and 'sellerSignals' to each record using heuristics (Craigslist by-dealer category, dealer keywords, phone/website in body, many active posts). Helps separate private sellers from dealers and bulk posters.

## `computeRegionAnalytics` (type: `boolean`):

After scraping, push one extra summary record per neighbourhood/sub-region with count, min/median/mean/max price, so you can compare pricing across areas. Each listing also gets 'priceVsRegionMedianPct'.

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

Hard cap on the number of listings to output across the whole run. Pre-filled with 50 for a quick, low-cost first run — raise it, or clear the field for no cap (bounded by Craigslist's per-search limits). Very large runs stop cleanly before the run timeout and keep everything fetched so far.

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

Maximum number of pages fetched in parallel. Lower this if you hit rate-limiting/blocks.

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

How many times to retry a failed or blocked request before giving up on it.

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

Proxy settings. Leave as-is — proxies are tuned automatically for reliable Craigslist runs. Requires an Apify account with a proxy plan.

## Actor input object example

```json
{
  "startUrls": [],
  "subdomain": "newyork",
  "searchQueries": [
    "iphone"
  ],
  "category": "sss",
  "sort": "date",
  "onlyFreeListings": false,
  "onlyWithPhotos": false,
  "postedToday": false,
  "searchTitlesOnly": false,
  "sellerType": "all",
  "hideDuplicates": true,
  "searchNearby": false,
  "condition": "any",
  "deliveryAvailable": false,
  "deepCoverage": false,
  "catsOk": false,
  "dogsOk": false,
  "autoTitleStatus": "any",
  "includeDetails": true,
  "extractContactInfo": false,
  "fetchContactInfo": false,
  "contactInfoMaxRetries": 5,
  "monitoringMode": false,
  "deduplicateReposts": false,
  "dealerDetection": true,
  "computeRegionAnalytics": false,
  "maxItems": 50,
  "maxConcurrency": 8,
  "maxRequestRetries": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `contacts` (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 = {
    "startUrls": [],
    "subdomain": "newyork",
    "searchQueries": [
        "iphone"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("brilliant_gum/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 = {
    "startUrls": [],
    "subdomain": "newyork",
    "searchQueries": ["iphone"],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("brilliant_gum/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 '{
  "startUrls": [],
  "subdomain": "newyork",
  "searchQueries": [
    "iphone"
  ],
  "maxItems": 50
}' |
apify call brilliant_gum/craigslist-scraper --silent --output-dataset

```

## MCP server setup

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