2dehands & 2ememain at a glance
2dehands & 2ememain is a ready-to-run Apify actor from abotapi for collecting ecommerce data. Its documented output includes item id, change type (incremental mode), title, url and price (eur), which you can export as JSON, CSV or Excel or read through the Apify API. It costs $2.00 per 1,000 results on the Apify Free plan and from $1.25 on paid plans, with smaller per-run or optional feature charges listed on Apify.
- Runs on
- Apify cloud: console, API, schedules and integrations
- Data type
- Ecommerce, Developer Tools, Lead Generation
- Pricing
- $2.00 per 1,000 results (Free plan), from $1.25 on paid plans
- Output
- Documented below · JSON, CSV, Excel
- Inputs
- 38 options
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.
What 2dehands & 2ememain can do
- 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.
Choose what to collect
These inputs let you configure 2dehands & 2ememain – Belgium Classifieds & Seller Reviews. Open the actor on Apify to enter your values and review the current options.
| Input | What it controls |
|---|---|
modestring · required | search = build a search from a keyword plus filters. url = paste one or more listing or search URLs and walk them forward. If you fill only Listing or search URLs (and no keywords, category or listing ids), they are read as url mode; with no keyword, category, URL or listing id at all, an example search for "bmw" runs and Incremental mode does not read or save state for it. |
queriesarray | Product keywords such as "bmw", "iphone", or "fiets". Each line runs a separate search. |
searchQuerystring | Single-keyword alias of Search keywords; both are combined. |
searchQueriesarray | Alias of Search keywords; both are combined. |
listingIdsarray | Fetch these exact listings (for example m2427421626) instead of searching. When filled, the keyword and filter fields are ignored. Accepts the bare id or a listing URL. |
startUrlsarray | One or more URLs (url mode). Supported: keyword search (/q/bmw/), paginated search (/p/2/ or ?currentPage=2), hash filters and sort (#sortBy:SORT_INDEX|sortOrder:DECREASING|offeredSince:Today|PriceCentsFrom:5000|PriceCentsTo:20000|currentPage:3), facet ids (#f:30 or #f:30,31; ids are category-specific, 30-32 are condition and 33-34 delivery values, and an id the category lacks is ignored by the site so the result can be broader than asked), keyword (#q:bmw) and start position (#offset:60) in the hash (baselines saved before these were supported keep ignoring them: use a new State key or Reset state to apply them), category pages (/l/auto-s/, /l/auto-s/q/bmw/, /l/auto-s/bmw/), listing pages (/v/...), seller profiles (/u/name/id/), on 2dehands.be or 2ememain.be, and /lrp/api/search links. Other links (mobile host, bare /v/) are skipped with a warning. Filters above apply to search links except postcode, condition and delivery, which do not apply to seller-profile or listing links; pagination starts at the page implied by the URL and walks forward. |
domainstring | Which Belgian site to search. Both share the same catalogue and API. |
Show 31 more inputs
| Input | What it controls |
|---|---|
minPriceinteger | Keep only listings priced at or above this amount (applied to the listing price). Leave at 0 to keep all. Narrows results. |
maxPriceinteger | Keep only listings priced at or below this amount. Leave at 0 for no upper bound. Narrows results. |
priceMinEurinteger | Alias of Minimum price; Minimum price wins when both are set. |
priceMaxEurinteger | Alias of Maximum price; Maximum price wins when both are set. |
sortstring | Order the results by relevance, newest, oldest, or price. |
sortBystring | Alias of Sort: date = newest first, relevance = best match. When set it wins over Sort. |
categoryIdinteger | Restrict the search to a top-level category by its numeric id. Leave at 0 to search all categories. |
postcodestring | 4-digit postcode (for example 2000). Keeps only listings within the radius below. An invalid postcode stops the run instead of silently returning unfiltered results. |
radiusKminteger | 1 to 300 km, only used together with a postcode. Defaults to 25 km when empty. |
conditionsarray | Keep only listings in any of these conditions. Empty keeps all. Not every category offers every condition. |
deliverystring | any, pickup (Ophalen) or shipping (Verzenden; includes listings offering both). |
buyItNowOnlyboolean | Drop bidding, swap and on-request listings and keep only listings with a fixed price. Dropped rows are never charged. |
postedAfterstring | YYYY-MM-DD (or an ISO timestamp, date part used). The site only publishes the posting day, so the window is day-precise (Europe/Brussels). Listings whose date cannot be read are kept. |
postedBeforestring | YYYY-MM-DD (or an ISO timestamp, date part used). Day-precise like Posted on or after. |
fetchDetailsboolean | Open each listing page to add view/favorite counts, full category, seller type and profile, item attributes, shipping, bidding, and the seller's tag-based reviews. Adds requests per listing (billed as a detail-enrichment surcharge). Turn off to collect only the search-card fields. |
scrapeDetailsboolean | Alias of Fetch details; either one switches it on. |
includeReviewsboolean | When details are on, capture the seller's full tag-based reviews (score, positive/negative characteristic tags, reviewed item, reviewer, date) plus a rating summary. Only fetched for sellers that have reviews. |
maxReviewsinteger | Cap on the number of individual reviews captured per listing's seller. |
maxItemsinteger | Global cap on the number of listings to return across all searches. This is THE cap - Max pages defaults to unlimited and stops at this number. 0 means unlimited (clamped to 1000). |
maxPagesinteger | Per-search page limit (30 listings per page). Leave empty for the built-in hard cap of 200 pages per search; the run stops at Max listings first. |
resumeFromRunIdstring | Paste a previous run ID or dataset ID to continue a large pull without returning or charging for listings already collected there. Use this after an interrupted run, or to keep extending one walk-all pull across separate runs. For recurring daily monitoring of the same search, use Incremental mode below instead. |
incrementalModeboolean | Turn this on for daily or recurring monitoring. The first run returns all matching listings as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED listings. Turn on "Emit unchanged" or "Emit expired" only when you also want those listings returned (and billed). State is kept separately per mode + search/URL/filter configuration; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above. |
stateKeystring | Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the mode + search/filter settings, different searches then never mix state with each other. |
emitUnchangedboolean | Off by default. Turn on to also return listings that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have, so leave it off unless you specifically want the full snapshot every run. |
emitExpiredboolean | Off by default. Turn on to also return listings that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search, not when Max listings capped it or when Resume was used. This returns, and bills, extra synthetic rows, so leave it off unless you need expiry tracking. |
onlyNewListingsboolean | Emit only listings this search has not shown before (remembered between runs). Rows skipped as already seen are never pushed or charged. Uses the same remembered state as Incremental mode. |
resetStateboolean | Clears the remembered state for this search before the run (only with Incremental mode or Only listings not seen before). |
ignoreFieldsForChangesarray | Incremental mode only. Field names (or dotted paths such as seller.name) that should NOT make a listing count as UPDATED when only they change. They stay in the output. View, favourite and bid counters are always ignored already. |
mcpConnectorsarray | Optionally send a condensed summary of each result into the apps you already use, via Model Context Protocol (MCP) connectors. The full record stays in the dataset. Leave empty to skip. Supported: Notion, Linear, Airtable, Apify. |
notionParentPageUrlstring | URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors. |
maxNotifyListingsinteger | Cap on items written to each connector per run. Does not affect the dataset. |
Fields in the output
The documented dataset includes the fields below. Availability can depend on the source page and selected mode.
itemIdItem ID (text)changeTypeChange type (incremental mode) (text)titleTitle (text)urlURL (link)pricePrice (EUR) (number)priceTypePrice type (text)cityCity (text)countryNameCountry (text)datePosted (text)imageUrlImage (image)sellerNameSeller (text)sellerIdSeller ID (text)Show 21 more fields
sellerVerifiedVerified (boolean)categoryNameCategory (text)viewCountViews (number)sellerRatingSeller rating (number)sellerReviewCountReviews (number)sourceSiteSite (text)descriptionDescription (text)changedFieldsChanged fields (incremental mode) (array)firstSeenAtFirst seen (incremental mode) (date)lastSeenAtLast seen (incremental mode) (date)sellerTypeSeller type (text)sellerResponseRateResponse rate (number)sellerResponseSpeedResponse speed (text)sellerActiveYearsActive years (number)sellerWebsiteUrlSeller website (link)sellerReviewsSummaryReviews summary (object)reviewsReviews (array)extendedAttributesExtended attributes (array)imageUrlsImages (array)latitudeLatitude (number)longitudeLongitude (number)Example output
An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.
{
"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"
}Ways to use this data
- 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.
Before you run
Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.
Questions about this actor
What data does 2dehands & 2ememain return?
The documented output for 2dehands & 2ememain includes item id, change type (incremental mode), title, url, price (eur), price type, city and country. Availability of each field depends on the source page and the selected mode.
How much does 2dehands & 2ememain cost?
It costs $2.00 per 1,000 results on the Apify Free plan and from $1.25 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.
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, a url-mode run where none of the links could be used, 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.
