Craigslist Multi-City Listings Scraper icon

Lead generation

Craigslist Multi-City Listings Scraper

Scrape Craigslist listings across dozens of US metro areas and 7 categories in one run — title, price, location, images, posted/updated dates, structured attributes, and the public reply link, normalized into one clean dataset instead of one run per city.

Free Apify credit covers a first run. No credit card to try.

What this Actor scrapes

Craigslist's own search pages (/search/area/<areaSlug>) and listing-detail pages (/view/d/<slug>/<postId>) -- fanned out across every metro area and category you pick, in one Actor call instead of one run per city. Point it at a single area, or union in a curated top-US-metro preset, pick one or all seven top-level categories, and get back a single normalized dataset: title, price, location, coordinates, images, posted and updated timestamps, structured attributes, description, and the public reply-flow link. Every request runs through our full anti-blocking stack by default -- rotating browser-fingerprint impersonation, session-rotated proxying, retries with backoff -- the same defenses every Devil Scrapes Actor ships, so your dataset holds up even if Craigslist changes its defenses tomorrow.

What we handle for you

  • 🛡️ We rotate browser fingerprints on every request -- curl-cffi impersonates real Chrome, Firefox, and Safari TLS/HTTP handshakes, so nothing about the traffic looks like a bare Python script, today or if Craigslist tightens up tomorrow.
  • 🌐 We rotate Apify Proxy sessions -- a fresh session ID and exit IP on any request that comes back looking blocked, no manual restart needed.
  • 🔁 We retry with exponential backoff on 408 / 429 / 5xx, honouring Retry-After -- up to 5 attempts before we give up on a page.
  • 🧱 We pace to the target's rate limits and isolate faults per combo -- one slow metro or blocked category logs a warning and gets skipped; the rest of the run keeps going, and partial progress is always reported via a clear status message, never a silent empty dataset.
  • 🧊 Clean, typed dataset rows -- every row is ResultRow-validated (Pydantic), ISO-8601 timestamps, stable post IDs, ready to export as JSON, CSV, or Excel.
  • 💰 Pay-per-result pricing -- a small one-off actor-start charge, then you pay only for rows that land in your dataset.

Use cases

  • Reseller and arbitrage price-scanning across dozens of metros in a single run, instead of one Actor call per city.
  • Multi-city lead-generation list building for local-services and real-estate outreach.
  • Rental and vehicle market intelligence -- price and inventory tracked across metros side by side.
  • Apartment and job aggregation, normalized into one schema instead of stitching together per-city exports.
  • Posting-velocity research -- track price drops and reposts via posted_at, updated_at, and is_edited_or_price_drop.

Input

Paste this into the Apify Console, or send it as the run input over the API. Proxy settings are on by default; you rarely need to touch them.

FieldTypeRequiredWhat it does
areas array no Craigslist area slugs to search (e.g. "newyork", "losangeles") from /about/sites. Combined with All top US metros below when that's enabled. Up to 20 entries.
allTopUsMetros boolean no Union in a curated list of up to 25 top-US-metro Craigslist area slugs (not the full ~700-area directory) on top of anything listed in Areas.
categories array no Top-level Craigslist category codes to search: sss (for sale), cta (cars & trucks), reo (real estate), apa (apartments/housing), jjj (jobs), bbb (community), ggg (gigs). Use "all" to expand to all 7.
query string no Single search term, passed verbatim to Craigslist's search. Leave empty to browse a category with no keyword filter.
maxResultsPerCombo integer no Cap on listings collected per (area, category) combo. 300 is Craigslist's observed single-page result-set ceiling.
fetchDetails boolean no When enabled, fetch each listing's detail page for posted/updated dates, full description, structured attributes, and the public reply-flow URL. When disabled, only search-list fields are returned.
postedWithinDays integer no Drop listings older than this many days. Only enforceable when Fetch listing detail pages is enabled -- posting date is a detail-page-only field.
maxRunMinutes integer no Run-wide wall-clock budget. New (area, category) combos stop being started once this is exceeded; already-collected rows are still flushed.
{
  "areas": [
    "newyork"
  ],
  "allTopUsMetros": false,
  "categories": [
    "sss"
  ],
  "query": "iphone",
  "maxResultsPerCombo": 3,
  "fetchDetails": true,
  "postedWithinDays": null,
  "maxRunMinutes": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}

Output

One row per result, schema-validated before it is written. Export JSON, CSV, Excel or XML from the run, or read it over the API.

area_slugcategory_codetitlepricelocation_textdetail_urlposted_atupdated_atis_edited_or_price_dropattributesreply_url

{
  "area_slug": "newyork",
  "category_code": "sss",
  "query": "iphone",
  "post_id": "7812345678",
  "title": "iPhone 14 Pro 256GB Unlocked",
  "price": 550.0,
  "currency": "USD",
  "location_text": "Queens",
  "latitude": 40.7282,
  "longitude": -73.7949,
  "images": [
    "https://images.craigslist.org/00A0A_abc_600x450.jpg"
  ],
  "detail_url": "https://www.craigslist.org/view/d/queens/7812345678",
  "posted_at": "2026-08-05T09:12:00-0400",
  "updated_at": "2026-08-09T14:03:00-0400",
  "is_edited_or_price_drop": true,
  "description": "Barely used, comes with box and charger...",
  "attributes": {
    "condition": "like new",
    "make": "apple"
  },
  "reply_url": "https://newyork.craigslist.org/reply/que/sss/7812345678/abc123",
  "repost_fingerprint": "a1b2c3d4",
  "scraped_at": "2026-08-12T12:00:00.000Z"
}

Pricing

EventPriceWhen
Actor start$0.20Once per run, covers warm-up and proxy session setup.
Result emitted$0.0020Per result written to the dataset.

You pay only for results that land. Cap any run with maxTotalChargeUsd. See pricing & billing for worked examples.

Limitations

  • Each (area, category) combo returns up to maxResultsPerCombo (max 300) -- Craigslist's observed single-page ceiling. There's no pagination past that in v1.
  • Total combos (areas x categories, post-resolution) are capped at 150 per run, a FREE-tier compute guardrail -- split very large fan-outs across multiple runs.
  • allTopUsMetros unions in a curated list of up to 25 metro slugs, not the full ~700-area Craigslist directory.
  • One query term per run -- no multi-keyword search in a single call.
  • reply_url is captured exactly as Craigslist renders it on the listing page. We never open it, solve any verification step it triggers, or de-obfuscate a masked email -- that boundary is deliberate, and Craigslist's own robots.txt disallows the reply flow.
  • repost_fingerprint is a best-effort hash (normalized title + area + category), not a guaranteed unique dedup key.
  • postedWithinDays only filters when fetchDetails is enabled -- posting date lives on the detail page only.

FAQ

Can I get a seller's phone number or email from this?
No. We surface reply_url exactly as Craigslist places it on the listing page, but we never open it, solve any verification step it triggers, or de-obfuscate a masked email. That's a deliberate scope boundary, not a technical shortfall -- Craigslist's own robots.txt disallows the reply flow, and we respect it.
Why one run instead of one run per city?
Every incumbent we benchmarked (memo23, automation-lab, solidcode, ivanvs) makes you run their Actor once per metro, then stitch the exports together yourself. We fan out across up to 20 explicit areas plus an optional curated top-US-metro preset, and every category you pick, in one call, into one normalized dataset.
What happens if one city or category is unreachable mid-run?
We isolate failures per (area, category) combo -- a dead metro or a blocked category logs a warning and gets skipped, the rest of the run keeps going, and you get a clear status message on what finished. We never fail the whole run over one bad combo, and we never hand back a silent empty dataset.
Do I need Residential proxy?
Not by default -- we route every request through the standard Apify Proxy group with session rotation and browser-fingerprint impersonation layered on top. Switch proxyConfiguration to Residential anytime you want that extra layer for larger or more aggressive runs.
How fresh are `posted_at` and `updated_at`?
Both are read live from the listing's own detail page during your run, when fetchDetails is on. Turn fetchDetails off to skip the detail-page fetch entirely and get search-list fields only, faster and cheaper.
Is this legal to run?
We only fetch what Craigslist serves on its public search and listing pages. We never touch the reply flow, never log in, and never scrape any authenticated seller feature. Match your own use case against Craigslist's terms before using the data commercially.

Ready to run it?

Open the listing on Apify, paste the input above, and watch rows land. If it ever breaks, it is our problem before it is yours.

Related Actors

Teams that run this also run