Leboncoin Listing Scraper: France Classifieds Data avatar

Leboncoin Listing Scraper: France Classifieds Data

Pricing

from $1.52 / 1,000 listing returneds

Go to Apify Store
Leboncoin Listing Scraper: France Classifieds Data

Leboncoin Listing Scraper: France Classifieds Data

leboncoin scraper by keyword: run a search on leboncoin.fr, France's largest classifieds marketplace, for one row per listing. Monitoring: new listings, price drops and gone ones, a sold items tracker with sell-through rate. No login required. Pay per listing returned; empty searches are free.

Pricing

from $1.52 / 1,000 listing returneds

Rating

0.0

(0)

Developer

Adriana Voss

Adriana Voss

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

4 days ago

Last modified

Share

Leboncoin Listing Lookup: leboncoin scraper from $2/1k

You type a search the way you'd type it into leboncoin's own search box — "iphone", "peugeot 208", "canape 3 places" — and this actor runs it against leboncoin.fr's own search-results pages, France's largest classifieds marketplace, and returns one clean row per listing: title, price, category, city, seller type, and a permanent link.

Turn on monitoring and the same search becomes a leboncoin alert for new listings and price drops: scheduled daily, each run returns only what is new or cheaper since the last one. Add the sold signal and it also tells you which listings went (a leboncoin sold items tracker with a sell-through rate and the search's median price). See "Monitoring" and "Sold signal and sell-through" below.

leboncoin sits behind DataDome — a bare request gets a JS-challenge block page, not the listings. This actor requests every search through Apify's UNBLOCKER proxy, which clears it cleanly (a real, server-rendered page comes back, no headless browser needed), so you never have to think about that yourself.

Who it's for

A reseller or arbitrage buyer watching one category (phones, cars, furniture) for underpriced listings wants a live feed of what's actually posted right now, across many search terms at once, without refreshing leboncoin's own search page by hand. A market-research team tracking asking prices for a product category, or a relocation/real-estate scout scanning a city's listings, gets the same shape here: paste a list of search terms, get back a flat row per listing, and pay only for listings actually returned — a search that finds nothing costs nothing.

This actor is leboncoin's counterpart to kleinanzeigen-listing-lookup (Germany) and otomoto-listing-lookup (Poland, vehicles) in this catalogue, but shaped differently from both: those take a specific listing URL and return one row for that one ad. This one takes a search term and returns every matching listing leboncoin's own search turns up — for when you don't have specific ad URLs yet, only a category or keyword you're watching.

Why this one

  • Gets past leboncoin's DataDome wall for you. A plain request returns an HTTP 403 DataDome challenge page — confirmed directly, not assumed. This actor routes every request through Apify's UNBLOCKER proxy group, which clears it cleanly: a normal 200 response with the full, real, server-rendered search page. No headless browser is needed once past that wall, since leboncoin's own listing data is already embedded in the HTML it sends back.
  • Reads leboncoin's own structured data, not scraped text. Every field here comes from the same __NEXT_DATA__ JSON block leboncoin's own React app renders from — not regex'd out of visible page text, so prices, IDs, and location fields are exact, typed values rather than parsed strings.
  • Works across every leboncoin category, not just one. Electronics, vehicles, real estate, jobs, general goods — leboncoin's per-listing attributes vary by category (phone model and storage for a handset, mileage and fuel type for a car), and this actor carries all of them through in a generic attributes object instead of hardcoding one category's fields and dropping the rest.
  • Monitoring built in: new listings, price drops, and a sold signal. Tick one box and a scheduled search returns only listings that are new or cheaper since its last run, read newest first. Opt in to "Alert on gone listings" and it also returns the ones that disappeared, with gonePresumedSold, daysListed, the watchlist's sellThroughRate and the search's medianPriceEur. Unchanged listings are never billed.
  • Never charged for a miss, or for a listing outside your price range. A search that finds nothing, or where every result falls outside your minPrice/maxPrice filter, still gets a row explaining what happened — and costs nothing. See "Pricing" below for the found-row rate.
  • Promoted "À la une" and top-list ads are excluded by default, so you never pay for one. These are leboncoin's own paid placements, not search matches — a "velo" search once put two unrelated property rentals in the first two slots. Turn on includePromoted if you want the page exactly as leboncoin renders it. See "Pricing" below.
  • Honest about what wasn't verified. Publish dates are passed through exactly as leboncoin's own API sends them (no timezone marker in the source, so none is invented here — see FAQ), and this actor fetches one search page per query rather than claiming to page through every result; see "vs. alternatives" for what that trade-off costs you.

What you get

One row per listing by default. (Turn off "One row per listing" in the Input tab to get one row per search instead, with the whole listing list nested in listings.) Every row carries these fields:

FieldType / formatDescription
querytextThe search term you passed in, unchanged.
foundbooleantrue if the search returned at least one listing. false rows are never charged.
statustextOK, NOT_FOUND (no listings for that search), BAD_FORMAT (a blank line), or BLOCKED.
searchQuerytextThe search text actually sent to leboncoin.
listingCountnumberHow many listings this search returned after your price filters and (by default) promoted-ad exclusion — this is exactly what you're charged for.
totalAvailablenumberleboncoin's own total-match count for this search.
truncatedbooleantrue if more listings were available than this actor's one-page fetch returned.
listingsarrayThe full listing list. Present in every row; it's what gets expanded into separate rows in "one row per listing" mode.
listingIdtextleboncoin's own numeric listing ID.
titletextListing title.
descriptiontextListing description, truncated to 500 characters.
pricenumberPrice in EUR, or null for a free/exchange/price-on-request listing.
categorytextleboncoin's own category name, e.g. "Telephones & Objets connectes".
city / region / department / zipcodetextSeller's stated location, as leboncoin displays it.
sellerTypetextpro or private, from leboncoin's own listing owner data.
sellerNametextSeller or shop name.
publishedAttextleboncoin's own first-publication timestamp, verbatim (see FAQ on why this isn't reformatted).
urllinkPermanent link to the listing on leboncoin.fr.
imagelinkThumbnail image URL.
imageCountnumberHow many photos the listing has.
isPromotedbooleantrue for a paid placement: an "À la une" ad injected at the top, or a top-list ad. These are dropped by default (see Pricing) and only ever appear when includePromoted is on, so in practice every row you actually get has this false.
attributesobjectEvery category-specific attribute leboncoin shows on the listing (phone model, vehicle mileage, property size, etc.) as a flat key/value object — see "Why this one".
scrapedAtdate (ISO)When this actor fetched the row.

With monitoring on (deltaMode, or a named watchlist in deltaName), rows also carry these. They are present but null on a plain run, so the columns never shift:

FieldType / formatDescription
changeTypetextnew, price-drop, gone (delisted since the last run, see "Sold signal"), or seen (named watchlist with monitoring off).
isNewbooleantrue when this watchlist has never returned the listing before.
previousPrice / priceDropPctnumberOn price-drop rows: the price last seen and how much cheaper it is now, in %.
firstSeenAt / lastSeenAt / daysListeddate / numberWhen the watchlist first and last saw the listing, and the days between.
lastPrice / gonePresumedSoldnumber / booleanOn gone rows only: the last price seen, and whether it looks like a sale (see "Sold signal").
newCount / priceDropCount / goneCountnumberPer search: new listings, price drops, and listings that went gone this run (gone is counted even with the gone alert off).
monitorStatustextNEW_ROWS, NO_NEW_ROWS (a quiet run: one summary row, no listings billed), or SEEDED (first run with "Seed silently" on).
sellThroughRatenumberListings gone / listings ever tracked by this watchlist, 0-1, over its lifetime.
medianPriceEurnumberMedian price of every listing on the page within your price filters, not just the rows returned.

A search that returns no listings comes back as a single found: false row with a status/message explaining why, and is never charged.

Pricing

  • Listing returned: $2 per 1,000 listings

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (found:false) are never charged.

Pay-per-event, per listing returned:

  • Listing returned: $2 per 1,000 listings, less on paid Apify plans.

Plus a $0.00005 start fee per run. Misses (found: false) are never charged.

With monitoring on, only the rows actually returned are billed: new listings, price drops, and (only if you turn on "Alert on gone listings") gone listings. Gone listings are ordinary listing rows at the same price; there is no separate event, and a gone row never makes a request, so it adds no proxy cost. A quiet run bills no listings: it returns one summary row with monitorStatus: NO_NEW_ROWS and costs only the start fee.

You're charged per listing returned, not per search — a search that returns 30 listings costs thirty, a search that returns none costs nothing, and a blank line costs nothing.

Promoted ads are excluded and never billed by default. leboncoin's paid "À la une" and top-list placements are sold to advertisers, not matched to your search text — a "velo" search once returned two unrelated property rentals in the first two slots. This actor drops them before maxListingsPerQuery is applied and before billing, so you never pay for one. Turn on includePromoted to get the page exactly as leboncoin renders it: promoted ads are then returned and billed as ordinary listing rows, same price, no extra event.

Getting past leboncoin's DataDome wall needs Apify's UNBLOCKER proxy group, already configured by default — there's nothing for you to set up, but it is a real, metered cost on this actor's own side, which is reflected in the price: this is higher per row than a plain-HTTP classifieds actor like kleinanzeigen-listing-lookup, which needs no proxy at all.

1,000 listings through this actor: ~$2 if every search finds results, less on a paid Apify plan. Because you pay per listing, "Most listings to return per search" is your budget control: leave it at 35 and a ten-search run costs at most 350 listings' worth.

How to use

  1. In the Apify Console. Open the actor page and click Start — the searches field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
  2. Via the API. Call it directly with a POST request — no Console needed once you have an API token:
    curl "https://api.apify.com/v2/acts/accountable_eel~leboncoin-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"searches":["iphone"]}'
  3. On a schedule. Save this actor as an Apify Task with the input you want, then add a Schedule (hourly, daily, weekly) so it runs on its own — no server of your own required.

Paste one search per line:

iphone
peugeot 208
appartement 3 pieces paris

Narrow the results — applied here, to the listings after they arrive:

InputWhat it does
minPriceDrop listings priced below this (EUR).
maxPriceDrop listings priced above this (EUR).
maxListingsPerQueryMost listings to return per search. Default 35 — leboncoin's own per-page count.
includePromotedOff by default: paid "À la une" / top-list ads are dropped and never billed. Turn on to get them back, billed like any other listing.

Input

{
"searches": [
"iphone"
]
}

One search per line — any keywords, e.g. "iphone 14", "peugeot 208", "canape 3 places". No login required. Accepted formats: iphone, peugeot 208, appartement 3 pieces paris.

Output

queryfoundstatussearchQuerylistingCounttotalAvailabletruncatednewCountpriceDropCountmonitorStatusgoneCountsellThroughRatemedianPriceEurlistingslistingIdtitlepricecategorycityregionsellerTypepublishedAtlistedAtlistingStatusurlimageUrlisPromotedisNewchangeTypefirstSeenAtlastSeenAtdaysListedscrapedAt
iphonefalseREQUEST_FAILED<gone since last run (delisted, most often sold)><sell-through rate, watchlist lifetime (0-1)><median price of the search, eur><all listings found (full list)> <price (eur)><seller type (pro/private)><listed at (standard alias of published)><availability (standard: available/reserved/sold)><image (standard alias of the existing image url)><promoted ad (à la une / top list)><is this listing new?><change (new / price-drop / gone / seen)><days listed (first to last seen)>2026-09-11T00:39:48.379Z

A miss comes back as a row with "found": false and is never charged.

How to filter

Every filter below is optional and off by default — leave it out and you get exactly the same rows as before. minPrice/maxPrice/includeKeywords/excludeKeywords already existed (the latter two match anywhere in the row — title, description, category, etc. — not just the title). This section adds cities and sortBy. A filtered-out listing is dropped before it's billed — you never pay for a row you didn't get.

1. A named city only, cheapest first:

{
"searches": ["iphone"],
"cities": ["bordeaux"],
"sortBy": "price_asc"
}

cities is a case-insensitive "contains" match against the listing's own city — no extra request. sortBy sorts the listings this run actually fetched, after filtering; leave it empty to keep leboncoin's own page order (unchanged default).

2. Exclude a keyword from the title:

{
"searches": ["vélo"],
"excludeKeywords": ["cassé", "pour pièces"]
}

3. Title must contain a phrase:

{
"searches": ["iphone"],
"includeKeywords": ["pro max"]
}

There is no conditions input: leboncoin's condition data lives inside a per-category attributes object that varies by category (phones, vehicles, real estate, ...), not as a standalone column — lifting it correctly is a separate, per-category job the standard's rule 2 keeps out of this batch. There is no availability input either: leboncoin's own feed never shows a sold listing, so with monitoring on, gonePresumedSold is the only sold signal — a real availability filter would just be a delta inference dressed up as source data.

Monitoring: new listings and price drops

The recipe for a leboncoin alert on new listings:

  1. Put your searches in searches and set minPrice / maxPrice if you want a price band.
  2. Turn on Only return listings that are new, or cheaper, since the last run (deltaMode). The search is then read newest first (leboncoin's "Plus récentes" sort), so the page holds the latest listings rather than the most relevant ones.
  3. Optionally turn on Seed silently so the first run remembers today's listings without sending all 35 to your webhook.
  4. Save it as a Task and add a Schedule: daily is the usual cadence, hourly for a busy search.
  5. Add a webhook or integration on the Task for Run succeeded (Slack, Discord, Telegram via n8n or Make, a Google Sheets append). Each run's dataset holds only the new and price-drop rows.
{
"searches": ["iphone 15 pro", "velo electrique"],
"maxPrice": 800,
"deltaMode": true,
"skipFirstRun": true,
"alertOnNew": true,
"alertOnPriceDrop": true,
"minPriceDropPct": 10
}

How it decides:

  • New means this watchlist has never returned that leboncoin listing ID before.
  • Price drop means the price is at least minPriceDropPct (default 5%) below the price last seen. The remembered price updates every run, so a second cut is measured from the latest price.
  • Unchanged listings are removed before you are billed. A run with nothing new returns one summary row with monitorStatus: NO_NEW_ROWS and listingCount: 0, and costs only the start fee.
  • Each watchlist remembers up to 5,000 listings per search, oldest forgotten first, and forgets any listing it has not seen for 30 days. The memory lives in a named key-value store in your own Apify account (leboncoin-listing-lookup-delta), so it survives between scheduled runs.
  • The watchlist name is derived from your price filters, so two schedules with different filters never share a memory. Set deltaName to choose your own.

The one-page limit. Each run reads one search page: the newest 35 listings. If more than 35 new listings appear between two runs, the ones in between are never seen; schedule busy searches more often, or narrow them (a model, a price band). A price drop on a listing that has already slid off the first page is not seen either. maxListingsPerQuery limits what you get back, not what the run reads: all 35 are still used to tell "still for sale" from "gone".

Sold signal and sell-through

Turn on Alert on gone listings (alertOnGone) next to deltaMode and a watchlist also returns the listings it saw on its previous run that have since disappeared from the search.

What "gone" means, honestly. leboncoin does not show sold ads or confirm a sale on its public search pages; a sold ad simply stops appearing. So does an ad the seller deleted, one that expired, or one edited so it no longer matches your search (a price raised above your maxPrice is recognised and not counted). gone means delisted: most often sold, sometimes withdrawn. Treat it as a strong signal, not a receipt.

  • gonePresumedSold: true when the listing's last price was at or under the search's median price and it was seen on at least 2 runs in a row. A fairly priced listing that stayed up and then vanished is the pattern of a sale; one seen once, or priced above the market, is only gone.
  • firstSeenAt, lastSeenAt, daysListed, lastPrice tell you how long it was up (as far as your runs saw it) and what it was asking.
  • sellThroughRate is listings gone / listings ever tracked over the lifetime of the watchlist. It is on every row and on the summary row of a quiet run, and it counts gone listings even when alertOnGone is off.
  • medianPriceEur is the median price of every listing on the page within your price filters, not just the rows returned.

How a run decides a listing is gone, not just out of view. One page shows 35 listings, and a listing pushed off it by newer ones is still for sale. A run calls a listing gone in only two cases:

  1. The search is small enough to fit on one page (leboncoin reports fewer than 35 matches and they all came back). Then any remembered listing that is missing is gone.
  2. The page is verified newest first. The actor checks on every run that leboncoin actually applied the date sort and that the listings really are in date order; it never assumes it. It then remembers each listing's position, counts the new arrivals above it, and calls it gone only if it should still sit well inside the page: at least 2 places above the bottom, and with a listing date newer than the listings near the bottom, so an ad bumped back to the top can't make another look gone.

Anything else gets no gone check: a watchlist's first run, a page that comes back empty or blocked, or a page that is not verifiably in date order. Paid placements ("À la une" and top-list ads) are never called gone, because their place on the page is bought — and, since they're excluded by default (see Pricing), a watchlist never remembers one in the first place unless includePromoted is on. On any doubt the answer is "still for sale". On a busy search where more than about 30 new listings arrive between two runs, the old page has slid off entirely and no gone listing can be seen: schedule more often or narrow the search.

A gone listing is reported once. If it shows up again later, it is taken back off the sell-through count. Gone rows are billed as ordinary listing rows, only when alertOnGone is on, and are built from what the watchlist already remembers, so they make no extra request.

{
"searches": ["iphone 13", "nintendo switch oled"],
"maxPrice": 600,
"deltaMode": true,
"deltaName": "resale-sold-signal",
"alertOnNew": false,
"alertOnPriceDrop": false,
"alertOnGone": true
}

Scheduled daily, that returns only the listings that went, with gonePresumedSold, daysListed and the running sellThroughRate: a leboncoin sold items tracker in a Google Sheet.

Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

curl "https://api.apify.com/v2/acts/accountable_eel~leboncoin-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
-X POST \
-H "Content-Type: application/json" \
-d '{"searches":["iphone"]}'

n8n. Add an HTTP Request node: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~leboncoin-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body Content Type JSON, JSON Body {"searches":["iphone"]} (swap in an expression from an earlier node for a real value).

Clay. Add an "HTTP API" column: Method POST, URL https://api.apify.com/v2/acts/accountable_eel~leboncoin-listing-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>, Body {"searches":["{{search}}"]}, mapping the row's search into the searches array.

MCP. In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Leboncoin Scraper | Apify" — the agent will find and run this actor.

Tips

  • Run "Try it first" on a handful of searches you already know return results for, to sanity-check the shape before spending on a full list.
  • For alerts, schedule the Task often enough that fewer than about 30 new listings appear between two runs; otherwise listings in between are missed and no sold signal can be seen.
  • Keep maxListingsPerQuery at or below 35 (leboncoin's own per-page count) — this actor fetches one search page per query, so raising it above 35 has no effect until a future version adds paging.
  • Use minPrice/maxPrice to cut noise from a broad category search before you pay for rows you don't want, rather than filtering client-side after the fact.
  • attributes varies by category — inspect a few real rows for your search term before building a downstream schema around specific attribute keys, since a phone search and a car search return different keys.
  • sellerType lets you split professional listings (shops, resellers) from private-party ones if you only want one or the other.

vs. alternatives

What it costsWhat you getTrade-off
This actor (leboncoin-listing-lookup)$0.002 per listing returned (less on paid Apify plans), $0.00005 actor start, nothing for a search that finds nothingOne row per leboncoin listing — title, price, category, city, seller type, and link — filtered by min/max price; monitoring mode for new listings, price drops and a sold signal with sell-through rateOne search page per query (up to 35 listings); no paging past that yet. Publish timestamps are passed through verbatim, without a timezone marker (see FAQ).
fatihtahta/leboncoin-fr-scraperPay-per-result on Apify (81 u30, rated 3.65 on 8 reviews)Also scrapes leboncoin search resultsA modest review base at a middling rating. This actor prices per listing, reads leboncoin's own structured JSON rather than parsed HTML, and carries every category's attributes through generically.
Doing it yourselfYour time + a DataDome-clearing proxy, tracking leboncoin's own __NEXT_DATA__ shape as it changes, and handling category-specific fields by handThe same dataThe DataDome-clearing proxy wiring and the generic attribute handling this actor already does are the maintenance burden it absorbs.

Prices for third-party tools are their published figures as of September 2026 and are not tracked here — check the vendor before relying on the comparison.

FAQ

Why is a row empty, or why does found say false? Either the input line is blank (status: BAD_FORMAT), leboncoin/DataDome blocked the request (status: BLOCKED, HTTP 403/429/503), the request failed after retries (status: REQUEST_FAILED), or the search genuinely has no matching listings right now (status: NOT_FOUND). Check the message column for the specific reason. None of these are billed.

Am I charged for a miss? No. You're only charged for listings actually returned. A blocked search, a search with zero matches, or a search where every result falls outside your price filter all produce a row (unless you turn on "Hide rows with no result") and none of them cost anything.

Why is publishedAt not a normal ISO timestamp? leboncoin's own API returns it as a plain "YYYY-MM-DD HH:MM:SS" string with no timezone marker. Rather than guess a timezone (leboncoin doesn't say whether it's UTC or Europe/Paris local time) and fabricate a false-precision ISO instant, this actor passes the value through exactly as leboncoin sends it.

Does this actor page through more than one screen of results? Not yet. Each search fetches leboncoin's first results page (up to 35 listings) through UNBLOCKER. totalAvailable still reports leboncoin's true match count so you know how many more exist; truncated is true whenever there are more.

Does this handle DataDome's block on leboncoin? Yes, automatically. Every request goes through Apify's UNBLOCKER proxy group by default — there's nothing for you to configure. If leboncoin still re-challenges a session, that shows up as a BLOCKED status on the affected search rather than a silent failure.

Do I need to configure proxies? No. UNBLOCKER is the default proxy group for this actor specifically, because a plain request to leboncoin is blocked by DataDome. You don't need to change anything to use it.

Can I get a leboncoin alert for new listings? Yes. Turn on deltaMode, save the input as an Apify Task and schedule it (daily is typical). Each run returns only listings that are new or dropped in price since the previous run of that watchlist, read newest first; a run with nothing new bills no listings. See "Monitoring".

Does it find sold listings? Not directly: leboncoin does not publish sales. With deltaMode and alertOnGone on, it returns listings that disappeared since the last run (changeType: gone), flags the likely sales (gonePresumedSold) and keeps a running sellThroughRate. Gone means delisted: most often sold, sometimes withdrawn. See "Sold signal and sell-through" for how it avoids calling a listing gone when it has only slid off the page.

Does this return leboncoin's paid "À la une" ads? Not by default. Those slots are sold to advertisers and are often unrelated to your search text, so this actor drops them before applying maxListingsPerQuery and before billing — you never pay for one. Turn on includePromoted if you want the page exactly as leboncoin renders it, promoted ads included and billed like any other listing.

Can an AI agent call this directly? Yes. It's registered on the Apify MCP server — an agent in Claude, Cursor, or another MCP client can find and run it by name ("Leboncoin Listing Lookup | Apify"), or you can call the REST endpoint shown above from any script or workflow tool.