# 2dehands & 2ememain New Listing Alerts (`lowlanddata/tweedehands-new-listing-alerts`) Actor

Watch one saved search on 2dehands.be or 2ememain.be and get only the NEW listings each run - postcode radius and price band included. Built for hourly schedules with email or Slack. No seller identity, ever. Quiet runs cost nothing.

- **URL**: https://apify.com/lowlanddata/tweedehands-new-listing-alerts.md
- **Developed by:** [Lowland Data](https://apify.com/lowlanddata) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.59 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 2dehands & 2ememain New Listing Alerts — hear about every new listing once

Point this actor at one saved search on **2dehands.be** or **2ememain.be** — Belgium's largest second-hand marketplaces — and each run hands you **only the listings posted since the run before**. First run: a baseline snapshot of the current matches. Every run after that: the difference, as clean structured JSON, ready for an email step, a Slack message, a spreadsheet or an AI agent. You never diff datasets, never re-read the same ad twice.

Belgian buyers already pay subscription services for this kind of alert — MarktAlert covers 2dehands explicitly — but those services end at a notification on your phone. None of them is API-first. This watch gives you the alert **and** the data behind it, in the same GDPR-clean shape as its parent, the [2dehands & 2ememain Scraper](https://apify.com/lowlanddata/tweedehands-scraper): seller identity is never collected, on any run.

### Quick start (30 seconds)

1. Type your search into **searchQuery**, exactly as you would on the site — e.g. `gazelle elektrische fiets`.
2. Click **Start**. This first run delivers the current matches and remembers them — the baseline.
3. Create an hourly Schedule in the Apify Console with the same input and attach an email or Slack integration. From then on, every notification contains news and nothing else.

Refinements when you want them: pick `2ememain.be` as the site, add a Belgian `postcode` with a `radiusKm`, set a `priceMinEur`/`priceMaxEur` band, or watch a `categoryId` instead of a query.

### What you can build with it

- **Be the first buyer, not the fifth.** Deal hunters flipping e-bikes, consoles or vintage hi-fi live and die by reaction time. An hourly watch on `searchQuery: "ps5"`, `priceMaxEur: 250` puts every fresh listing in front of you with the price already parsed — while the seller is still answering messages.
- **Wait for the one item you actually want.** Hunting a specific bike frame, a discontinued lens, an oak dresser that fits your hallway? Set the watch with your `postcode` and a 25 km radius and stop refreshing search pages. The day it appears nearby, the run says so; every other day, silence.
- **Watch in French, buy in Belgium.** Brussels and Wallonia shop on 2ememain.be — same listings, French interface. Set `domain: "2ememain.be"` and your links and labels arrive in French, no separate setup.
- **Wire alerts into anything.** Because a run's dataset holds only new listings, the downstream step is trivial: a Make/Zapier/n8n scenario, a webhook into your own scoring script, or an MCP agent that reads the news and decides what deserves your attention.

### What you get

Each new listing is one dataset item, in the exact shape the parent scraper produces — anything built on one works on the other:

```json
{
  "listingId": "m2431188402",
  "title": "Gazelle Ultimate C8 elektrische fiets",
  "description": "Weinig gebruikt, accu vorig jaar vervangen...",
  "priceCents": 129500,
  "priceType": "FIXED",
  "city": "Gent",
  "country": "België",
  "latitude": 51.05,
  "longitude": 3.72,
  "postedAt": "Vandaag",
  "imageUrls": ["//images.2dehands.com/api/v1/listing-mp-p/images/..."],
  "categoryId": 445,
  "url": "https://www.2dehands.be/v/fietsen-en-brommers/...",
  "attributes": { "condition": "Zo goed als nieuw", "delivery": "Ophalen" },
  "reserved": false,
  "sellerType": "private"
}
```

Category-specific specs ride along in `attributes` — condition and delivery on anything, `constructionYear`/`mileage`/`fuel` on cars, `livingArea` on property — whitelisted, non-personal keys only.

Field notes:

- `priceCents` is the asking price in euro cents (`129500` = €1,295); `priceType` says how to read it — `FIXED` asking price or `MIN_BID` minimum bid. Listings without a price omit it.
- `postedAt` is the date as the marketplace shows it — a date, or a label like `Vandaag` (today).
- Sellers bump listings back to the top of the newest-first order. The watch remembers what it has already delivered, so a bump does **not** re-alert — one listing, one alert.
- `latitude`/`longitude` and `city` are the marketplace's municipality-level location, never a seller address. `sellerType` (business or private) is the only seller-related field anywhere in the output.
- `imageUrls` are protocol-relative; prepend `https:`.
- The run's status message states the outcome plainly: "34 new listings since the last run" — or on the first run, "180 listings (baseline run - future runs deliver only new ones)".

### How much do 2dehands alerts cost?

$1.99 per 1,000 **new** listings delivered. You pay for news, not for checking:

- **A run that finds nothing new costs nothing beyond the actor start** — and for a well-scoped watch, most runs are that run.
- **A search surfacing ~100 new listings a week ≈ $0.85 a month**, checked hourly.
- **The one-time baseline of 200 listings ≈ $0.40.**

That price is all-inclusive: platform usage covered, datacenter proxies are all it needs, and a run finishes in seconds — an hourly schedule barely registers. Free-plan runs cap at 25 items, enough to watch the baseline-then-news mechanics work on your own search.

### Not technical? Let your AI assistant set it up

Copy this into ChatGPT, Claude or any AI assistant, fill in the one line, and follow the conversation:

```text
Help me set up the "2dehands & 2ememain New Listing Alerts" actor on Apify
(https://apify.com/lowlanddata/tweedehands-new-listing-alerts). Guide me one
step at a time.

What I want to be alerted about: [E.G. "electric bikes under 1500 EUR within
30 km of Antwerp"]

Guide me to:
1. Propose my input values: searchQuery (what I'd type in the 2dehands search
   box), domain (2dehands.be for Dutch, 2ememain.be for French — same
   listings), an optional priceMinEur/priceMaxEur band, and an optional
   Belgian postcode (e.g. 2000) plus radiusKm for listings near me.
2. Create a free Apify account (apify.com), open the actor page, paste the
   values into the Input form, and start one run — that first run is my
   baseline snapshot, not yet an alert.
3. Set up an hourly Schedule in the Apify Console with the same input, plus an
   email or Slack integration on finished runs, so new listings reach me
   without opening the site.
4. If I want a second watch (another search or another region), show me how to
   add another Schedule with its own input and a different stateStoreName so
   the two watches keep separate memories.
5. If the alerts are what I wanted, remind me at the end to leave a quick
   rating on the actor page, and to report anything broken on its Issues tab.
```

### Input

| Field                         | Description                                                                                         |
| ----------------------------- | --------------------------------------------------------------------------------------------------- |
| `searchQuery`                 | The saved search to watch, as you'd type it on the site. A query, a category, or both.              |
| `domain`                      | `2dehands.be` (Dutch, default) or `2ememain.be` (French). Both serve the same listings.             |
| `categoryId`                  | Numeric category id to watch, instead of or together with the query.                                |
| `postcode` / `radiusKm`       | Watch only around a Belgian postcode (e.g. `2000`), radius 1–300 km. The radius needs the postcode. |
| `priceMinEur` / `priceMaxEur` | Only listings inside this price band, in whole euros.                                               |
| `maxItems`                    | Stop after this many new listings per run (default 200; free plan caps at 25).                      |
| `stateStoreName`              | Named store remembering what was already delivered — give each watched search its own.              |
| `resetState`                  | Forget everything seen so far; the next run delivers the current snapshot as a fresh baseline.      |
| `proxyConfiguration`          | Proxy settings; keep Apify proxy enabled.                                                           |

A run needs at least a `searchQuery` or a `categoryId`. Invalid input — an inverted price band, a radius without a postcode — fails immediately with the reason in the run's status message.

#### Finding a category id

Run the [parent scraper](https://apify.com/lowlanddata/tweedehands-scraper) once with a broad query: every result carries its `categoryId`, reusable here as input.

### Use it from your code

A watch run finishes in seconds, so one synchronous HTTP call returns exactly the new listings:

```bash
curl "https://api.apify.com/v2/acts/lowlanddata~tweedehands-new-listing-alerts/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -X POST -H "Content-Type: application/json" \
  -d '{"searchQuery": "gazelle elektrische fiets", "priceMaxEur": 1500}'
```

Node.js:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('lowlanddata/tweedehands-new-listing-alerts').call({
  searchQuery: 'gazelle elektrische fiets',
  priceMaxEur: 1500,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
// items = only listings that appeared since the previous run
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("lowlanddata/tweedehands-new-listing-alerts").call(
    run_input={"searchQuery": "gazelle elektrische fiets", "priceMaxEur": 1500})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

Schedules, finished-run webhooks and the Make/Zapier/n8n integrations work as on any Apify actor — and since the dataset never contains anything but news, "run finished → send dataset items" is the entire pipeline.

### Use it with AI agents (MCP)

Claude, Cursor and other MCP-capable agents can run the watch as a tool through Apify's hosted MCP server — the agent checks your search, reads only what's new, and acts on it.

Claude Code:

```bash
claude mcp add apify --transport http "https://mcp.apify.com?actors=lowlanddata/tweedehands-new-listing-alerts"
```

Cursor or Claude Desktop (add a custom connector / MCP server with this URL):

```text
https://mcp.apify.com?actors=lowlanddata/tweedehands-new-listing-alerts
```

Sign in with your Apify account when prompted — runs are billed to it. Setup details per client: [Apify MCP docs](https://docs.apify.com/platform/integrations/mcp), or start from this actor's own MCP page: [apify.com/lowlanddata/tweedehands-new-listing-alerts/api/mcp](https://apify.com/lowlanddata/tweedehands-new-listing-alerts/api/mcp).

Prompts that work once connected:

- "Check my 2dehands watch for electric bikes and tell me if anything new under €1,500 appeared."
- "Run the watch and summarize what's new — flag private sellers asking under €1,000."
- "Anything new on my 2ememain watch today? Link the two closest to Liège."

### Is it legal to monitor 2dehands listings?

The watch reads public listing data — prices, descriptions, categories, municipality-level locations — and is built so the hard part of the GDPR question never comes up: **seller identity is never collected.** The mapper whitelists non-personal fields into the output; seller names, IDs, contact routes and profile links are structurally unreachable, not filtered out after the fact. The only seller-derived value is the business-vs-private flag. Requests are paced to keep load on the marketplace negligible, and no anti-bot protections are bypassed.

One honest limit: titles and descriptions are the seller's own words, delivered as-is. If a seller types their phone number into the listing text, that text is not rewritten — the guarantee covers the marketplace's data fields, not what sellers publish about themselves.

### Is there a 2dehands API for new-listing alerts?

No. 2dehands and 2ememain publish no public API, and the Belgian alert services built on that gap — MarktAlert among them — sell notifications, not data. This actor is the API-first version of the same idea: the "only what's new" delivery, but as JSON you can send anywhere — inbox, Slack, webhook, spreadsheet, AI agent.

### Does 2dehands block the watch?

The marketplace serves search results openly, and the watch stays well inside that welcome. It reads newest-first and stops the moment it hits a streak of listings it has already delivered, so a typical scheduled run touches a page or two. Paced requests, standard datacenter proxies, nothing circumvented — which is exactly why runs take seconds and hourly schedules are uneventful.

### How do I set up a 2dehands watch?

This actor **is** the alert engine — no date arithmetic, no dedupe step, no comparing yesterday's export to today's. The whole setup:

1. Run once with your search. That run is the baseline and is announced as such in its status message.
2. Add a Schedule in the Apify Console — hourly where being first matters, daily for slow categories — with the same input.
3. Attach an email or Slack integration to finished runs. Every message from then on is pure news.

Several searches? One Schedule per search, each with its own `stateStoreName`. The AI-assistant prompt above walks a non-technical user through every step.

### FAQ

**How do I get 2dehands alerts?** Run this actor on a Schedule with your search as input and attach an email or Slack integration to finished runs. Each run delivers only listings that appeared since the previous run, so every notification is news. The prompt in the AI-assistant section above turns setup into a guided conversation.

**Can 2ememain notify me of new listings?** Yes — set `domain: "2ememain.be"` and the watch runs against the French-language site. Everything else works identically, alerts included.

**Should I watch 2dehands.be or 2ememain.be?** They are the same marketplace in Dutch and French — the same listings behind both. Pick the site whose language you want in your listing links; the data shape does not change.

**Can I watch only listings near me?** Yes — give a Belgian `postcode` (like `2000` for Antwerpen) plus a `radiusKm` between 1 and 300. Only new listings inside that circle are delivered. The radius requires the postcode.

**Why did my first run deliver listings I'd already seen on the site?** The first run is a baseline: it delivers the current matches and memorizes them. From the second run on, only listings new since the last run come through. The status message tells you which kind of run you got.

**Why did my run deliver zero listings?** Nothing new matched since the last run — a quiet run. That is the normal state of a well-scoped watch, and it costs nothing beyond the actor start.

**Do bumped listings alert twice?** No. Sellers bump listings back to the top of the newest-first order, but the watch remembers each listing it has delivered and skips it on re-appearance. One listing, one alert.

**How long does the watch remember a listing?** Two months. A listing resurfacing after that could in principle alert again, but marketplace listings rarely live that long.

**Can I run several watches at once?** Yes — one Schedule per search, each with its own `stateStoreName`. Every store keeps the memory of exactly one search; pointing two different searches at one store mixes their histories.

**How do I make a watch start over?** Run once with `resetState: true`. The store forgets everything and that run delivers the current snapshot as a fresh baseline; the runs after it return to news-only.

**What if I change my search query?** Change the `stateStoreName` too (or set `resetState: true`). The store remembers one search; reusing it for a different one produces misleading "new" results.

**How often should the watch run?** Hourly where speed wins deals, daily for slow-moving categories. Runs finish in seconds and quiet runs are nearly free, so frequent is the safe default.

**What does a quiet run cost?** Nothing beyond the actor start — the $1.99 per 1,000 applies only to new listings actually delivered. A watch that spends most days silent costs cents per month.

**Can I export new listings to Excel or CSV?** Yes — each run's dataset exports as CSV, Excel, JSON or XML from the Apify Console or API, and it contains only that run's news.

**Does it work with Make, Zapier or n8n?** Yes — this is a standard Apify actor. Because the dataset is already filtered to new listings, "run finished → send items" is a complete scenario.

**Can I see who the seller is?** No — by design, on the same whitelist as the parent scraper. The output tells you only whether the listing comes from a business or a private seller.

**Where do I find a category id?** Run the parent 2dehands & 2ememain Scraper once with a broad query — every result carries its `categoryId`, which you can paste straight into this watch.

**Is there an official 2dehands API for alerts?** No — the sites publish no public API, and the paid Belgian alert services on top of them are notification-only. This watch is the version that hands you the data.

**Can Claude or another AI agent check my watch?** Yes, through Apify's hosted MCP server — the agent runs the watch and reads only the new listings. The one-line setup is in the MCP section above.

### Related scrapers

Same output discipline, same no-seller-data guarantee, across the family:

- [2dehands & 2ememain Scraper](https://apify.com/lowlanddata/tweedehands-scraper) — the parent: full searches, category crawls, condition and delivery filters, crawls past the 5,000-result window. Explore with the parent; stay informed with this watch.
- [Marktplaats.nl Scraper](https://apify.com/lowlanddata/marktplaats-scraper) — the Netherlands' largest marketplace, same platform family.
- [Kleinanzeigen Free Finds](https://apify.com/lowlanddata/kleinanzeigen-free-finds) — Germany's "zu verschenken" giveaways, fresh finds only.

### Troubleshooting

The actor fails fast, with the reason in the run's status message:

- **Empty input** — the watch needs a scope: fill in `searchQuery`, `categoryId`, or both.
- **Inverted price band** — `priceMinEur` was higher than `priceMaxEur`; swap them.
- **Radius without postcode** — `radiusKm` only works together with a Belgian `postcode`.
- **A run fails reporting unusable listings** — every fetched listing came back missing its core fields, which means the marketplace changed its response format, not that your search went quiet. The run fails loudly rather than pretending "nothing new"; report it on the Issues tab and it gets fixed.
- **A watch suddenly re-delivered everything** — `resetState` was left on `true`, or the `stateStoreName` changed between runs; either one starts a fresh baseline.
- **Only 25 items on the baseline** — the free-plan sample cap; run on a paid Apify plan for the full baseline.

### Support

Found an issue or missing something you need? Open an issue on the actor's **Issues tab** — reports get fixed, this actor is actively maintained.

Working well for you? A **rating on this page** takes ten seconds and helps other Belgian buyers find an API-first alert among the notification apps — and it is the clearest signal of what to build next.

# Actor input Schema

## `searchQuery` (type: `string`):

The saved search to watch, exactly as you would type it on 2dehands.be or 2ememain.be, e.g. "gazelle elektrische fiets". A query, a category id, or both.

## `domain` (type: `string`):

Which Belgian site to watch: 2dehands.be (Dutch) or 2ememain.be (French). Both serve the same listings.

## `categoryId` (type: `integer`):

Optional Marktplaats level-1 category id to watch.

## `postcode` (type: `string`):

Belgian postcode (e.g. "2000") to center a radius search on.

## `radiusKm` (type: `integer`):

Watch this many km around the postcode (up to 300). Needs a postcode.

## `priceMinEur` (type: `integer`):

Only listings costing at least this much.

## `priceMaxEur` (type: `integer`):

Only listings costing at most this much.

## `maxItems` (type: `integer`):

Stop after this many new listings per run. Free-plan runs are capped at 25 items.

## `stateStoreName` (type: `string`):

Named store remembering which listings were already delivered, so scheduled runs emit only new ones. Use a different name to track several searches independently.

## `resetState` (type: `boolean`):

Forget everything seen so far and deliver the current snapshot as a fresh baseline.

## `proxyConfiguration` (type: `object`):

Proxy settings. Keep Apify proxy enabled.

## Actor input object example

```json
{
  "searchQuery": "gazelle elektrische fiets",
  "domain": "2dehands.be",
  "maxItems": 25,
  "stateStoreName": "tweedehands-alerts-state",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `listings` (type: `string`):

Scraped marketplace listings, one JSON object per listing.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQuery": "gazelle elektrische fiets",
    "domain": "2dehands.be",
    "maxItems": 25,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("lowlanddata/tweedehands-new-listing-alerts").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQuery": "gazelle elektrische fiets",
    "domain": "2dehands.be",
    "maxItems": 25,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("lowlanddata/tweedehands-new-listing-alerts").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQuery": "gazelle elektrische fiets",
  "domain": "2dehands.be",
  "maxItems": 25,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call lowlanddata/tweedehands-new-listing-alerts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lowlanddata/tweedehands-new-listing-alerts"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/QtWdnYMJYBywr4nfu/builds/iFwPzXUcA70Z2Ml7z/openapi.json
