Bien'ici Property Scraper (France)
Pricing
from $0.84 / 1,000 results
Bien'ici Property Scraper (France)
Scrapes flats and houses for sale or rent from Bien'ici, France's #3 real estate portal. Search any city, district or postal code; returns price, surface area, rooms, bedrooms, floor, energy class, full description, photos and seller details.
Pricing
from $0.84 / 1,000 results
Rating
0.0
(0)
Developer
Ibnu Adzim
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Scrapes flats and houses for sale or rent from Bien'ici — France's #3 real estate portal (after SeLoger and Leboncoin Immo), and one of the few in this portfolio where a single search call returns everything: no separate detail-page fetch exists or is needed.
Public data only. No login, no cookies, no browser.
What you get
Two record types share one dataset, told apart by recordType.
PROPERTY — one row per listing
Every field Bien'ici's own search response carries, passed through
verbatim: price, surface area, rooms, bedrooms, floor, energy/greenhouse-gas
classification, full description, the complete photo set, and the
seller/agency name — all in one row, no includePropertyDetails toggle
because there is nothing more to fetch.
SEARCH_SUMMARY — one row per location searched
Upstream's own match total, how many rows this run returned, and whether it
hit Bien'ici's from/size result-window ceiling.
Input
| Field | What it does |
|---|---|
| Buy / rent | which side of the market |
| Property type | flat or house |
| Locations | free text resolved through Bien'ici's own geocoder: Paris, Paris 15e, 75015, Lyon… one search per entry |
| Price / bedrooms | min–max filters, verified to actually shift the match count |
| Max properties per location | 0 = unlimited (up to Bien'ici's own ~2,400-row ceiling) |
| Results per request | page size, default 24 |
Example
{"transactionType": "buy","propertyType": "flat","locations": ["Paris 11e", "Paris 20e"],"minPrice": 300000,"maxPrice": 600000,"minBedrooms": 2,"maxItems": 500}
Two things this actor is honest about
1. An unrecognised transactionType is a silent-widen trap upstream, so
this actor refuses it instead. Bien'ici does not error on a bad value —
it answers 200 with total equal to buy-count plus rent-count, quietly
mixing both transaction types into one result set. Confirmed live
(buy=7,591 + rent=1,234 ≈ bogus-value total of 8,826). Only buy/rent are
accepted; anything else is refused before the first request rather than
risk a dataset that looks filtered but isn't.
2. Bien'ici stops around 2,400–2,500 rows per query, with an honest
error. Past that, realEstateAds.json answers HTTP 400
"Too many ads requested" — not a silent empty page, not a clamp to the
last page. A run that hits it gets resultCapReached: true and a log line
telling you to narrow by price/bedrooms or search a smaller area:
WARN buy/flat/paris: upstream reports 7592 matches but Bien'ici's realEstateAds.jsonrefuses from > 2400 ('Too many ads requested') -- narrow by price/bedroomsor search a smaller area to reach the rest.
That is exactly why Locations is a list — several narrow searches return far more data than one broad one.
Notes on reliability
- No WAF challenge. Serves data cold on every TLS profile tested. The whole HTML site is a client-side SPA (every URL serves the same ~15KB shell) — the two JSON endpoints this actor calls are the same unauthenticated, unthrottled ones the SPA itself uses.
- Runs unattended. No human-captured session, no expiring token.
- Unknown locations fail cleanly as
not_found— the geocoder answers an empty array, never a fallback to a nationwide search. minSurfaceis not offered. Tried during recon and confirmed silently ignored by the API (match count did not move) — not shipped as a broken-looking option.- Residential proxy by default on cloud runs.
Output envelope
Every record carries _input, _source and _scrapedAt. Upstream field
names pass through verbatim — no renaming, no nested propertyDetails
object (there is nothing to nest; the search row IS the full record).
_source is S1-json-search on every row.
Note propertyType/transactionType on a property row are upstream's own
fields; the location/filters you searched with are propertyTypeQueried/
transactionTypeQueried/locationQueried instead, so the two never
collide.
propertyUrl is constructed from the listing id
(https://www.bienici.com/annonce/<id>) as a convenience human link — it
resolves to the same client-side app shell as every other Bien'ici URL, so
fetching it yourself will not return more data than this actor already gives
you.
See CRAWLING_METHOD.md for the full reverse-engineering trail, including the geocoder discovery and the pagination-ceiling bisection.