How to get Craigslist listings in Python
By the end of this guide you have a CSV of San Francisco Bay Area apartments for rent from Craigslist, with price, size and posting date, from one script you can run again tomorrow. Everything below was run on 16 October 2026; the output table is what it returned.
Why scraping Craigslist yourself breaks
- Craigslist has no API for reading or searching listings. Its Bulk Posting Interface only submits paid postings, and only for approved high-volume posters.
- Bedrooms, bathrooms and size are not separate fields on the search page. They sit in short text such as "2BR / 2Ba" and "1200ft2" that you have to parse.
- One search page lists about 350 postings. Getting more means reading the same search in other sort orders and dropping the repeats.
None of that is impossible, but each point is code you then maintain. The rest of this guide uses Listingrail, which does the reading and hands back clean JSON. The Craigslist API page has the official options with sources.
1. Get a key
One request with your email returns a key with 100 free listings. It is shown once, so keep it in an environment variable.
curl -X POST https://listingrail.com/v1/signup \
-H "content-type: application/json" \
-d '{"email": "you@example.com"}'
export LISTINGRAIL_KEY=re_...2. Search and save to CSV
Install requests (pip install requests), then run:
import csv
import os
import requests
r = requests.post(
"https://listingrail.com/v1/listings/search",
headers={"Authorization": f"Bearer {os.environ['LISTINGRAIL_KEY']}"},
json={"source":"craigslist","location":"sfbay","type":"rent","limit":50},
timeout=300,
)
r.raise_for_status()
listings = r.json()["listings"]
fields = ["price", "currency", "beds", "baths", "area", "address", "url"]
with open("craigslist-sf-bay-area.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(fields)
for home in listings:
writer.writerow([home[k] for k in fields])
print(len(listings), "listings saved to craigslist-sf-bay-area.csv")The request body, on its own:
{
"source": "craigslist",
"location": "sfbay",
"type": "rent",
"limit": 50
}Each listing returned costs one credit. A search that finds nothing, or fails at Craigslist, costs nothing. A request can take up to a minute or two, because Craigslist is read at that moment.
3. What comes back
The first three rows of the run on 16 October 2026:
Every listing has the same keys on all 17 portals, so the code for Craigslist reads the others too. Fields Craigslist does not show come back as null, and the portal's own record sits in raw. With pandas:
import pandas as pd
df = pd.DataFrame(listings)
df["agent"] = df["agent"].str.get("company")
print(df[["price", "beds", "baths", "area", "address", "agent"]].head())4. Get new listings and price cuts every day
Running the script on a schedule works, but then you compare the files yourself. A monitor stores the search, checks it once a day, and sends only what changed: new listings and price cuts. The free key holds one monitor.
requests.post(
"https://listingrail.com/v1/monitors",
headers={"Authorization": f"Bearer {os.environ['LISTINGRAIL_KEY']}"},
json={
"source": "craigslist",
"location": "sfbay",
"filters": {"type":"rent"},
"webhook_url": "https://example.com/new-listings",
},
timeout=30,
)Changes can also be read with GET /v1/monitors/{id}/changes or downloaded as CSV. Plans and limits are on the monitors page.
Craigslist quirks worth knowing
- Pass the Craigslist site code, not a city name: "sfbay", "newyork", "losangeles". "sfbay" covers the whole Bay Area, from Santa Rosa to Gilroy.
- address and city are null. Craigslist has no address field; what the poster typed as a location is in raw.location.
- area is in square feet and null when the poster left it out. listed_at is the posting time with the local UTC offset.
- agent is always empty. raw.by_owner says whether the poster listed it as by owner.
Questions
Does Craigslist have an API for housing listings?
No. The official interface is for posting ads in paid categories, not for reading them.
Can I get listings for sale too?
Yes: use "type": "sale" for the real estate for sale section.
Why is the address missing?
Craigslist posts have no address field. Many posters write a cross street or neighbourhood, which comes back in raw.location.