# Wallapop Monitor: New Listings & Price Change Alerts (`epicscrapers/wallapop-monitor`) Actor

Watch Wallapop searches on a schedule and get only what changed since the last run: new listings and price drops or rises in Spain, Italy and Portugal. Connect Slack, Telegram or Sheets. From $0.35 per 1,000 alerts.

- **URL**: https://apify.com/epicscrapers/wallapop-monitor.md
- **Developed by:** [Epic Scrapers](https://apify.com/epicscrapers) (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 $0.35 / 1,000 alerts

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Wallapop Monitor: new listings and price change alerts

Wallapop Monitor watches Wallapop searches on a schedule and outputs only what changed since the last run: listings that are **new**, or whose price **dropped** or **rose**. Everything you already saw is remembered in a private store on your Apify account, so a run after an hour or a day emits a handful of rows instead of the whole market — perfect for Slack, Telegram or Google Sheets alerts. It covers Spain, Italy and Portugal with the same location and filter fields as [Wallapop Scraper](https://apify.com/epicscrapers/wallapop-scraper).

- **Only changes, every run.** Each run compares the feed against the previous one and emits `new`, `price_drop` and `price_rise` rows. Unchanged listings are not output and not charged.
- **Same filters as the Scraper.** Keywords, category, search URLs, 30 cities or your own coordinates, price range, condition and shipping-only — mirrored from [Wallapop Scraper](https://apify.com/epicscrapers/wallapop-scraper).
- **Choose your alerts.** `notifyOn` picks which of new listings, price drops and price rises you want, with new listings and price drops as the default.
- **First run shows the market.** The first run outputs the current listings (up to your max per search) and remembers them, so later runs report only changes. Set `firstRun` to `baseline` to record silently instead.
- **Many schedules, one Actor.** Give each schedule a `monitorName` to keep their memories separate — an hourly and a daily watch of the same search won't interfere.
- **Rich rows.** Every alert is a full Wallapop Scraper row (price, location, images, dates, flags) plus `changeType`, `previousPrice`, `priceChange`, `firstSeenAt` and `monitorKey`, so rows join with Scraper output on `id`.
- **Pay per alert, no login.** No Wallapop account, cookies or proxy setup. A quiet run costs only the small actor-start fee.

To pull everything a search returns in one go — thousands of listings with views, favorites and seller ratings — use [Wallapop Scraper](https://apify.com/epicscrapers/wallapop-scraper). For a seller's whole inventory and reviews, see [Wallapop Seller Scraper](https://apify.com/epicscrapers/wallapop-seller-scraper).

### What can you do with Wallapop Monitor?

| Goal                                  | How                                                                                  |
| ------------------------------------- | ------------------------------------------------------------------------------------ |
| Snag underpriced deals fast           | Watch a keyword hourly with a max price and forward `new` rows to Slack or Telegram. |
| Know when a sought-after item appears | Watch a narrow search (`notifyOn: ["new"]`) and alert on every new listing.          |
| Catch price cuts on fresh listings    | Keep `price_drop` on and read `previousPrice` and `priceChange`.                     |
| Source stock before competitors       | Watch several keywords in one city and sort each run's alerts by `price`.            |
| Watch several searches independently  | One task per search, each with its own `monitorName` and schedule.                   |

### Quick start

1. Click **Try for free** and sign in to Apify.
2. Type one or more **Search keywords**, or paste a Wallapop **search URL**. Pick a **City** and filters as needed.
3. Save the input as a task (**Save as a new task**) and run it once. This run outputs the current listings and remembers them.
4. In the task's **Schedules** tab, run it hourly or daily. From the second run on, the dataset holds only new listings and price changes.
5. Connect Slack, Telegram, Google Sheets, Make or Zapier in the task's **Integrations** tab to get the alerts delivered.

### Input

```json
{
    "keywords": ["iphone 15"],
    "country": "ES",
    "city": "madrid",
    "radiusKm": 30,
    "maxPrice": 500,
    "notifyOn": ["new", "price_drop"],
    "maxItemsPerSearch": 100,
    "firstRun": "output",
    "monitorName": "iphone-hourly",
    "includeDetails": false,
    "includeSellerInfo": false
}
```

| Field                   | Type             | Default                 | Effect                                                                                              |
| ----------------------- | ---------------- | ----------------------- | --------------------------------------------------------------------------------------------------- |
| `keywords`              | Array of strings | `[]`                    | One watch per keyword. Leave empty with a `categoryId` to watch a category.                         |
| `startUrls`             | Array of strings | `[]`                    | Wallapop **search** URLs (filters in the URL are kept). Listing URLs are skipped with a warning.    |
| `monitorName`           | String           | `""`                    | Namespaces the state, so two schedules with the same search keep separate memories.                 |
| `notifyOn`              | Array            | `["new", "price_drop"]` | Which changes to emit: `new`, `price_drop`, `price_rise`.                                           |
| `maxItemsPerSearch`     | Integer          | `100`                   | Max alerts per keyword or URL per run. `0` means no limit.                                          |
| `firstRun`              | String           | `"output"`              | `output` emits current listings as `new` on a search's first run; `baseline` records them silently. |
| `country`               | String           | `"ES"`                  | `"ES"`, `"IT"` or `"PT"`.                                                                           |
| `city`                  | String           | `""`                    | One of 30 preset cities. Sets the country and a 30 km radius.                                       |
| `radiusKm`              | Integer          | none                    | Distance limit around the city or coordinates.                                                      |
| `latitude`, `longitude` | String           | none                    | Custom search point. Overrides the city and turns off the country filter.                           |
| `categoryId`            | String           | `""`                    | One Wallapop category, for example `24200` (technology) or `100` (cars).                            |
| `minPrice`, `maxPrice`  | Integer          | none                    | Price range in EUR.                                                                                 |
| `condition`             | Array            | `[]`                    | `un_opened`, `new`, `as_good_as_new`, `good`, `fair`, `has_given_it_all`.                           |
| `shippingOnly`          | Boolean          | `false`                 | Only listings that can be shipped.                                                                  |
| `includeDetails`        | Boolean          | `false`                 | Adds views, favorites, condition, brand, model and attributes to emitted rows.                      |
| `includeSellerInfo`     | Boolean          | `false`                 | Adds seller name, rating, reviews, items sold and shop contacts to emitted rows.                    |

There is no sort or posted-within option: the monitor always reads the feed newest first.

### Output

Every emitted row is a full listing (same fields as Wallapop Scraper, joined on `id`) plus the change fields. Values are illustrative.

```json
{
    "id": "abcd1234efgh",
    "url": "https://es.wallapop.com/item/iphone-15-plus-128gb-1234567890",
    "title": "iPhone 15 Plus 128GB",
    "price": 380,
    "currency": "EUR",
    "changeType": "price_drop",
    "previousPrice": 420,
    "priceChange": -40,
    "firstSeenAt": "2026-10-07T09:00:12.000Z",
    "monitorKey": "9f13c2e0a4b7d8f6c1a2b3c4",
    "city": "Madrid",
    "region": "Comunidad de Madrid",
    "countryCode": "ES",
    "distanceKm": 2.2,
    "isShippable": true,
    "isBumped": false,
    "createdAt": "2026-10-07T08:41:00.000Z",
    "modifiedAt": "2026-10-07T08:41:15.000Z",
    "sellerId": "8z81e1q7lo63",
    "searchKeyword": "iphone 15",
    "searchUrl": null,
    "scrapedAt": "2026-10-07T10:00:20.000Z"
}
```

- `changeType` is `new`, `price_drop` or `price_rise`. `previousPrice` is what this monitor last saw, `null` for new listings; `priceChange` is `price - previousPrice`.
- `firstSeenAt` is when the monitor first recorded the listing. `monitorKey` identifies the watch, so rows from several schedules can be told apart.
- Unchanged listings are not output. With `includeDetails` or `includeSellerInfo`, only emitted rows carry those (charged) fields.

#### How price detection works

The monitor pages Wallapop's feed **newest first**, and Wallapop's `newest` order is by the listing's **creation date** (verified against the API: pages are sorted by `created_at`, and edited listings keep their position). New listings therefore appear on the first pages. A **price change** does not move a listing up the feed, so a change is detected only while the listing is still on the pages the monitor reads — in practice the first pages, where recently created listings live. A price change on a listing that has drifted many pages deep may go unnoticed; run the monitor more often, or use [Wallapop Scraper](https://apify.com/epicscrapers/wallapop-scraper) for full scans.

#### Italy and Portugal

Wallapop's newest-first searches ignore the location unless a radius is set, and return Spanish listings. The monitor therefore searches Italy within 650 km of Rome and Portugal within 350 km of central Portugal, and drops listings from other countries. If you set your own latitude and longitude, no country filter is applied.

### Scheduling and integrations

1. Save your input as a task and run it once.
2. In **Schedules**, add a schedule. Every hour: cron `0 * * * *`. Every day at 9:00: `0 9 * * *`. See [Schedules](https://docs.apify.com/platform/schedules).
3. In the task's **Integrations** tab, connect Slack (post a message per dataset item), Telegram (send a message), Google Sheets (append rows) or Make/Zapier.
4. Each scheduled run appends only the changes to the dataset, and your integration forwards them.

Give each schedule its own `monitorName` when two tasks watch the same search. The state lives in a key-value store named `wallapop-monitor` on your Apify account and survives purges, migrations and reboots; delete its records to start over.

### Pricing

You pay per **alert** — a new listing or a price change actually emitted — and only for the extras you turn on. Unchanged listings, and a first run with `firstRun: baseline`, are never charged. Platform usage and proxies are included. See the **Pricing** tab for your plan's rates.

| Event           | Charged when                                  |
| --------------- | --------------------------------------------- |
| Alert           | Every row emitted to the dataset              |
| Listing details | Details are on and were fetched for the alert |
| Seller info     | Seller info is on and was found for the alert |

Set **Max cost per run** in the run options to cap spending. The run stops when it's reached.

### FAQ

**Do I need a Wallapop account or proxies?** No. The Actor uses public data and handles proxies itself.

**Why does a later run output nothing?** Nothing changed since the previous run. Quiet runs cost only the start fee. To start a search over, change `monitorName`.

**Will I miss price drops?** Only for listings deep in the feed. Because Wallapop's newest order is by creation date, price changes surface while a listing is still on the pages the monitor reads — the first pages in practice. Hourly schedules catch nearly everything recent.

**How much state is kept?** Up to 5,000 listings per search. Older entries are dropped first; listings not seen for a long time may reappear as `new` if they resurface.

**Can I watch a single listing?** No — use [Wallapop Scraper](https://apify.com/epicscrapers/wallapop-scraper) with listing URLs, on a schedule.

**Is it legal to scrape Wallapop?** The Actor collects only publicly visible listing data. Personal data such as seller names is protected by GDPR in the EU, so only collect it when you have a legitimate reason, and check Wallapop's terms for your use case.

# Actor input Schema

## `keywords` (type: `array`):

What to watch, one search per keyword (for example `iphone 15` or `bicicleta carretera`). Leave empty to watch a whole category.

## `startUrls` (type: `array`):

Paste search URLs from your browser (all filters in the URL are kept). Listing URLs are not supported here; use Wallapop Scraper for single listings.

## `monitorName` (type: `string`):

Optional name that keeps this schedule's memory separate, so two schedules watching the same search don't share state (for example `iphone-daily` and `iphone-hourly`).

## `notifyOn` (type: `array`):

Which changes to emit: new listings, prices that dropped, prices that went up.

## `maxItemsPerSearch` (type: `integer`):

Maximum rows emitted per keyword or search URL per run. `0` means no limit.

## `firstRun` (type: `string`):

What the first run of a search does. `Output` emits the current listings (up to the max per search) as new. `Record only` saves them without output or charge, so the first scheduled run is silent and later runs report only changes.

## `country` (type: `string`):

Wallapop market to search. Results are limited to this country unless you set your own latitude and longitude.

## `city` (type: `string`):

Search around a city instead of the whole country. The city sets the country. Radius defaults to 30 km.

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

Only listings within this distance of the city or your coordinates. Leave empty for the default (whole country, or 30 km around a city).

## `latitude` (type: `string`):

Custom search point, for example `40.4168`. Overrides the city. Set longitude too.

## `longitude` (type: `string`):

Custom search point, for example `-3.7038`.

## `categoryId` (type: `string`):

Limit results to one Wallapop category.

## `minPrice` (type: `integer`):

In the country's currency (EUR).

## `maxPrice` (type: `integer`):

In the country's currency (EUR).

## `condition` (type: `array`):

Only listings in these conditions. Leave empty for all.

## `shippingOnly` (type: `boolean`):

Only listings that can be shipped.

## `includeDetails` (type: `boolean`):

Adds views, favorites, conversations, condition, brand, model and all attributes to emitted rows. One extra request per alert, charged per alert.

## `includeSellerInfo` (type: `boolean`):

Adds seller name, profile URL, rating, review count, items sold, professional status and shop contacts to emitted rows. Charged per alert.

## Actor input object example

```json
{
  "keywords": [
    "iphone 15"
  ],
  "notifyOn": [
    "new",
    "price_drop"
  ],
  "maxItemsPerSearch": 100,
  "firstRun": "output",
  "country": "ES",
  "city": "",
  "categoryId": "",
  "shippingOnly": false,
  "includeDetails": false,
  "includeSellerInfo": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "keywords": [
        "iphone 15"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("epicscrapers/wallapop-monitor").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 = { "keywords": ["iphone 15"] }

# Run the Actor and wait for it to finish
run = client.actor("epicscrapers/wallapop-monitor").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 '{
  "keywords": [
    "iphone 15"
  ]
}' |
apify call epicscrapers/wallapop-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,epicscrapers/wallapop-monitor"
        }
    }
}
```

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/CnbNw5ETxkrt3VUph/builds/kIJS1NXpUas3YcTCM/openapi.json
