# Craigslist Scraper Premium (`newbs/craiglist-scraper-premium`) Actor

Scrape Craigslist listings by city, category, and keyword. Export titles, prices, dates, URLs, images, descriptions, locations, IDs, attributes, salary, and housing details.

- **URL**: https://apify.com/newbs/craiglist-scraper-premium.md
- **Developed by:** [Newbs](https://apify.com/newbs) (community)
- **Categories:** E-commerce, Jobs
- **Features:** Limited permissions
- **Stats:** 17 total users, 4 monthly users, 90.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Premium

Scrape Craigslist listings by city, category, and keyword. Full listing details are enabled by default. The Actor uses Craigslist’s lightweight JSON endpoints, with an HTML fallback, and fetches up to 12 listing details concurrently. It supports bulk exports of up to 10,000 listings.

### Input

```json
{
  "category": "for sale",
  "city": "New York",
  "searchKeyword": "iPhone",
  "numberOfResults": 30
}
```

| Field | Default | Description |
| --- | --- | --- |
| `category` | `for sale` | `community`, `housing`, `jobs`, `services`, `for sale`, `gigs`, or `resumes`. |
| `city` | `New York` | City name, site slug, city URL, `https://www.craigslist.org/area/newyork`, or a copied search URL such as `https://www.craigslist.org/search/area/newyork?cat=sss`. The category and keyword input fields control the search. |
| `searchKeyword` | empty | Optional search text. |
| `numberOfResults` | `10` | Maximum listings to return, 1–10,000. Availability, runtime and spending limits may reduce the total. |
| `includeDetails` | `true` | Fetch descriptions, dates, numeric post IDs, gallery images, addresses, and attributes. Set to `false` for search-only exports. |
| `maxConcurrency` | `12` | Concurrent detail requests, 1–32. Lower this when rate-limited. |
| `requestTimeoutSecs` | `15` | Timeout for each HTTP attempt, 5–60 seconds. |
| `maxRetries` | `1` | Retries for temporary failures, 0–2. Removed listings are not retried. |
| `proxy` | direct connection | Optional automatic Apify datacenter proxy (`useApifyProxy: true`). Residential proxies, selected groups, custom URLs and credential overrides are rejected before scraping. |

#### Fast search-only export

```json
{
  "category": "housing",
  "city": "sfbay",
  "numberOfResults": 1000,
  "includeDetails": false
}
```

This mode saves titles, URLs, prices, locations, and any images available in search metadata. Descriptions and addresses require full details; IDs, dates, salary and other metadata are included when available in search data. `listingKey` identifies both older numeric URLs and newer opaque Craigslist URLs.

### Output

Existing fields are preserved: `title`, `priceInfo`, `postedDate`, `updatedDate`, `postLink`, `imageSrc`, `description`, `location`, `id`, `postAdress`, `postAddress`, `attribute`, `companyName`, `salary`, `housingSize`, `category`, `city`, `searchKeyword`, and `scrapedAt`.

Both `postAdress` (the original spelling) and `postAddress` contain the same address. Missing source fields remain empty strings or empty arrays. `id` remains the numeric Craigslist post ID when available; `listingKey` can also represent a modern opaque URL identifier.

Every exported listing has `detailStatus: "complete"` when details were fetched or `"not_requested"` for a search-only export. Optional fields can still be empty when Craigslist does not provide them.

Removed or failed detail listings are excluded from the result dataset. The Actor tries additional search candidates to reach your requested count. A sample of up to 100 failed listing URLs and error codes is saved separately in the key-value store’s `DIAGNOSTICS` record. Empty searches and errors never create billable diagnostic rows.

The key-value store’s `OUTPUT` record reports result counts, failures, search pages, HTTP requests, retries, downloaded bytes, timing, API/fallback usage, billing counters, and the reason the run stopped. Status is `complete`, `partial`, `empty`, or `failed`. A run can finish below the requested count when the source is exhausted or the spending limit is reached. If a later HTML search page fails, the Actor still exports usable listings from earlier pages and reports `search_error` with the failing URL and error code. Failed runs include an `errorMessage` and a specific `INVALID_INPUT` code for input errors.

`OUTPUT.health.needsAttention` flags failed runs and incomplete runs stopped by blocking, the scraping time budget, or a search-page error. Its `reason` identifies the failure or stop code. Expected empty searches, source exhaustion, spending limits, and user aborts keep this flag false. Partial runs can have a platform status of `SUCCEEDED`, so integrations should inspect the summary and health flag as well as the platform status.

### Pricing and result billing

When pay-per-result pricing is active, one result means one successfully saved listing in the default dataset. Search-only listings also count as results. The Actor checks the remaining spending allowance before fetching details and before saving each batch. It uses Apify’s automatic dataset-item event; failed listings and diagnostics are never written to the billable dataset.

The pricing is **$0.50 per 1,000 successfully saved results** ($0.0005 each), with Apify platform usage included and no additional start fee. Search-only results have the same per-result price. No price is hard-coded in the scraper.

### Reliability and performance

- Uses Craigslist’s current canonical search URLs, avoiding two legacy redirects.
- Reuses HTTP connections and bounds concurrency; results retain search order.
- Streams listing rows to Apify in batches of up to 64 full listings or 1,000 search-only listings, keeping memory bounded and making results available during the run.
- Fetches bulk search metadata in one request; stops detail requests at the result or spending limit. The HTML fallback follows only explicit next-page links and stops on repeated pages.
- Deduplicates URLs and resolved numeric post IDs.
- Retries transient errors with backoff, respects `Retry-After`, and stops within a scraping budget of at most 240 seconds (less for shorter platform timeouts).
- Extracts all static gallery links and removes duplicate image sizes.
- Defaults to 256 MB of memory, with a 512 MB maximum to limit resource costs.
- Uses a Node.js 22 runtime, a locked dependency tree, and a small browser-free container.

Craigslist results change continuously. Network latency, server load, proxy location, removed posts, and blocking affect runtime and completeness. The supported datacenter proxy usage is included in the new result price. The Actor does not use residential, Unblocker, SERP or custom proxies.

### Local development

Requires Node.js 22 or newer.

The locked dependency tree includes patched Crawlee, JSON streaming, and HTTP cache packages. A scoped npm override pins the FTP client pulled in by `get-uri` to `6.2.3`, which fixes the directory-listing denial-of-service advisory. The scraper's supported proxy modes remain direct and automatic Apify datacenter connections.

```sh
npm ci
npm test
npm run check
npx apify-cli validate-schema
npx apify-cli run --input-file example-input.json
```

For a manual local run, put input in `storage/key_value_stores/default/INPUT.json`, then run `npm start`. Use `CRAWLEE_STORAGE_DIR` to select a separate local storage directory.

# Actor input Schema

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

Craigslist category to scrape.

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

City name, Craigslist site slug, or city/search URL. Examples: New York, newyork, https://newyork.craigslist.org, https://www.craigslist.org/search/area/newyork. Category and keyword are taken from the fields below.

## `searchKeyword` (type: `string`):

Optional keyword to filter listings.

## `numberOfResults` (type: `integer`):

Maximum number of successfully exported listings, up to 10,000. Availability and the run spending limit may reduce the total.

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

Fetch descriptions, all gallery images, post IDs, dates, addresses and attributes. Turn off for the fastest search-only export; detail-only fields may be empty.

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

Number of listing detail requests in parallel. Lower this if Craigslist rate-limits your IP.

## `requestTimeoutSecs` (type: `integer`):

Maximum time for each HTTP attempt, including reading the response.

## `maxRetries` (type: `integer`):

Retry temporary network errors and rate limits. Removed listings are not retried.

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

Optional automatic Apify datacenter proxy. Residential proxies, custom proxy URLs and selected proxy groups are not supported. Leave disabled for direct connections.

## Actor input object example

```json
{
  "category": "for sale",
  "city": "New York",
  "searchKeyword": "iPhone",
  "numberOfResults": 10,
  "includeDetails": true,
  "maxConcurrency": 12,
  "requestTimeoutSecs": 15,
  "maxRetries": 1,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

## `diagnostics` (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 = {
    "category": "for sale",
    "city": "New York",
    "searchKeyword": "iPhone",
    "numberOfResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("newbs/craiglist-scraper-premium").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 = {
    "category": "for sale",
    "city": "New York",
    "searchKeyword": "iPhone",
    "numberOfResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("newbs/craiglist-scraper-premium").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 '{
  "category": "for sale",
  "city": "New York",
  "searchKeyword": "iPhone",
  "numberOfResults": 10
}' |
apify call newbs/craiglist-scraper-premium --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,newbs/craiglist-scraper-premium"
        }
    }
}
```

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/0QlaCm1ISElFTC7Md/builds/X2tpcMyeTweIiDu6a/openapi.json
