How to get Fotocasa listings in Python
By the end of this guide you have a CSV of Barcelona flats for rent from Fotocasa with rent, bedrooms, size and agency, 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 Fotocasa yourself breaks
- Fotocasa's official integration lets agencies publish their own listings with an API key. No official API for searching or reading listings was found.
- The search results sit as JSON inside each page (window.__INITIAL_PROPS__), one page of results at a time, so a whole city means paging through them.
- Price filters are part of Fotocasa's own search link, so a place name alone cannot carry them.
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 Fotocasa 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":"fotocasa","location":"barcelona","type":"rent","limit":50},
timeout=300,
)
r.raise_for_status()
listings = r.json()["listings"]
fields = ["price", "currency", "beds", "baths", "area", "address", "url"]
with open("fotocasa-barcelona.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 fotocasa-barcelona.csv")The request body, on its own:
{
"source": "fotocasa",
"location": "barcelona",
"type": "rent",
"limit": 50
}Each listing returned costs one credit. A search that finds nothing, or fails at Fotocasa, costs nothing. A request can take up to a minute or two, because Fotocasa 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 Fotocasa reads the others too. Fields Fotocasa 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": "fotocasa",
"location": "barcelona",
"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.
Fotocasa quirks worth knowing
- Pass a Spanish city ("barcelona", "madrid"). For price filters, build the search on fotocasa.es and pass its link as location.
- address is the neighbourhood ("Pedralbes", "El Raval"), not the street. The coordinates are in lat and lng.
- area is in square metres as the advertiser typed it, so check for odd values: one row in this run said 1 m².
- listed_at can be years old: the first row of this run was published in March 2024. agent.company is the agency, and agent.phone is filled when Fotocasa shows a number.
Questions
Does Fotocasa have a public API?
No. Its API is for agencies publishing their own stock to Fotocasa, Habitaclia and Milanuncios.
Does it cover homes for sale?
Yes: use "type": "sale".
Is this the same as Idealista?
No, they are separate portals. Use "source": "idealista" for Idealista; the listing format is the same.