Craigslist Listings Scraper — For Sale, Housing, Jobs, Services
Pricing
Pay per usage
Craigslist Listings Scraper — For Sale, Housing, Jobs, Services
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.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Mori
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
0
Monthly active users
13 days ago
Last modified
Categories
Share
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+ optionalsearchQuery. Pagination is?s=<offset>whereoffset = 120 * pageIndex. - For each search-result page, the actor:
- Pulls
<script id="ld_searchpage_results">JSON (rich structured data: title, price, neighborhood, geo, image). - Pulls all
/view/d/.../...hrefs in result order from the DOM. - Pairs them by index (
itemListElement[i] ↔ hrefs[i]). Items without a matching href are skipped (CL occasionally trims; the actor never hallucinates URLs). - Pushes one Dataset record per item.
- If
fetchBodies=true, follows the first 5 post URLs per page to grab the post body + timestamp.
- Pulls
- Detail fetcher pulls
#postingbodytext (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 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):
{"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:
{"city": "newyork","category": "sss","searchQuery": "furniture","maxPages": 5,"fetchBodies": true}
Cars+trucks in Chicago, no body fetches (faster):
{"city": "chicago","category": "cta","searchQuery": "","maxPages": 3,"fetchBodies": false}
Browse all sublets in Seattle (no query):
{"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).
maxPageshard-capped at 25 (input schema enforces 1-25). At 120 results per page that's 3000 listings per run.bodyExcerptisnullwhenfetchBodies=false, or for items past the first 5 per page even whenfetchBodies=true(capped to keep request count bounded).priceValueisnullwhen Craigslist shows no price (e.g. "Please contact" listings).priceretains 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, 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.