DEV Community

Laurent HALBRUN
Laurent HALBRUN

Posted on

Scraping Leboncoin & SeLoger past DataDome — what actually worked (and the honest cost)

If you've tried to scrape Leboncoin or SeLoger (two of France's biggest sites), you've met DataDome — and probably lost. Here's what I found after actually shipping it, including the parts nobody says out loud.

The wall: DataDome guards everything

First myth to kill: there's no clever back door. I tested the obvious one — Leboncoin's internal mobile API (api.leboncoin.fr/finder/search), the JSON endpoint the app uses. From a residential IP, with and without an app key:

HTTP 403  {"url":"https://geo.captcha-delivery.com/captcha/?..."}
Enter fullscreen mode Exit fullscreen mode

DataDome sits in front of the web pages and the internal API. A plain residential IP + stealth browser passes lighter anti-bot (Google Maps, Amazon, Pages Jaunes) but not DataDome's hard mode. Don't waste a week on undetected-chromedriver here.

What actually works: solver-backed residential unlocker

The only reliable path is a service that (a) provides a residential IP and (b) solves the DataDome challenge. I routed the request through one, asking for a French residential proxy + JS render + antibot, and got a clean 200 with the full page:

HTTP 200 | 1.05 MB | not blocked | __NEXT_DATA__ present
Enter fullscreen mode Exit fullscreen mode

The honest tradeoff: it's not free. A premium+antibot+JS-render request burns real credits (~$0.005–0.02 each). If someone tells you DataDome is beatable for free at scale, they're selling you a cookie that expires in a few hours.

Don't parse the DOM — read the embedded JSON

Once you're through, both sites hand you structured data if you know where to look:

  • Leboncoin is a Next.js app: the ads are in __NEXT_DATA__ at props.pageProps.searchData.ads — each with list_id, subject, price, location, owner.type (private/pro), images, attributes. No brittle CSS selectors.
const m = html.match(/<script id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/);
const ads = JSON.parse(m[1]).props.pageProps.searchData.ads;
Enter fullscreen mode Exit fullscreen mode
  • SeLoger renders cards you can read with data-testid (sl.explore.card-container, sl.explore-card-price). One gotcha that cost me a 422: SeLoger's inseeCodes param zero-pads the commune to 4 digits — INSEE 33063 (Bordeaux) becomes 330063. Miss that and you get "could not get content".

The result

Clean, structured listings from both:

Leboncoin "iphone" Bordeaux → 3976 results
  60 € | iPhone 11 | private | 2026-07-23
 130 € | iPhone 11 - Garantie | pro

SeLoger Bordeaux → median 5657 €/m²
  Appartement 4p 163m² | 1 370 000 € | 8422 €/m²
Enter fullscreen mode Exit fullscreen mode

I wrapped both as pay-per-result actors so you don't have to manage the unlocker, cookies or the SeLoger code quirk:

Happy to answer questions on the DataDome specifics in the comments.

Top comments (0)