2dehands & 2ememain Scraper
Scrape 2dehands.be and 2ememain.be as clean JSON: paste search links from either site or run several searches at once, and get only new listings on a...
Scrape 2dehands.be and 2ememain.be as clean JSON: paste search links from either site or run several searches at once, and get only new listings on a schedule. Prices, condition, specs, locations and images. GDPR-safe: no seller names or personal data.
$1.99 / 1,000 · 97.3% success over 74 public runs in the last 30 days.
Run it on Apify → Free to try - runs bill per result on your own account.
What you give it
Every field is optional unless marked; sensible defaults do the rest.
| Field | Type | What it does | Default |
|---|---|---|---|
searchQuery |
string | Free-text search. Provide this, a category, or both. | gazelle |
searchQueries |
array | Several searches in one run, one per line, e.g. gazelle and sparta. Each one uses the filters below. A listing found by two searches is delivered once. Up to 50. |
- |
startUrls |
array | Set up a search on 2dehands.be or 2ememain.be with any filters you like (category, condition, brand, price, distance...), then paste the address of the results page here, including everything after the #. Add as many as you watch, up to 50, from either site. When URLs are given, the search fields of this form are ignored (the marketplace too: each URL keeps its own site) and the run status says which ones were. Posted-date window, max items and only-new mode still apply. A listing page or another site is refused with the reason. |
- |
domain |
string | Which marketplace to scrape. | 2dehands.be |
categoryId |
integer | Numeric marketplace category ID to scope the crawl. Every result includes its categoryId, so run a broad search first to discover the IDs you need. Optional. |
- |
listingIds |
array | Fetch these exact listings instead of searching, e.g. ["m2107483664"] - the id is in every listing URL and every dataset item. Useful to re-check price or availability of listings you found earlier. When set, all search filters are ignored. | [] |
postcode |
string | Belgian postcode to search around, e.g. "2000". Combine with radiusKm to narrow the search area. The distance itself is not returned as a field. | - |
radiusKm |
integer | Only return listings within this many km of the postcode. Requires postcode. | - |
conditions |
array | Only return listings in these conditions. Empty = all conditions. Without a category ID the search runs category by category, because the marketplace applies this filter only inside a category. | [] |
delivery |
string | Only listings offering pickup, or only listings that ship. "any" = no filter. | any |
buyItNowOnly |
boolean | Only listings with the "Direct Kopen" (buy it now) option. | False |
priceMinEur |
integer | Only return listings priced at or above this amount. Optional. | - |
priceMaxEur |
integer | Only return listings priced at or below this amount. Optional. | - |
sortBy |
string | Order of the results. Newest first is the default and the right choice for monitoring. | date |
postedAfter |
string | Only keep listings posted on or after this date. Listings whose posting date cannot be read are excluded when a window is set. Format: YYYY-MM-DD (for example 2026-09-08) or a full ISO timestamp such as 2026-09-08T14:30:00Z. Slash dates like 08/09/2026 are refused on purpose: they are read as September in Europe and August in the United States, and this actor will not guess which you meant. |
- |
postedBefore |
string | Only keep listings posted on or before this date. Format: YYYY-MM-DD (for example 2026-09-08) or a full ISO timestamp such as 2026-09-08T14:30:00Z. Slash dates like 08/09/2026 are refused on purpose: they are read as September in Europe and August in the United States, and this actor will not guess which you meant. |
- |
maxItems |
integer | Stop after this many listings, across all searches of the run. With several searches, each gets an equal share of what is left. | 1000 |
onlyNewListings |
boolean | For scheduled runs: return only listings this search has not delivered before. The first run returns the usual results and remembers them; later runs skip them, and skipped listings are not charged. The memory is kept per search (and per task) in a key-value store named tweedehands-scraper-seen-listings in your own account, for 90 days. Changing a search's filters starts a fresh memory. |
False |
resetState |
boolean | With only-new mode on: forget what the searches of this run delivered before, return the full results once and remember again. | False |
scrapeDetails |
boolean | The search returns only a ~200-character description snippet. Turn this on to read each listing's own page for the seller's full description. This makes one extra request per listing, so a run is slower and costs one more page fetch each. The seller/trader block is never read; only the description text reaches the output. Leave off for a fast search-only sweep. | False |
reportFailures |
boolean | When a run fails, send the developer the actor name and build, an error category, the error message (truncated) and a one-way hash of your input - so a bug can be fixed without anyone asking you to reproduce it. Your input itself, your results and your identity are never sent, and successful runs send nothing at all. Switch this off and the actor behaves exactly the same. | True |
What you get back
One JSON object per result, with exactly these fields - no seller identities, no contact data, ever.
A real result row from a recent run:
{
"attributes": {
"body": "Break",
"constructionYear": "2021",
"driveTrain": "Achterwielaandrijving",
"engineDisplacement": "1995 cc",
"fuel": "Diesel",
"mileage": "136500",
"model": "3 Reeks",
"numberOfCilindersCars": "4 cilinders",
"options": [
"Adaptive Cruise Control",
"Trekhaak",
"Cruise Control",
"Climate control",
"Parkeersensor",
"Bi-Xenon koplampen",
"Geheel digitaal combi-instrument",
"LED dagrijverlichting",
"Lederen bekleding",
"Sound system",
"Autonomous Driving",
"Radio",
"Lane Departure Warning",
"Metaalkleur",
"Sportpakket",
"Elektrische buitenspiegels",
"Achteruitrijcamera",
"Regensensor",
"Lane Keeping Assist",
"Bluetooth",
"Emergency brake assist",
"Android Auto",
"Sportstoelen",
"Elektrische koffer",
"Apple Carplay",
"Electronic Stability Program (ESP)",
"Start-stop-systeem",
"Inductieladen voor smartphones",
"Mistlampen",
"Bochtverlichting",
"Multifunctioneel stuurwiel",
"Lichtmetalen velgen",
"Grootlichtassistent",
"Boordcomputer",
"Koplamp volledig LED",
"Adaptieve lichten",
"Navigatiesysteem",
"Automatische klimaatregeling",
"USB",
"Airconditioning",
"Airbags",
"Night view assist",
"Elektrische stoelverstelling",
"ABS",
"Alarm",
"Elektronische parkeerrem",
"Hill-Hold Control",
"Centrale vergrendeling",
"LED verlichting"
],
"transmission": "Automaat",
"upholstery": "Leder en Stof"
},
"categoryId": 96,
"city": "Anderlecht",
"country": "België",
"description": "Vends cette magnifique bmw 318d touring il s’agit de la diesel/électrique très faible en consommation et taxe. Le véhicule est en parfait ét...",
"imageUrls": [
"//images.2dehands.com/api/v1/hz-twh-pro-listing/images/24cceb3f-5c9d-4a7b-bf6e-c1c8f8f42c5f?rule=ecg_mp_eps$_82.jpg"
],
"latitude": 50.828984465588,
"listingId": "m2453915601",
"longitude": 4.2929055124851,
"postedAt": "Vandaag",
"postedDate": "2026-10-18",
"priceCents": 1790000,
"priceType": "FIXED",
"reserved": false,
"sellerType": "private",
"title": "BMW 318D Touring Diesel/Electrique",
"url": "https://www.2dehands.be/v/auto-s/bmw/m2453915601-bmw-318d-touring-diesel-electrique"
}
| Field | Type | Meaning |
|---|---|---|
listingId |
string | Marketplace listing ID, e.g. m2419606891. |
title |
string | Listing title as posted. |
description |
string | Listing description. The ~200-character search snippet by default, or the seller's full free-text description when Fetch full descriptions is on (read from the listing's own page; the seller/trader block is never read). |
priceCents |
integer | Asking price in euro cents. Absent when the listing has no price. |
priceType |
string | Marketplace price kind, e.g. FIXED or MIN_BID. |
city |
string | Municipality of the listing. |
country |
string | Country of the listing. |
latitude |
number | Marketplace-provided municipality-level coordinate, not a seller address. |
longitude |
number | Marketplace-provided municipality-level coordinate, not a seller address. |
postedAt |
string | Posting date as the marketplace displays it: a date, or a relative label like 'Vandaag' (today). |
imageUrls |
array | Protocol-relative image URLs; prepend https: to use them. |
categoryId |
integer | Numeric marketplace category ID. Reusable as the categoryId input. |
url |
string | Direct link to the listing. |
attributes |
object | Whitelisted, non-personal listing attributes that vary by category — e.g. condition and delivery on any item; constructionYear, mileage, fuel, transmission and options for cars; livingArea and numberOfRooms for property; storage and cameraResolution for phones. Present when the listing has them. |
reserved |
boolean | Whether the listing is currently marked as reserved by the marketplace. |
sellerType |
string | Whether the listing comes from a business or a private seller. The only seller-related field in the output. |
postedDate |
string | Posting date normalized to YYYY-MM-DD (from the site's display date). |
Health
97.3% success over 74 public runs in the last 30 days. Live record, failures included: status page.
Full documentation, examples and the AI-assistant prompt live on the store page.
Questions
Is scraping 2dehands and 2ememain legal?
This is not legal advice. The actor reads what any logged-out visitor sees on 2dehands.be and 2ememain.be, with no login, no CAPTCHA solving and no private endpoints, and it extracts no seller name, contact detail or profile, so the output holds no personal data. Whether your own use of the listings is allowed under the site's terms and your jurisdiction's rules is yours to assess.
Can I get only new listings?
Yes. Switch on onlyNewListings and every scheduled run delivers only the listings it has not delivered before, which is what an hourly watch needs. The first run returns the full result set as a baseline, and resetState starts the memory over.
How much do 10,000 listings cost?
$19.90, billed per delivered listing at $1.99 per 1,000. That price covers the run's platform usage, and a run that fails or returns nothing bills nothing.
Does it return seller phone numbers?
No. The only seller field is whether the seller is a business or a private person. Names, phone numbers, profiles and exact addresses are never extracted, and a test on every build fails if one ever appears, so this is not a setting that can be switched off.
What is the difference between 2dehands.be and 2ememain.be?
The same Belgian marketplace in Dutch and in French. Pick the domain whose language you want the listings in; the data shape is identical and search links from either site are accepted.
Can I limit results to listings near me?
Yes. Give a Belgian postcode and a radius in kilometres and only listings inside it come back. Each row carries the seller's stated city, never a computed distance or an exact address.
The history behind this tool
This tool answers what a source shows today. Its output also feeds a series recorded every day and kept: Used car asking prices and listing lifecycles. A run gives you the current market; the series is the part that cannot be reconstructed afterwards, because the source overwrites it.