To scrape Craigslist, give a scraper a Craigslist search URL, or a list of cities and categories, and let it save every matching ad as a row: title, price, neighborhood, category, when it was posted and refreshed, attributes such as condition and make, and the full ad text. The fastest way is the Craigslist Scraper on Scrape.ly. It reads search results in bulk, opens each ad for the details, and costs $4 per 1,000 ads, with no login, API key or proxy of your own.
This guide covers the no-code steps, the API (curl, Python, Node.js and AI agents over MCP), the cost, the pitfalls and how to use the data responsibly. Everything comes from a real run on 23 October 2026: bike ads in New York.
What data you can get from Craigslist
The Craigslist Scraper writes one row per ad, in the same shape for every section (for sale, housing, jobs, gigs, services). These are the fields you'll use most:
| Field | What it holds |
|---|---|
title, url, id | Ad title, link and Craigslist post ID |
price, priceValue, currency | Price as shown ("$500"), as a number, and the currency |
location, area, subarea | Neighborhood text, Craigslist site (newyork) and sub-area (mnh, brk) |
category, categoryCode | Category name ("bicycles - by owner") and code |
postedAt, datetime | When the ad was first published, and when it was last updated or reposted |
post | The full ad text |
attributes, properties | Attributes as listed ("wheel size: 29 in") and by name |
condition, manufacturer, model | For-sale details when the seller fills them in |
bedrooms, bathrooms, space, availableFrom, rentPeriod | Housing details |
compensation, employmentType, companyName | Jobs and gigs details |
pics, latitude, longitude, address | Photos, map position and address parts when the poster shows them |
phoneNumbers | Phone numbers written in the ad text |
Craigslist ads are free text, so a field appears only when the ad has it: bedrooms on housing, pay on jobs, condition on items for sale.
How to scrape Craigslist without code
- Open the Actor's page in the Scrape.ly Store.
- Pick a ready-made input. We used Bikes for sale in New York, 10 ads. Two more presets show the other shapes: road bikes in the SF Bay Area from $100 to $2,000 and apartments for rent in Chicago.
- Paste your own Craigslist search URL, or switch to cities and categories. This is the exact input of our run:
{
"urls": [{ "url": "https://newyork.craigslist.org/search/sss?query=bike" }],
"maxItems": 10,
"scrapeDetails": true
}- Click Start. Our 10 ads took 10 seconds, most of it container start-up.
- Export the ads as JSON, CSV, Excel or HTML.
The fields that matter most:
urls: any Craigslist search page; filters already in the URL (price, query, distance) are kept. Single ads work too, ashttps://www.craigslist.org/view/...links.citiesandcategories: site names such asnewyork,sfbayorchicago, combined with category codes such asbia(bicycles),apa(apartments) orjjj(jobs). Fifty cities and five categories make 250 searches in one run.searchQuery,titleOnly: keywords for every search, optionally matched against titles only.minPrice,maxPrice,maxAge,sellerType,sortBy: price range, maximum age in days, owner or dealer, and sort order.scrapeDetails: opens every ad for the full text, attributes and posting date. Turn it off for a fast list of titles, prices and images; the price per ad stays the same.maxItems: total ads for the run (default 20,0for no limit).
How to scrape Craigslist with the API
Create an API token under Settings → API & Integrations and export it as SCRAPELY_TOKEN.
Our run took 10 seconds, so the synchronous endpoint returns the ads in one request:
curl -X POST "https://api.scrape.ly/v2/actors/shelfbright~craigslist-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls": [{"url": "https://newyork.craigslist.org/search/sss?query=bike"}], "maxItems": 10, "scrapeDetails": true}'The synchronous call waits up to 45 seconds. For thousands of ads or many cities, start the run with /runs and read the dataset when it finishes, as the Actors API docs show.
Python (pip install scrapely-client-python). call() waits however long the run takes. Sellers often post the same ad in several categories, so this example drops repeats with the same title and text, then sorts by price:
import os
from scrapely_client import ScrapelyClient
client = ScrapelyClient(os.environ["SCRAPELY_TOKEN"], api_url="https://api.scrape.ly")
run = client.actor("shelfbright/craigslist-scraper").call(
run_input={"urls": [{"url": "https://newyork.craigslist.org/search/sss?query=bike"}],
"maxItems": 10, "scrapeDetails": True}
)
print(run.status) # SUCCEEDED, FAILED, ABORTED or TIMED-OUT
seen, ads = set(), []
for ad in client.dataset(run.default_dataset_id).iterate_items():
key = (ad["title"].lower(), (ad.get("post") or "")[:200])
if key not in seen:
seen.add(key)
ads.append(ad)
for ad in sorted(ads, key=lambda a: a.get("priceValue") or 0):
print(f'{ad.get("price") or "no price":>8} {ad["category"][:26]:26} {ad["location"][:16]:16} {ad["title"][:50]}')Node.js (npm install scrapely-client):
import { ScrapelyClient } from 'scrapely-client';
const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN, baseUrl: 'https://api.scrape.ly' });
const run = await client.actor('shelfbright/craigslist-scraper').call({
cities: ['newyork', 'chicago'],
categories: ['bia'],
searchQuery: 'road bike',
maxItems: 20,
maxAge: 7,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((a) => [a.area, a.priceValue, a.condition, a.title]));For AI agents, add the Scrape.ly MCP server. In Cursor, put this in ~/.cursor/mcp.json:
{
"mcpServers": {
"scrapely": {
"url": "https://api.scrape.ly/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}In Claude Code, run claude mcp add --transport http scrapely https://api.scrape.ly/mcp and sign in with /mcp. Then ask: "Use scrapely to run shelfbright/craigslist-scraper on bicycles in sfbay from $100 to $2,000, 20 ads, and give me a table of price, condition and neighborhood." Runs this size finish inside the 45 seconds call-actor waits; for big runs the agent polls get-actor-run. The Craigslist API page shows the same Actor as an API product.
A real run: bike ads in New York
We ran the "Bikes for sale in New York, 10 ads" preset on 23 October 2026. It returned 10 ads in 10 seconds and was charged $0.0405. The first five rows:
| Title | Price | Neighborhood | Sub-area | Category | Condition | Make |
|---|---|---|---|---|---|---|
| 2018 Cannondale fsi Carbon 5 | $500 | Upper West Side | mnh | bicycles - by owner | good | Cannondale |
| Monza bicycle 80's style bmx 24" bike skyways tange new! | $2,000 | New York | mnh | bicycles - by owner | new | Monza |
| Men's mountain bike | no price | Flushing | que | wanted - by owner | not stated | not stated |
| Woom 5 Red - Lightly Used, Excellent Condition! - Brooklyn Pick up | $550 | Midwood | brk | bicycles - by owner | not stated | Woom |
| LIKE NEW Lizard Lime Woom 2 Kids Bike – (Brooklyn) | $350 | brooklyn | brk | bicycles - by owner | not stated | Woom |
These sellers are private people. Craigslist doesn't show their names and the Actor doesn't collect them; we also left out post links, map coordinates and ad text so no ad can be traced back to its owner. What the full ten show:
- A keyword search is broad. "bike" matched five bicycle ads, but also an exercise bike, a helmet, a vintage bike in sporting goods and a "wanted" ad. Pick the bicycles category (
bia) or turn ontitleOnlywhen you want bikes only. - Cross-posts are common. The same exercise bike appeared three times, in bicycles, garage sales and general for sale, with three different post IDs. Deduplicating on title and text left 8 unique ads, as in the Python example.
- Refreshed is not new. All ten were updated between 10:16 and 10:45 pm on 22 October, but nine had first been published earlier, as far back as 2 August.
datetimetells you when an ad was refreshed;postedAttells you when it first went up. - Prices are optional. Six of ten had a price, from $25 to $2,000. Wanted ads and some for-sale ads have none.
- Structured details come along. Eight of ten carried attributes such as wheel size, frame material, brake type and condition, the fields you need to compare like with like.
The log, without the platform's start-up lines:
2026-10-23T03:01:25.477Z [Scrapely] Container started (setup 4.0 s)
2026-10-23T03:01:26.784Z [scrapely.proxy_configuration] INFO Platform proxy access check succeeded.
2026-10-23T03:01:31.010Z [scrapely.actor] INFO [Status message]: 10 ads saved. Stopped at the limit of 10
2026-10-23T03:01:31.492Z [Scrapely] Container state: status=exited exitCode=0 oomKilled=falseReading the search and opening ten ads took about four seconds on the default datacenter proxy. The publisher's own tests put 1,000 ads with full descriptions at about 30 seconds.
What it costs
The Actor charges one event: $0.004 per ad saved, which is $4 per 1,000 ads. Platform usage is billed on top and is tiny here, because searches are read from the JSON feed the Craigslist site itself uses, not through a browser.
For our run: 10 ads × $0.004 = $0.04 in ad fees. The run was charged $0.0405 in total: $0.0003 of compute, $0.0002 of datacenter proxy traffic and a fraction of a cent of storage. Ads removed before their page loads are not saved and not charged. A 1,000-ad pull costs about $4, so the Free plan's $5 of monthly credit covers it, and a maximum charge per run stops a big job before it goes over.
What to watch out for when scraping Craigslist
- 10,000 ads per search. Craigslist caps every search there. Split big jobs by city, category, price range or keyword; the run's status says when a search hit the cap.
- Old post links are dead. Links ending in
.htmlno longer open on Craigslist. Usehttps://www.craigslist.org/view/...links or a search URL. - Contact details stay hidden. Craigslist puts contact details behind its reply button, and the Actor doesn't press it.
phoneNumbersonly holds numbers a seller typed into the ad; our ten ads had none. - Duplicates and reposts. Deduplicate cross-posts by title and text, and use
postedAtto tell new ads from refreshed ones. - Use the site name, not the city.
citiestakes Craigslist's own site names on craigslist.org, such asnewyork,sfbayorlondon; copy them from the subdomain of the search page. - Blocks. The built-in datacenter proxy worked in our run. If Craigslist starts refusing requests, switch
proxyConfigurationto residential. - Personal data. Sellers, landlords and gig posters are mostly private people. Addresses, map positions and phone numbers in ads are personal data, so collect them only if you truly need them.
Is it legal to scrape Craigslist?
The Actor reads publicly visible ads without logging in, and it never posts, replies or uses the reply button.
Craigslist's Terms of Use cover automated collection, high-volume access and contacting users, so read them and check how they apply to your project before you start.
Responsible practice keeps the footprint small: set maxItems and a maximum charge per run so each job stays at a low volume, analyze prices and supply rather than republishing ads, never contact posters with scraped details, and store personal data only with a lawful basis under privacy laws such as the GDPR and the CCPA. Our guide on whether web scraping is legal covers the general rules. This is not legal advice; if Craigslist data feeds a commercial product, get legal advice for your use case.
Why Scrape.ly for Craigslist
- There is no read API. Craigslist's only developer interface is its bulk posting interface, which submits new ads and is "granted to high volume posters (hundreds of postings per month or more) on a case-by-case basis", limited to paid US categories. It doesn't return search results. The Actor does.
- Tutorials go stale; fields don't. The top-ranking ScrapingBee tutorial (January 2026) parses
.result-infoand.result-title. When we loaded a New York search page on 23 October 2026, it had zero.result-infoelements; listings now sit inli.cl-static-search-result. The Actor reads the JSON feed the Craigslist site itself uses and hands youprice,postedAtandattributesas named fields. - Full ads, every section, one shape. Search snippets give you a title and a price. The Actor opens each ad for the full text, attributes, posting date and map position, and housing, jobs and for-sale ads come back in the same format.
- Cities times categories. One run can cover 50 cities and 5 categories, with duplicates removed. Schedule it and compare runs to spot new ads.
- Part of a resale toolkit. The same publisher maintains the eBay Scraper and scrapers for Amazon, Walmart and Google Shopping. See how to scrape eBay and our price scraping guide to compare used and new prices.
FAQ
Does Craigslist have an API?
Not for reading ads. Craigslist offers a bulk posting interface for high-volume paid posters, and nothing for searching or downloading listings. To collect ads, use a scraper that reads public search pages.
Can I scrape Craigslist with Python?
Yes. Install scrapely-client-python, call shelfbright/craigslist-scraper with a search URL or cities and categories, and iterate over the dataset, as in the example above. You don't need BeautifulSoup selectors or your own proxies.
How do I scrape Craigslist across several cities?
Put the site names in cities (for example newyork, sfbay, chicago) and the category codes in categories. The Actor runs every city and category pair in one go and removes duplicates.
Can I scrape Craigslist housing or job ads?
Yes. Use apa for apartments, hhh for all housing, jjj for jobs or ggg for gigs. Housing rows add bedrooms, bathrooms, space and move-in date; jobs add compensation and employment type.
How much does it cost to scrape Craigslist?
$0.004 per ad, so $4 per 1,000, plus a little compute and proxy usage. Our 10-ad run was charged $0.0405 in total.




