# Craigslist Listings Scraper — For Sale, Housing, Jobs, Services (`scrupulous_waterbird_m4w/craigslist-listings`) Actor

Crawl Craigslist listings by city + category + optional free-text query. Returns title, price, location, post URL, ISO 8601 posted-at, first image, and (top results per page) a 500-char post body excerpt. Public JSON-LD + static HTML; no auth, no proxy, no captcha. ~700 cities, 30+ categories.

- **URL**: https://apify.com/scrupulous\_waterbird\_m4w/craigslist-listings.md
- **Developed by:** [Mori](https://apify.com/scrupulous_waterbird_m4w) (community)
- **Categories:** E-commerce, Lead generation, Real estate
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Craigslist Listings Scraper (Apify Actor)

Crawl Craigslist listings by city + category + optional free-text query. Returns one dataset record per result with title, price, location, post URL, ISO 8601 posted-at timestamp, first image URL, and (for the top results per page) a 500-char excerpt of the post body. No auth, no proxy, no captcha. Reads public static HTML + JSON-LD.

### Source coverage

| Source | Auth | Captcha | Cities | Categories | Use |
|---|---|---|---|---|---|
| `https://{city}.craigslist.org/search/{category}` | none | none | ~700 city subdomains (newyork, losangeles, sfbay, chicago, boston, seattle, etc.) | sss (for sale), cta (cars+trucks), roo (rooms/shared), apa (apartments), etc. | browse + search results |
| `https://{city}.craigslist.org/view/d/{slug}/{id}` | none | none | same | per listing | post body + timestamp |

All endpoints return HTML + JSON-LD, no proxy / captcha / JS rendering needed, no auth, no API key. Craigslist serves standard HTML to anything that looks like Chrome; the actor sets a Chrome `User-Agent` automatically.

### What it does

- Crawl by `city` + `category` + optional `searchQuery`. Pagination is `?s=<offset>` where `offset = 120 * pageIndex`.
- For each search-result page, the actor:
  1. Pulls `<script id="ld_searchpage_results">` JSON (rich structured data: title, price, neighborhood, geo, image).
  2. Pulls all `/view/d/.../...` hrefs in result order from the DOM.
  3. Pairs them by index (`itemListElement[i] ↔ hrefs[i]`). Items without a matching href are skipped (CL occasionally trims; the actor never hallucinates URLs).
  4. Pushes one Dataset record per item.
  5. If `fetchBodies=true`, follows the first 5 post URLs per page to grab the post body + timestamp.
- Detail fetcher pulls `#postingbody` text (strips the "QR Code Link to This Post" boilerplate), trims to 500 chars, and pulls `<time.datetime>` as the ISO posted-at timestamp.
- Pairs JSON-LD with DOM hrefs by position (not by slug match) for resilience to markup changes — see the "Why JSON-LD + DOM-order pairing" section in `src/main.js`.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `city` | textfield | `"newyork"` | Craigslist city subdomain, lowercase, no spaces — e.g. `newyork`, `losangeles`, `sfbay`, `chicago`, `boston`, `seattle`. See [craigslist.org/about/sites](https://www.craigslist.org/about/sites) for the full ~700-city list. |
| `category` | textfield | `"sss"` | 3-letter category code. `sss` = all for sale (default), `cta` = cars+trucks, `roo` = rooms/shared, `apa` = apartments, `sub` = sublets. For sub-categories like `cta?auto_make_model=...`, pass the full query string — the actor URL-encodes it. Empty = browse all categories. |
| `searchQuery` | textfield | `""` | Free-text query appended to the search URL. Empty = browse the category. |
| `maxPages` | number (1-25) | 3 | Number of search-result pages to crawl. Each page has ~120 results. |
| `fetchBodies` | checkbox | `true` | If true, fetches the first 5 post bodies per page to capture the post body excerpt and posted-at timestamp. Adds 1 detail request per item (so 5 extra requests per page). |

### Output

One dataset record per listing. See `.actor/dataset_schema.json` for the full shape. Sample (first record from a `city=sfbay, category=cta, searchQuery=bicycle, maxPages=2, fetchBodies=true` smoke run, abridged):

```json
{
  "city": "sfbay",
  "category": "cta",
  "searchQuery": "bicycle",
  "title": "Specialized road bike — size 56cm",
  "price": "$450",
  "priceValue": 450,
  "location": "San Francisco",
  "url": "https://sfbay.craigslist.org/pen/d/specialized-road-bike-size-56cm/1234567890.html",
  "postedAt": "2026-07-08T14:23:18-0700",
  "bodyExcerpt": "Selling my Specialized Allez road bike, size 56cm. Lightly used, new tires in May. Includes pedals and a bike computer. Cash only, no trades. Pickup in the Mission.",
  "image": "https://images.craigslist.org/...600x450.jpg",
  "page": 1,
  "scrapedAt": "2026-07-08T..."
}
````

### Examples

**All for-sale items in Brooklyn under furniture:**

```json
{
  "city": "newyork",
  "category": "sss",
  "searchQuery": "furniture",
  "maxPages": 5,
  "fetchBodies": true
}
```

**Cars+trucks in Chicago, no body fetches (faster):**

```json
{
  "city": "chicago",
  "category": "cta",
  "searchQuery": "",
  "maxPages": 3,
  "fetchBodies": false
}
```

**Browse all sublets in Seattle (no query):**

```json
{
  "city": "seattle",
  "category": "sub",
  "searchQuery": "",
  "maxPages": 2,
  "fetchBodies": true
}
```

### Limits / gotchas

- 60 requests/min per IP (Craigslist's published limit; the actor respects this by default).
- `maxPages` hard-capped at 25 (input schema enforces 1-25). At 120 results per page that's 3000 listings per run.
- `bodyExcerpt` is `null` when `fetchBodies=false`, or for items past the first 5 per page even when `fetchBodies=true` (capped to keep request count bounded).
- `priceValue` is `null` when Craigslist shows no price (e.g. "Please contact" listings). `price` retains the raw display string.
- About 6% of result pages have "similar listings" sidebar links at the end of the href list. These don't have a JSON-LD partner and are skipped — they never make it into the dataset.

### License

Actor code: MIT (inherited from project template).

Craigslist data: per [Craigslist's terms of use](https://www.craigslist.org/about/terms.of.use), personal/non-commercial use is permitted. Commercial use cases (reselling listings, large-scale scraping for commercial datasets) should review CL's ToS and the specific city's robots.txt.

### Changelog

- 0.1.0 — initial release. City + category + search query, JSON-LD + DOM-order pairing, optional body fetches, hard cap 25 pages.

# Actor input Schema

## `city` (type: `string`):

Craigslist city subdomain, lowercase and unspaced — e.g. 'newyork', 'losangeles', 'sfbay', 'chicago', 'boston', 'seattle'. See https://www.craigslist.org/about/sites for the full list of ~700 cities.

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

Craigslist 3-letter category code. Common ones: 'sss' = all for sale (default), 'cta' = cars+trucks, 'roo' = rooms/shared, 'hhh' is NOT a valid code (housing uses 'apa'='apartments', 'roo', 'sub'). For sub-categories like cta?category=121, pass the full query string 'cta?category=121' — we'll URL-encode it. Empty = browse all categories on the search page.

## `searchQuery` (type: `string`):

Optional free-text search appended as ?query=<value>. URL-encoded automatically. Empty = browse all listings in the chosen category. Examples: 'bicycle', 'macbook pro', 'room for rent'.

## `maxPages` (type: `integer`):

How many search-result pages to crawl. Each page has ~120 results; total cap = maxPages \* 120 listings. Default 3 = up to 360 listings.

## `fetchBodies` (type: `boolean`):

If true, follow the first 5 post URLs per page to grab the first 500 chars of each post body as `bodyExcerpt`. Adds up to 5 \* maxPages extra HTTP requests per run. Disable for pure listing-card enumeration.

## Actor input object example

```json
{
  "city": "newyork",
  "category": "sss",
  "searchQuery": "bicycle",
  "maxPages": 3,
  "fetchBodies": true
}
```

# 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 = {
    "city": "newyork",
    "category": "sss",
    "searchQuery": "bicycle"
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrupulous_waterbird_m4w/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 = {
    "city": "newyork",
    "category": "sss",
    "searchQuery": "bicycle",
}

# Run the Actor and wait for it to finish
run = client.actor("scrupulous_waterbird_m4w/craigslist-listings").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "city": "newyork",
  "category": "sss",
  "searchQuery": "bicycle"
}' |
apify call scrupulous_waterbird_m4w/craigslist-listings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=scrupulous_waterbird_m4w/craigslist-listings",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Craigslist Listings Scraper — For Sale, Housing, Jobs, Services",
        "description": "Crawl Craigslist listings by city + category + optional free-text query. Returns title, price, location, post URL, ISO 8601 posted-at, first image, and (top results per page) a 500-char post body excerpt. Public JSON-LD + static HTML; no auth, no proxy, no captcha. ~700 cities, 30+ categories.",
        "version": "0.0",
        "x-build-id": "pYI03UgUnoSCp8MPv"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/scrupulous_waterbird_m4w~craigslist-listings/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-scrupulous_waterbird_m4w-craigslist-listings",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/scrupulous_waterbird_m4w~craigslist-listings/runs": {
            "post": {
                "operationId": "runs-sync-scrupulous_waterbird_m4w-craigslist-listings",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/scrupulous_waterbird_m4w~craigslist-listings/run-sync": {
            "post": {
                "operationId": "run-sync-scrupulous_waterbird_m4w-craigslist-listings",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "city",
                    "category"
                ],
                "properties": {
                    "city": {
                        "title": "City subdomain",
                        "type": "string",
                        "description": "Craigslist city subdomain, lowercase and unspaced — e.g. 'newyork', 'losangeles', 'sfbay', 'chicago', 'boston', 'seattle'. See https://www.craigslist.org/about/sites for the full list of ~700 cities."
                    },
                    "category": {
                        "title": "Category code",
                        "type": "string",
                        "description": "Craigslist 3-letter category code. Common ones: 'sss' = all for sale (default), 'cta' = cars+trucks, 'roo' = rooms/shared, 'hhh' is NOT a valid code (housing uses 'apa'='apartments', 'roo', 'sub'). For sub-categories like cta?category=121, pass the full query string 'cta?category=121' — we'll URL-encode it. Empty = browse all categories on the search page."
                    },
                    "searchQuery": {
                        "title": "Search query",
                        "type": "string",
                        "description": "Optional free-text search appended as ?query=<value>. URL-encoded automatically. Empty = browse all listings in the chosen category. Examples: 'bicycle', 'macbook pro', 'room for rent'."
                    },
                    "maxPages": {
                        "title": "Number of result pages",
                        "minimum": 1,
                        "maximum": 25,
                        "type": "integer",
                        "description": "How many search-result pages to crawl. Each page has ~120 results; total cap = maxPages * 120 listings. Default 3 = up to 360 listings.",
                        "default": 3
                    },
                    "fetchBodies": {
                        "title": "Fetch post bodies",
                        "type": "boolean",
                        "description": "If true, follow the first 5 post URLs per page to grab the first 500 chars of each post body as `bodyExcerpt`. Adds up to 5 * maxPages extra HTTP requests per run. Disable for pure listing-card enumeration.",
                        "default": true
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
