# Craigslist Scraper - Listings, Prices & Photos, No Login (`cliqtomedia/craigslist-scraper`) Actor

Scrape Craigslist listings from any of 700+ cities by category, keyword and price: title, price, location, coordinates, photos and post date, plus full description and attributes on request. Only-new mode for monitoring. No login, proxy or seller contacts.

- **URL**: https://apify.com/cliqtomedia/craigslist-scraper.md
- **Developed by:** [Cliqto Media](https://apify.com/cliqtomedia) (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.45 / 1,000 listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 searches Craigslist by city, category and keyword and returns one clean row per listing: title, price, location, posting time and photo links. Turn on **Include full listing details** to also get the description, attributes and ZIP code.

Use it to collect or monitor Craigslist listings without a login, cookies, an API key or a proxy. The smallest run is one city and one keyword, and it finishes in a few seconds.

![Craigslist Listings Scraper: price, location, photos and posting time from Craigslist search, with an Only new mode for monitoring](https://api.apify.com/v2/key-value-stores/Lb5pbUlGyQtKAKBxJ/records/hero.png)

### Table of contents

- [What it does](#what-it-does)
- [Quick start](#quick-start)
- [Use cases](#use-cases)
- [Input](#input)
- [Output](#output)
- [Pricing and limits](#pricing-and-limits)
- [API, export and integrations](#api-export-and-integrations)
- [Troubleshooting](#troubleshooting)
- [FAQ](#faq)
- [Support](#support)

### What it does

The Actor runs the same search you would run on a Craigslist site, for example `sfbay.craigslist.org`, and saves the results newest first. Each row is one listing. With details on, the Actor also opens each listing page.

#### Good fit

- Price research: collect asking prices for bikes, cars, furniture or rentals in one or more cities.
- Monitoring: run on a schedule with **Only new listings since last run** and get only listings posted after the previous run.
- Housing and car data: bedrooms, square feet and odometer from search, plus attributes like `housing_type`, `laundry`, `make_manufacturer` or `odometer` with details.

#### Not a fit / limits

- No seller contacts. The Actor does not collect phone numbers or e-mails, and masks the ones it recognises in titles and descriptions (best effort, not a guarantee).
- Craigslist shows at most 10,000 listings per search. To get more, split the search by city, category or price.
- Up to 50 cities per run. Listings that Craigslist adds from nearby sites are left out; add those sites as their own cities.
- `query` is a full-text search, like the Craigslist search box: `bike` also finds listings that only mention a bike in the text.

### Quick start

1. Open the **Input** tab.
2. Keep the prefilled example: **Cities** `sfbay`, **Category** `For sale — all`, **Search keywords** `bike`, **Max listings per city** `10`.
3. Click **Start**.
4. Open the **Output** tab. The **Listings** view shows title, price, location and posting time.

Expected result: 10 listings matching "bike" from the SF bay area, newest first, in a few seconds.

### Use cases

| Buyer job | Input | Output | Key fields | Next step |
| --- | --- | --- | --- | --- |
| Compare used prices | `cities`, `category`, `query` | listing row | `price`, `currency`, `title`, `location` | Export CSV or Excel |
| Watch new listings | `onlyNew` on a schedule | only new rows | `postId`, `postedAt`, `url` | Webhook or integration on run finish |
| Rental research | `category: "apa"`, `includeDetails` | detailed row | `price`, `bedrooms`, `sqft`, `attributes`, `postalCode` | Spreadsheet or BI tool |
| Car listings | `category: "cta"`, `sellerType` | listing row | `price`, `mileage`, `attributes.odometer` | Dataset API |

### Input

#### Input fields

| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect |
| --- | --- | --- | --- | --- | --- | --- |
| `cities` | Cities | array of strings | yes | prefill `["sfbay"]` | 1–50 Craigslist sites, e.g. `sfbay`, `newyork`, `london`, `toronto`; full addresses work too | Which sites to search |
| `category` | Category | string | no | default and prefill `sss` | 61 Craigslist sections, e.g. `sss` (for sale — all), `apa` (apartments), `cta` (cars & trucks), `bik` | Which section to search |
| `query` | Search keywords | string | no | prefill `bike` | up to 200 characters | Full-text search; empty lists the whole category |
| `maxItems` | Max listings per city | integer | no | default `100`, prefill `10` | 1–10,000 | Limit **for each city**, newest first |
| `includeDetails` | Include full listing details | boolean | no | `false` | | Opens each listing page: description, attributes, all photos, ZIP code. Slower and charged as a detailed listing |
| `onlyNew` | Only new listings since last run | boolean | no | `false` | | Saves only listings posted or renewed after the previous run of the same search |
| `minPrice`, `maxPrice` | Min / max price | integer | no | none | 0 and up, local currency | Price range |
| `sellerType` | Seller type | string | no | `all` | `all`, `owner`, `dealer` | For-sale categories only |
| `hasImages` | Only listings with photos | boolean | no | `false` | | Skips listings without photos |
| `postedToday` | Only posted today | boolean | no | `false` | | Only listings posted today |

The same city entered twice (for example `sfbay` and `SFBay`) is searched once. An unknown city or category stops the run before any request with a clear `INVALID_INPUT` message.

#### Interacting settings

| When | Settings | Behavior |
| --- | --- | --- |
| Several cities | `cities` + `maxItems` | `maxItems` applies to each city: 3 cities × 100 = up to 300 rows |
| Monitoring | `onlyNew` + any filters | Each city, category, keyword and filter set is remembered on its own. The first run saves the newest listings up to `maxItems` and sets the starting point |
| More new listings than the limit | `onlyNew` + `maxItems` | The newest `maxItems` are saved; older new ones are skipped. Use a limit above what you expect between runs |
| Stopped run | `onlyNew` + charge or time limit | The starting point stays, so the next run returns the new listings this run did not reach |

#### Input examples

##### Bikes in the SF bay area (prefill)

```json
{
  "cities": ["sfbay"],
  "category": "sss",
  "query": "bike",
  "maxItems": 10
}
```

Expected behavior: one search, 10 newest listings that match `bike`.
Expected output: rows with `title`, `price`, `location`, `postedAt`, `images`.

##### New apartments with details, for a schedule

```json
{
  "cities": ["sfbay", "losangeles"],
  "category": "apa",
  "maxItems": 200,
  "includeDetails": true,
  "onlyNew": true
}
```

Expected behavior: the first run saves up to 200 newest rentals per city. Later runs save only rentals posted after the previous run.
Limit note: details take about 0.5 s per listing.

### Output

#### Output fields

| Field | Meaning | Type | Can be empty? | Example |
| --- | --- | --- | --- | --- |
| `postId` | Craigslist post id | string | no | `7954661391` |
| `url` | Listing page | string | rarely | `https://www.craigslist.org/view/d/...` |
| `title` | Listing title, contacts masked | string | rarely | `B-TWIN, 6 SPEED CRUISER BIKE` |
| `price`, `priceText`, `currency` | Asking price, as shown, ISO currency | number, string, string | yes, when there is no price (never `0` for a missing price) | `20`, `$20`, `USD` |
| `city`, `cityName`, `subarea` | Craigslist site and sub-area | string | `subarea` yes | `sfbay`, `SF bay area`, `pen` |
| `location`, `neighborhood` | Place the poster typed, neighborhood | string | yes | `millbrae` |
| `latitude`, `longitude` | Approximate position | number | yes | `37.6004`, `-122.402` |
| `category`, `categoryName` | Listing category | string | rarely | `bik`, `bicycles - by owner` |
| `sellerType` | `owner` or `dealer` | string | yes | `owner` |
| `postedAt` | Posted or last renewed, UTC | string | rarely | `2026-10-15T05:43:10.000Z` |
| `images`, `imageCount` | Photo links (600×450 JPEG) and their count | array, integer | empty array when no photos | |
| `bedrooms`, `sqft`, `mileage` | Housing and vehicle numbers from search | integer | yes; `bedrooms: 0` is a studio | `1`, `null`, `null` |
| `description` | Full text, contacts masked | string | details only | |
| `attributes` | Listing attributes, e.g. `condition`, `odometer`, `housing_type`, `laundry` | object | details only | `{"laundry": "w/d in unit"}` |
| `postalCode` | ZIP / postal code | string | details only | `94978` |
| `firstPostedAt`, `updatedAt` | First posting time and last edit, UTC | string | details only; `updatedAt` is `null` when never edited | |
| `detailsStatus` | `ok`, `gone` (removed before it was read), `failed` (page unreadable, search fields only), `not_requested` | string | no | `not_requested` |
| `searchCity`, `searchCategory`, `searchQuery` | Your input that found the row | string | `searchQuery` yes | `sfbay`, `sss`, `bike` |
| `source` | Always `craigslist.org` | string | no | |
| `fetchedAt` | When the Actor read the listing, UTC | string | no | |

`postedAt` changes when a poster renews a listing. `firstPostedAt` (details only) keeps the first posting time.

#### Output example

Trimmed row from the prefill run:

```json
{
  "postId": "7954661391",
  "url": "https://www.craigslist.org/view/d/millbrae-twin-speed-cruiser-bike-too/2WS4Z9LeXGU7iV32nC6RR5",
  "title": "B-TWIN ,  6 SPEED  CRUISER BIKE ,  TOO CHEAP !!!  .",
  "price": 20,
  "priceText": "$20",
  "currency": "USD",
  "city": "sfbay",
  "location": "millbrae",
  "latitude": 37.6004,
  "longitude": -122.402,
  "category": "bik",
  "categoryName": "bicycles - by owner",
  "sellerType": "owner",
  "postedAt": "2026-10-15T05:43:10.000Z",
  "images": ["https://images.craigslist.org/01616_c3M0s56xj4e_0CI0t2_600x450.jpg"],
  "imageCount": 4,
  "description": null,
  "attributes": null,
  "detailsStatus": "not_requested",
  "searchQuery": "bike",
  "fetchedAt": "2026-10-15T05:50:11.109Z"
}
```

`null` means Craigslist does not give that value for this listing, or details were off.

#### Output views and run summary

| Situation | Where to look | What you see |
| --- | --- | --- |
| Normal run | Dataset, views **Listings** and **With details** | One row per listing |
| No listings found | Dataset is empty; `RUN_SUMMARY` status `NO_RESULTS` | The run succeeds; check city, category and filters |
| Charge or time limit reached | `RUN_SUMMARY` status `PARTIAL`, reason `CHARGE_LIMIT` or `TIME_LIMIT` | Rows saved before the stop are kept |
| Craigslist error | `RUN_SUMMARY` status `PARTIAL` or `ERROR`, reason `SOURCE_ERROR` | The run fails only when nothing was saved |

`RUN_SUMMARY` in the run's key-value store also has per-city counts: listings available, saved, already seen, details read.

### Pricing and limits

#### Pricing unit

You pay per run start and per saved listing:

| Event | Price (free plan) |
| --- | ---: |
| Actor start | $0.004 per run |
| Listing (search fields) | $0.0006 ($0.60 per 1,000) |
| Listing with details | $0.0011 ($1.10 per 1,000) |

Higher Apify plans get lower per-listing prices. Platform usage (compute, storage, proxy) is included; you do not pay it separately.

- A run with no matching listings, or an **Only new** run with nothing new, costs only the start.
- Listings that were already delivered (Only new) or left out as nearby sites are not charged.
- A listing removed before its page could be read is charged as a plain listing, not as a detailed one.
- Set **Max total charge** in run options to cap a run. The Actor stops before the cap and keeps what it saved.

#### Cost examples

| Scenario | Input | Rows | Cost on the free plan |
| --- | --- | ---: | ---: |
| Quick test (prefill) | 1 city, `bike`, 10 listings | 10 | $0.010 |
| Small batch | 1 city, 100 listings | 100 | $0.064 |
| Market snapshot | 1 city, 1,000 listings | 1,000 | $0.604 |
| With details | 1 city, 100 listings, details on | 100 | $0.114 |
| Monitor, nothing new | 50 cities, Only new | 0 | $0.004 |

#### Limits

| Limit | Value |
| --- | --- |
| Cities per run | 50 |
| Listings per city | 10,000 (the Craigslist cap for one search) |
| Speed, search only | about 50,000 listings in 4 minutes (5 cities × 10,000, measured) |
| Speed, with details | about 2 listings per second; 1,163 detailed listings in 10 minutes (measured) |
| Memory | 256 MB (fixed; enough for 50 cities and for details) |

For a big run with details, set a run timeout of at least 1 second per listing.

### API, export and integrations

- **Export:** download the dataset as JSON, CSV, Excel, XML or HTML from the Output tab, or with the dataset items API.
- **API:** start the Actor with the [Apify API](https://docs.apify.com/api/v2) or the Apify client for JavaScript or Python; send the same JSON as in the Input examples.
- **Scheduling:** create a schedule for the Actor or for a saved task with `onlyNew: true`. Each run then returns only new listings.
- **Webhooks and no-code:** start a webhook, Zapier, Make or n8n flow when the run finishes, and read new rows from the dataset.

#### Use with AI agents (MCP)

- MCP server with only this Actor: `https://mcp.apify.com?tools=cliqtomedia/craigslist-scraper`
- Smallest useful input: `{"cities": ["sfbay"], "category": "sss", "query": "bike", "maxItems": 10}`
- Key output fields: `title` (what is offered), `price` + `currency` (asking price), `location` (place), `postedAt` (posted or renewed), `url` (listing page).
- Typical run: a few seconds for 10 listings, about $0.01 on the free plan. `maxItems` is per city; details are about 2 listings per second.

### Troubleshooting

#### The run finished with 0 rows

Symptom: the dataset is empty and `RUN_SUMMARY` status is `NO_RESULTS`.
Likely reason: no listing matches the keywords and filters on that site, or, with **Only new**, nothing new was posted since the last run.
Check: open the same search on the Craigslist site.
Action: remove a filter, try a broader category such as `sss`, or check the city code.

#### The run stopped with INVALID_INPUT

Symptom: the run fails at once with a message about a city or category.
Likely reason: the city is not a Craigslist site (for example a typo), or the category code is unknown.
Action: use the subdomain from the city address, for example `sfbay` from `sfbay.craigslist.org`.

#### Fewer rows than Max listings per city

Symptom: `RUN_SUMMARY` reason is `CHARGE_LIMIT` or `TIME_LIMIT`, or the city had fewer listings.
Check: `totalAvailable` per city in `RUN_SUMMARY`.
Action: raise **Max total charge** or the run timeout and run again.

#### Some rows have no description

Symptom: `detailsStatus` is `gone` or `failed`.
Likely reason: the poster removed the listing before it was read (`gone`), or the page could not be read (`failed`). Search fields are still there.

### FAQ

#### Do I need a Craigslist account, cookies or a proxy?

No. The Actor reads public Craigslist search and listing pages. No login, cookies or proxy are needed.

#### Does it collect seller phone numbers or e-mails?

No. It does not collect seller contacts, and it masks the phone numbers and e-mails it recognises in titles and descriptions as `[phone removed]` and `[email removed]`. Masking is best effort: an unusual format can slip through, and a long number can sometimes be masked by mistake.

#### How fresh is the data?

Each row is read during the run; `fetchedAt` shows when. Craigslist lists renewed listings again as new, so `postedAt` is the latest posting or renewal time.

#### Which cities and countries work?

Any Craigslist site: the US, Canada, the UK, Europe, Asia, Australia and more. Prices keep the local currency in `currency`.

#### Is scraping Craigslist allowed?

The Actor reads only public pages at a gentle pace. You are responsible for using the data in line with the Craigslist terms of use and the laws that apply to you, including privacy laws.

### Support

For a bug, a question or a feature request, open an issue in the **Issues** tab of this Actor. Include the run ID and the input you used. Do not post API tokens or other private data.

# Changelog

This Actor's version history is a separate document: https://apify.com/cliqtomedia/craigslist-scraper/changelog.md

# Actor input Schema

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

Craigslist sites to search: the subdomain from the city address, e.g. "sfbay" for sfbay.craigslist.org, "newyork", "losangeles", "london", "toronto". Full addresses work too. Any of the 700+ Craigslist sites, up to 50 per run.

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

Craigslist section to search. "— all" entries cover the whole group.

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

Words as you would type them in the Craigslist search box, e.g. "road bike" or "iphone 15". Leave empty to list the whole category.

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

Upper limit of listings saved for each city, newest first. Craigslist itself lists at most 10,000 per search.

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

Also open each listing page for the full description, attributes (condition, make, odometer, bedrooms…), all photos and ZIP code. Slower and charged as a detailed listing. Phone numbers and e-mails in the text are always masked.

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

Saves (and charges) only listings posted or renewed after the previous run of the same search. The first run saves the newest listings up to the limit and sets the starting point. Use it with a schedule to monitor a search.

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

Only listings priced at or above this, in the local currency of the site.

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

Only listings priced at or below this, in the local currency of the site.

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

All sellers, owners only or dealers only (for-sale categories).

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

Skip listings without pictures.

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

Only listings posted today.

## Actor input object example

```json
{
  "cities": [
    "sfbay"
  ],
  "category": "sss",
  "query": "bike",
  "maxItems": 10,
  "includeDetails": false,
  "onlyNew": false,
  "sellerType": "all",
  "hasImages": false,
  "postedToday": false
}
```

# Actor output Schema

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

Craigslist listings as a table.

## `listingsWithDetails` (type: `string`):

Description, attributes and ZIP code (filled when includeDetails is on).

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

Status, per-city counts, totals available, charged listings and request statistics.

# 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": [
        "sfbay"
    ],
    "category": "sss",
    "query": "bike",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("cliqtomedia/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": ["sfbay"],
    "category": "sss",
    "query": "bike",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("cliqtomedia/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": [
    "sfbay"
  ],
  "category": "sss",
  "query": "bike",
  "maxItems": 10
}' |
apify call cliqtomedia/craigslist-scraper --silent --output-dataset

```

## MCP server setup

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