2dehands & 2ememain – Belgium Classifieds & Seller Reviews
Pricing
from $1.25 / 1,000 results
2dehands & 2ememain – Belgium Classifieds & Seller Reviews
Scrape classifieds from 2dehands.be and 2ememain.be by keyword, filters, or URLs. Returns title, price, location, images, seller details, category, and full tag-based seller reviews. Supports sort, category, price filters, listing URLs, and search URLs.
Pricing
from $1.25 / 1,000 results
Rating
3.0
(2)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
8
Total users
0
Monthly active users
1.6 hours
Issues response
8 hours ago
Last modified
Categories
Share
2dehands & 2ememain Scraper: Listings, Prices, Sellers and Reviews from Belgium's Classifieds
2dehands & 2ememain Scraper turns Belgium's two largest general classifieds marketplaces into structured data. 2dehands.be and 2ememain.be share one catalogue (the same listings in Dutch and French), so one actor covers both. Search by keyword with price, sort and category filters, or paste listing and search links directly. Get normalized listing records: id, title, description, price and price type, full location, images, seller and, when you turn on full detail, view and favorite counts, item attributes, shipping and bidding info, plus the seller's full tag-based reviews. Export to JSON, CSV or Excel, or pull the results straight into your app through the API.
Why This Scraper?
- Two ways in. Search by keyword with price, sort and category filters, or paste listing and search links directly. No need to build URLs by hand.
- One catalogue, two languages. Covers 2dehands.be (Dutch) and 2ememain.be (French), the same Belgian marketplace under both names, from a single run.
- Optional full detail. Turn on listing detail to add view and favorite counts, full category, item attributes, vehicle specs, shipping and bidding info, and seller profile fields.
- Full tag-based reviews. Seller feedback on these sites is structured, not free text. Every score, characteristic tag, reviewed item and review direction is captured in full.
- Exact price filtering. Minimum and maximum price bounds are applied to each listing's real price, not an approximate facet, so filtered results are always exact.
- Built for monitoring. Incremental mode tracks a saved search over time and returns only what changed.
- Resume large pulls. Continue an interrupted run from its previous dataset without paying twice for listings already collected there.
Use Cases
- Resale and pricing research: track asking prices, price types and category supply across Belgium over time.
- Marketplace intelligence: benchmark seller activity, verification status and buyer feedback for competitor or vendor research.
- Lead generation: surface active sellers and dealers with contact and profile context for prospecting.
- Data engineering: feed normalized listing records into a warehouse, dashboard or alerting pipeline on a schedule.
- Category and vehicle tracking: monitor a specific model, brand or category and get notified only when something new appears or a price changes.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
type | "listing" |
itemId | "2330775046" |
title | "Sample bicycle, well maintained" |
url | "https://www.2dehands.be/v/fietsen/mountainbikes/m2330775046-sample-bicycle" |
price / priceCents | 150 / 15000 |
priceType | "FIXED" (also RESERVED, MIN_BID, FAST_BID, ON_REQUEST, FREE, EXCHANGE, SEE_DESCRIPTION) |
city / countryName | "Gent" / "Belgium" |
latitude / longitude | 51.05 / 3.72 |
date | "2025-05-01T10:00:00Z" |
imageUrl / imageUrls | the first image, then the full list of image URLs |
sellerId / sellerName | "u-000111" / "sample_seller" |
sellerVerified | true |
categoryId | 2100 |
traits / verticals | listing flags and marketplace segment tags, straight from the source |
sourceSite | "2dehands.be" (or "2ememain.be") |
scrapedAt | "2025-05-01T10:05:00Z" |
changeType | "NEW" (incremental mode only; also UPDATED, UNCHANGED, REAPPEARED, EXPIRED) |
changedFields | ["price"] (incremental mode only) |
firstSeenAt / lastSeenAt | incremental mode only, first and most recent time this listing was seen |
viewCount / favoritedCount | 128 / 4 (populated when Fetch full listing details is on) |
sellerRating / sellerReviewCount | 4.8 / 23 (populated when Fetch full listing details is on) |
When Fetch full listing details is on, each record also gets categoryName, categoryFullName, categoryParentName, attributes[] (plus each attribute flattened to its own attr_<key>), carDetails for vehicle listings, shippingDeliveryType, biddingEnabled and bidCount, adType, buyItNowEnabled, isReserved, highlights[], detailImageUrls[], and seller profile fields (sellerType, sellerActiveYears, sellerProfileUrl, sellerContactOptions[], sellerPaymentMethod, sellerSmbVerified, sellerHasPhone, sellerResponseRate, sellerResponseSpeed, sellerResponseLabel, sellerReviewSystem). When Include seller reviews is also on and the seller actually has reviews, the record additionally carries sellerReviewsSummary (average score, rating distribution, positive and negative characteristic tags) and a reviews[] array, each entry with reviewId, author, date, score, direction, reviewedItemTitle, reviewedItemCategory, isSeller and feedbackTags[].
How to Use
- Pick a mode:
search(keyword plus filters) orurl(paste listing or search links). - For search mode, add keywords and set the domain, price range, sort and category filters you want. For url mode, paste listing or search links; the site is taken from each link, price bounds still apply, sort and category are used only when the link doesn't already set them, and pagination continues from wherever the link points.
- Decide whether to turn on Fetch full listing details and Include seller reviews, and set Max listings (the run cap) and, if needed, Max pages.
- Click Start, then download the dataset as JSON, CSV or Excel, or read it through the API.
Keyword search with a price floor:
{"mode": "search","queries": ["bmw"],"domain": "2dehands.be","sort": "price_asc","minPrice": 2000,"maxItems": 50}
Full detail plus reviews:
{"mode": "search","queries": ["iphone"],"fetchDetails": true,"includeReviews": true,"maxReviews": 30,"maxItems": 25}
Paste links across both sites:
{"mode": "url","startUrls": ["https://www.2dehands.be/q/fiets/","https://www.2ememain.be/q/velo/"],"maxItems": 40}
Recurring monitoring of a saved search:
{"mode": "search","queries": ["fiets"],"domain": "2dehands.be","incrementalMode": true,"emitExpired": true}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/2dehands-2ememain-scraper").call(run_input={"mode": "search", "queries": ["bmw"], "maxItems": 20})for listing in client.dataset(run["defaultDatasetId"]).iterate_items():print(listing["title"], listing["price"], listing["url"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/2dehands-2ememain-scraper').call({ mode: 'search', queries: ['bmw'], maxItems: 20 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
One cap rule
Max listings is the single cap on a run. Max pages defaults high and stops once Max listings is reached. Set Max pages only when you also want a hard per-search page limit.
Resume and recurring updates
- Resume from a previous run (
resumeFromRunId) continues one specific interrupted or large pull: paste a run or dataset id and this run skips listings already collected there, so you only pay for what's new. - Incremental mode (
incrementalMode) is for scheduling this same search again and again, for example daily. The actor remembers the previous run of the same search itself, so you never paste a run id. The first run returns everything asNEW. Later runs return onlyNEWandUPDATEDlistings by default; unchanged listings are suppressed and not billed unless Emit unchanged is on.REAPPEAREDandEXPIREDare both off by default and only appear once you turn on Emit expired listings: a run that fully scans the tracked search with that setting on marks any previously-tracked listing that is no longer found asEXPIRED, and only after that has happened at least once can that same listing later come back asREAPPEARED. A capped or resumed run never marks anything expired, since it did not prove the listing is really gone.stateKeynames or deliberately shares one monitoring campaign; left empty, the actor derives a key from the mode and search settings automatically.
Send results into your apps (MCP connectors)
Optionally pipe a condensed summary of each result into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each listing, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify > Settings > Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. If the picker is empty, you haven't authorized a connector yet.
- For Notion, also set the Notion parent page field to the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | search | search (keyword plus filters) or url (paste listing or search links). |
queries | array | none (prefill: ["bmw"]) | Search keywords, one per line. Each runs a separate search. |
startUrls | array | none (prefill: ["https://www.2dehands.be/q/bmw/"]) | Listing or search links (url mode). |
domain | string | 2dehands.be | 2dehands.be (Dutch) or 2ememain.be (French). Same catalogue. |
minPrice | integer | 0 | Keep only listings priced at or above this amount in EUR. 0 keeps all. |
maxPrice | integer | 0 | Keep only listings priced at or below this amount in EUR. 0 sets no upper bound. |
sort | string | optimized | optimized, date_desc, date_asc, price_asc, or price_desc. |
categoryId | integer | 0 | Restrict to a top-level category by numeric id. 0 searches all categories. |
fetchDetails | boolean | false | Open each listing page for view/favorite counts, full category, seller profile, item attributes, shipping, bidding, and reviews. Billed as a detail-enrichment surcharge. |
includeReviews | boolean | true | When details are on, capture the seller's tag-based reviews and rating summary. |
maxReviews | integer | 20 | Cap on individual reviews captured per listing's seller. |
maxItems | integer | 20 | The run cap: global max listings returned. 0 means unlimited, clamped to 1000. |
maxPages | integer | 200 | Per-search page limit (30 listings per page). Stops at Max listings regardless. |
proxy | object | Residential, country BE | Connection settings. |
mcpConnectors | array | none | Optional: send a summary of each listing to apps you authorized under Integrations. |
notionParentPageUrl | string | none | Notion connector only: page under which items are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. Does not affect the dataset. |
resumeFromRunId | string | none | Continue one specific previous run or dataset without re-collecting its listings. |
incrementalMode | boolean | false | Turn on for recurring monitoring; return only new and changed listings on later runs. |
stateKey | string | none | Name or deliberately share one monitoring campaign's saved state. |
emitUnchanged | boolean | false | Incremental mode only. Also return (and bill) listings unchanged since the last run. |
emitExpired | boolean | false | Incremental mode only. Also return (and bill) listings no longer found, after a complete scan. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"type": "listing","itemId": "2330775046","title": "Sample bicycle, well maintained","description": "Well maintained mountain bike, 28 inch, barely used.","url": "https://www.2dehands.be/v/fietsen/mountainbikes/m2330775046-sample-bicycle","price": 150,"priceCents": 15000,"priceType": "FIXED","city": "Gent","countryName": "Belgium","latitude": 51.05,"longitude": 3.72,"date": "2025-05-01T10:00:00Z","imageUrl": "https://sample.example/img/1.jpg","imageUrls": ["https://sample.example/img/1.jpg", "https://sample.example/img/2.jpg"],"sellerId": "u-000111","sellerName": "sample_seller","sellerVerified": true,"categoryId": 2100,"categoryName": "Bikes","sourceSite": "2dehands.be","viewCount": 128,"favoritedCount": 4,"sellerRating": 4.8,"sellerReviewCount": 23,"sellerReviewsSummary": {"averageScore": 4.8,"numberOfReviews": 23,"positiveCharacteristics": ["Fast handling", "Friendly contact"],"negativeCharacteristics": []},"reviews": [{"reviewId": "r-000","author": "sample_buyer","date": "2025-05-01T10:00:00Z","score": 5,"direction": "B2S","reviewedItemTitle": "Sample bicycle","reviewedItemCategory": "Bikes","isSeller": true,"feedbackTags": [{ "text": "Fast handling", "isPositive": true },{ "text": "Friendly contact", "isPositive": true }]}],"scrapedAt": "2025-05-01T10:05:00Z"}
Plan Requirement
The default connection setting, Residential proxy group with country BE, works out of the box and gives the most reliable results for these two Belgian sites. The actor automatically retries with a fresh connection when a request is refused, and falls back further, including a direct connection, before giving up. If a run returns no results, try a fresh run or check that the connection is set to Residential country BE.
FAQ
How much does it cost?
You pay per listing returned, plus a small charge when the actor starts, plus a detail-enrichment charge per listing only when Fetch full listing details is on. The Pricing tab shows the current rates. Use Max listings to cap the cost of any run.
Is it legal to scrape 2dehands and 2ememain?
This actor collects only publicly available classifieds data. You are responsible for how you use it: follow the sites' terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Listing photos and seller-provided text may carry their own rights.
Can I get only new or changed listings on a schedule?
Yes. Schedule the actor from the Schedules tab with the same search settings and Incremental mode turned on. The first run returns everything as NEW; later runs return only NEW and UPDATED listings by default, and unchanged ones are not billed. Turn on Emit expired listings if you also want to know when a tracked listing disappears, and only after that has fired once can a listing later come back marked REAPPEARED.
What's the difference between Resume and Incremental mode?
Resume from a previous run continues one specific interrupted or large pull using a pasted run or dataset id, so this run appends only the remaining new listings. Incremental mode is for running the exact same search again and again on a schedule and getting only what changed; the actor tracks its own state and you never paste an id. Combining incremental mode with resumeFromRunId only works to bootstrap a brand-new monitoring campaign; if that search already has saved incremental state, the run fails with a clear message rather than mixing the two.
Why did my run fail instead of returning an empty dataset?
A few situations fail on purpose rather than quietly returning nothing: a resumeFromRunId that doesn't match a run or dataset this account can read, combining incremental mode with resumeFromRunId when that search already has saved state, or no usable connection could be set up at the start of the run. In those cases the run stops with a clear message so you know to fix the input rather than assume there were no listings. If individual requests are refused during the scrape, or the connection succeeds but no listings match your filters (for example, a very narrow price range), the run still finishes successfully and returns zero or fewer items with a warning suggesting you re-run or broaden the filters.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more classifieds data?
Pair this actor with these related scrapers from the same team:
| 🚗 Marktplaats.nl Scraper From $1/1K. Scrape Marktplaats.nl listings by keyword or URL. Extract title, price... | 🚗 AutoTrader UK Scraper Pull structured vehicle listings from autotrader.co.uk at scale. Search by filters or use... |
| 🏷️ OLX Global From $1/1K. Fast OLX scraper across 9 countries, including Poland, Romania, Portugal... | 🏷️ Craigslist Scraper Scrape Craigslist postings in any of 700+ cities worldwide: price, title, posting text... |
| 🏷️ Finn.no From $0.8/1K. Scrape active Finn.no ads via search builder or URL. Returns 30+ fields per... | 🏷️ Daangn Scraper From $1/1K. Scrape Daangn (당근) marketplace listings by keyword and region. Returns 35+... |
💬 Support & custom scrapers
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email abotapi@proton.me or message Telegram @abotapi.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.