Leboncoin Listing Scraper: France Classifieds Data
Pricing
from $1.52 / 1,000 listing returneds
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
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
4 days ago
Last modified
Categories
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
attributesvary 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 genericattributesobject 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'ssellThroughRateand the search'smedianPriceEur. 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/maxPricefilter, 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
includePromotedif 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:
| Field | Type / format | Description |
|---|---|---|
query | text | The search term you passed in, unchanged. |
found | boolean | true if the search returned at least one listing. false rows are never charged. |
status | text | OK, NOT_FOUND (no listings for that search), BAD_FORMAT (a blank line), or BLOCKED. |
searchQuery | text | The search text actually sent to leboncoin. |
listingCount | number | How many listings this search returned after your price filters and (by default) promoted-ad exclusion — this is exactly what you're charged for. |
totalAvailable | number | leboncoin's own total-match count for this search. |
truncated | boolean | true if more listings were available than this actor's one-page fetch returned. |
listings | array | The full listing list. Present in every row; it's what gets expanded into separate rows in "one row per listing" mode. |
listingId | text | leboncoin's own numeric listing ID. |
title | text | Listing title. |
description | text | Listing description, truncated to 500 characters. |
price | number | Price in EUR, or null for a free/exchange/price-on-request listing. |
category | text | leboncoin's own category name, e.g. "Telephones & Objets connectes". |
city / region / department / zipcode | text | Seller's stated location, as leboncoin displays it. |
sellerType | text | pro or private, from leboncoin's own listing owner data. |
sellerName | text | Seller or shop name. |
publishedAt | text | leboncoin's own first-publication timestamp, verbatim (see FAQ on why this isn't reformatted). |
url | link | Permanent link to the listing on leboncoin.fr. |
image | link | Thumbnail image URL. |
imageCount | number | How many photos the listing has. |
isPromoted | boolean | true 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. |
attributes | object | Every 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". |
scrapedAt | date (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:
| Field | Type / format | Description |
|---|---|---|
changeType | text | new, price-drop, gone (delisted since the last run, see "Sold signal"), or seen (named watchlist with monitoring off). |
isNew | boolean | true when this watchlist has never returned the listing before. |
previousPrice / priceDropPct | number | On price-drop rows: the price last seen and how much cheaper it is now, in %. |
firstSeenAt / lastSeenAt / daysListed | date / number | When the watchlist first and last saw the listing, and the days between. |
lastPrice / gonePresumedSold | number / boolean | On gone rows only: the last price seen, and whether it looks like a sale (see "Sold signal"). |
newCount / priceDropCount / goneCount | number | Per search: new listings, price drops, and listings that went gone this run (gone is counted even with the gone alert off). |
monitorStatus | text | NEW_ROWS, NO_NEW_ROWS (a quiet run: one summary row, no listings billed), or SEEDED (first run with "Seed silently" on). |
sellThroughRate | number | Listings gone / listings ever tracked by this watchlist, 0-1, over its lifetime. |
medianPriceEur | number | Median 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
- In the Apify Console. Open the actor page and click Start — the
searchesfield is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found. - 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"]}'
- 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:
iphonepeugeot 208appartement 3 pieces paris
Narrow the results — applied here, to the listings after they arrive:
| Input | What it does |
|---|---|
minPrice | Drop listings priced below this (EUR). |
maxPrice | Drop listings priced above this (EUR). |
maxListingsPerQuery | Most listings to return per search. Default 35 — leboncoin's own per-page count. |
includePromoted | Off 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
| query | found | status | searchQuery | listingCount | totalAvailable | truncated | newCount | priceDropCount | monitorStatus | goneCount | sellThroughRate | medianPriceEur | listings | listingId | title | price | category | city | region | sellerType | publishedAt | listedAt | listingStatus | url | imageUrl | isPromoted | isNew | changeType | firstSeenAt | lastSeenAt | daysListed | scrapedAt |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| iphone | false | REQUEST_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:
- Put your searches in
searchesand setminPrice/maxPriceif you want a price band. - 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. - Optionally turn on Seed silently so the first run remembers today's listings without sending all 35 to your webhook.
- Save it as a Task and add a Schedule: daily is the usual cadence, hourly for a busy search.
- 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_ROWSandlistingCount: 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
deltaNameto 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: truewhen 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 onlygone.firstSeenAt,lastSeenAt,daysListed,lastPricetell you how long it was up (as far as your runs saw it) and what it was asking.sellThroughRateis 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 whenalertOnGoneis off.medianPriceEuris 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:
- 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.
- 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
maxListingsPerQueryat 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/maxPriceto cut noise from a broad category search before you pay for rows you don't want, rather than filtering client-side after the fact. attributesvaries 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.sellerTypelets you split professional listings (shops, resellers) from private-party ones if you only want one or the other.
vs. alternatives
| What it costs | What you get | Trade-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 nothing | One 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 rate | One 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-scraper | Pay-per-result on Apify (81 u30, rated 3.65 on 8 reviews) | Also scrapes leboncoin search results | A 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 yourself | Your time + a DataDome-clearing proxy, tracking leboncoin's own __NEXT_DATA__ shape as it changes, and handling category-specific fields by hand | The same data | The 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.
Related actors
- Kleinanzeigen Listing Lookup — the same classifieds category, for Germany's Kleinanzeigen — takes a listing URL rather than a search term.
- OLX Listing Lookup — the same search-by-keyword shape, for Poland's OLX.pl.
- Otomoto Listing Lookup — a vehicle-specific classifieds lookup for Poland's largest car marketplace.