# Wallapop Scraper - New Listings & Price Drop Monitor (`neverempty/wallapop-new-listings-monitor`) Actor

Schedule it hourly or daily and get only new Wallapop listings, price drops and removed ads for your keywords, city and radius. Price with currency, category, city, postcode, dates, photos, shipping and seller ID. JSON, CSV or Excel.

- **URL**: https://apify.com/neverempty/wallapop-new-listings-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.27 / 1,000 listing returneds

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 Scraper - New Listings & Price Drop Monitor

**Schedule it hourly or daily → get only new Wallapop listings & price drops.** Set your keywords, city and radius once, turn on **Monitor mode**, and every scheduled run returns just what changed since the previous run: **new listings**, **price drops and rises** (with the previous price) and **listings that disappeared from the search** (sold, reserved, deleted or no longer matching). Each row says what happened in `changeType`: `new`, `price_drop`, `price_up`, `removed` or `unchanged`.

Built for resellers, bargain hunters, car and bike flippers, and n8n / Make / Zapier flows or Telegram and Discord bots that should alert on fresh Wallapop deals without paying for the same listings again.

- **Search the way you search on Wallapop.** Keywords (several at once), a Spanish city or coordinates plus a radius in km, price range, category, condition (new, as good as new, good, fair, has given it all), only shippable, posted today, and sort order (newest, cheapest, most expensive, closest). Or paste a Wallapop search address from your browser and its filters are used as they are.
- **Every listing field you need in one row.** ID and link, title, description, **price with currency**, condition, category and subcategory, city, postcode, region, coordinates, listed and updated dates, reserved, shippable, refurbished, warranty and top-seller flags, bumped flag, main photo and all photos, and the seller ID.
- **Only genuinely new listings.** Older ads that merely come into view when another one goes away are not reported as new, and ads bumped back to the top are not repeated.
- **Price drops with the numbers.** `previousPrice`, `priceChange` and `priceChangePercent` on every price change.
- **Cheap scheduled runs.** A run with nothing new returns one free row per search that says so and pays only the run start fee.
- **Each listing once.** The same listing found by two of your searches is returned once per run.

### What you get

One row per listing. Example (a real row from a Monitor mode run on 2026-10-28, `iphone 13` near Madrid; the first run, so `isFirstCheck` is `true`):

```json
{
  "status": "ok",
  "changeType": "new",
  "isFirstCheck": true,
  "id": "xzog5odyrlj9",
  "url": "https://wallapop.com/item/pantalla-iphone-13-pro-soft-oled-dd-1317965281",
  "title": "Pantalla iPhone 13 Pro Soft OLED DD",
  "description": "📱Pantalla para iPhone 13 Pro marca DD - Soft Oled, calidad como la original ✅ - Compatibilidad para transferir IC - Tasa de refresco 120Hz - Envíos 24/48h 📦 Cualquier duda no dudéis en preguntar! Ref: PAN-13P-DD",
  "price": 70,
  "currency": "EUR",
  "condition": null,
  "categoryId": 24200,
  "category": "Tecnología y electrónica",
  "subcategory": "Piezas de recambio",
  "city": "Madrid",
  "postalCode": "28042",
  "region": "Madrid",
  "countryCode": "ES",
  "latitude": 40.409498018050265,
  "longitude": -3.708078468504492,
  "createdAt": "2026-10-28T01:53:59.517Z",
  "updatedAt": "2026-10-28T01:54:14.764Z",
  "isReserved": false,
  "isShippable": true,
  "isRefurbished": false,
  "hasWarranty": false,
  "isTopSeller": false,
  "isBumped": false,
  "imageUrl": "https://cdn.wallapop.com/images/10420/ls/ok/__/c10420p1317965281/i6825065959.jpg?pictureSize=W800",
  "photos": [
    "https://cdn.wallapop.com/images/10420/ls/ok/__/c10420p1317965281/i6825065959.jpg?pictureSize=W800",
    "https://cdn.wallapop.com/images/10420/ls/ok/__/c10420p1317965281/i6825065965.jpg?pictureSize=W800"
  ],
  "sellerId": "qjwyv523egzo",
  "previousPrice": null,
  "previousCurrency": null,
  "priceChange": null,
  "priceChangePercent": null,
  "searchKeywords": "iphone 13",
  "searchDescription": "\"iphone 13\", near Madrid (50 km)",
  "searchUrl": null,
  "checkedAt": "2026-10-28T06:37:33.258Z",
  "note": null
}
```

| Field | What it is |
|---|---|
| `status` | `ok` for a listing. Free rows (with `note` saying why): `no-new-listings`, `no-results`, `incomplete`, `blocked`, `unreadable`, `bad-input`, `budget-reached`. |
| `changeType` | Monitor mode: `new`, `price_drop`, `price_up`, `removed` or `unchanged`. `null` in a normal search. |
| `isFirstCheck` | `true` on the first monitor run, which returns every listing as `new` to set the baseline. |
| `id`, `url`, `title`, `description` | Wallapop listing ID, link, title and description as the seller wrote them. Phone numbers and e-mail addresses in the text are replaced with `[phone removed]` / `[email removed]`. |
| `price`, `currency` | Asking price and its currency (EUR in Spain, Italy and Portugal). |
| `previousPrice`, `previousCurrency`, `priceChange`, `priceChangePercent` | Monitor mode: the price at the previous run and the change. |
| `condition` | The condition you searched for (`new`, `as_good_as_new`, `good`, `fair`, `has_given_it_all`). Search with one or more conditions to get it on every row. |
| `categoryId`, `category`, `subcategory` | Wallapop category number and names, in Spanish as Wallapop shows them (`Tecnología y electrónica`, `Bicicletas`). |
| `city`, `postalCode`, `region`, `countryCode`, `latitude`, `longitude` | Where the item is, as shown on the listing (Wallapop shows an approximate point). |
| `createdAt`, `updatedAt` | When the listing was published and last edited. |
| `isReserved`, `isShippable`, `isRefurbished`, `hasWarranty`, `isTopSeller`, `isBumped` | Listing flags. |
| `imageUrl`, `photos` | Main photo and all photos (large, 800 px wide). |
| `sellerId` | Wallapop user ID of the seller. No names. |
| `searchKeywords`, `searchDescription`, `searchUrl` | The search that found it (`searchUrl` is the address you pasted). |
| `checkedAt`, `note` | Time of the check, and the reason on free rows. |

### Input

| Field | Type | What it does |
|---|---|---|
| `keywords` | list | What you would type into the Wallapop search box, one per line (`iphone 13`, `bicicleta`, `ps5`). Each keyword is its own search. Empty (and no URLs) = example search `iphone` near Madrid. |
| `location` | string | A city (Madrid, Barcelona, Valencia, Sevilla, Zaragoza, Málaga, Bilbao and about 70 more, also Roma, Milano, Lisboa, Porto) or coordinates `40.4168,-3.7038`. Empty = all of Spain. |
| `distanceKm` | integer | Radius around the location, 1 to 500 km. Default 50 km. |
| `minPrice`, `maxPrice` | integer | Price range in euros. |
| `category` | select | One Wallapop category (Cars, Motorbike, Technology & electronics, Bikes, Fashion & accessories, Home & garden ...). |
| `conditions` | list | `new`, `as_good_as_new`, `good`, `fair`, `has_given_it_all`. Each condition is searched separately so every row carries it. Keywords × conditions + search URLs: up to 20 searches per run. |
| `sortBy` | select | `newest` (default), `price_low_to_high`, `price_high_to_low`, `closest`. Monitor mode always uses `newest`. |
| `onlyShippable`, `postedToday` | boolean | Only listings with shipping / published today. |
| `searchUrls` | list | Wallapop search addresses copied from your browser (`https://es.wallapop.com/search?keywords=...`). Keywords, location, distance, price, category, condition, shipping and order are taken from the address. |
| `maxResultsPerSearch` | integer | How many of the newest listings to read per search, 1 to 5,000. Default 100. In Monitor mode these are the listings watched on every run. |
| `onlyNew` | boolean | Monitor mode. Default off. |
| `includePriceChanges`, `includeRemoved` | boolean | Monitor mode: return price changes / listings that disappeared (sold, reserved, deleted or no longer matching). Default on. |
| `includeUnchanged` | boolean | Monitor mode: also return listings that did not change. Default off. |
| `watchName` | string | Separate memories for different schedules on the same searches. |
| `resetMonitoringState` | boolean | Forget what earlier runs saw for these searches and start a new baseline. |

### Monitor mode

1. The first run returns the newest `maxResultsPerSearch` listings of each search as `new` with `isFirstCheck: true` and remembers them.
2. Every later run reads the same window again and returns:
   - `new`: a listing it has not seen, published at or after the oldest listing it watched last time;
   - `price_drop` / `price_up`: a watched listing whose price changed, with `previousPrice`;
   - `removed`: a watched listing that is no longer in the search (sold, reserved and hidden, deleted, or edited out of your filters).
3. Nothing changed = one free `no-new-listings` row per search.
4. The memory lives in a named key-value store of your account (`wallapop-monitor-state`), one entry per search and `watchName`, so it carries over between scheduled runs.
5. Rows that did not fit into your maximum charge are not charged and are returned by the next run.

Pick `maxResultsPerSearch` so that it covers more listings than appear between two runs. On the night of 2026-10-28 the newest 100 listings for `iphone` near Madrid spanned about 7.5 hours, so the default 100 covers an hourly schedule; daytime is busier, so raise it for broad searches (all of Spain, popular words) or longer gaps between runs.

### Pricing

Pay per event:

- **Run start**: once per run that returns listings, and once per monitor run that read and compared the list, even when nothing changed. Not charged when nothing could be read or nothing matched in a normal search.
- **Listing returned**: per listing row delivered. Free rows (nothing new, no results, bad input and so on) are never charged.

If your maximum total charge has no room for the run start fee plus one listing, the run reads nothing and is charged nothing.

### Good to know

- Wallapop's newest-first list is split by date, so some pages come back empty in between; the Actor keeps reading until it has `maxResultsPerSearch` listings or the search ends.
- `condition` is filled when you search with conditions; Wallapop's list itself does not carry it.
- If more new listings appear between two runs than `maxResultsPerSearch`, the oldest of them are not reported; raise it for busy searches or schedule more often.
- Coordinates are the approximate point Wallapop shows, not an exact address.
- Two schedules running the same searches with the same `watchName` at the same moment can return the same change twice; give each schedule its own `watchName`.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

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

What to look for on Wallapop, one search per line (for example "iphone 13" or "bicicleta"). Each keyword is a separate search. Schedule this Actor hourly or daily with Monitor mode on to get only new listings and price drops.

## `location` (type: `string`):

A Spanish city (for example Madrid, Barcelona, Valencia, Sevilla, Bilbao; also Roma, Milano, Lisboa, Porto) or coordinates like "40.4168,-3.7038". Leave empty to search all of Spain.

## `distanceKm` (type: `integer`):

Radius around the city in kilometres. Default 50 km when a city is given.

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

Lowest price in the listing currency (EUR on Wallapop Spain).

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

Highest price in the listing currency (EUR on Wallapop Spain).

## `category` (type: `string`):

Limit the search to one Wallapop category.

## `conditions` (type: `array`):

Only listings in these conditions. Each condition is searched separately, so every row says its condition. Leave empty for any condition (the condition column is then empty).

## `sortBy` (type: `string`):

Order of the results. Monitor mode always uses newest first.

## `onlyShippable` (type: `boolean`):

Only listings the seller can ship.

## `postedToday` (type: `boolean`):

Only listings published today.

## `searchUrls` (type: `array`):

Paste a search from wallapop.com (the address of the results page after you set keywords, location, distance, price, category and condition). Filters in the address are used as they are.

## `maxResultsPerSearch` (type: `integer`):

How many of the newest listings to read for each search (up to 5,000). In Monitor mode these are the listings that are watched on every run.

## `onlyNew` (type: `boolean`):

Schedule it hourly/daily → get only new listings & price drops. The first run returns every listing to set the baseline; later runs return only what changed since the previous run: new listings, price drops and rises (with the previous price) and removed listings. A run with nothing new returns one free row that says so.

## `includePriceChanges` (type: `boolean`):

In Monitor mode, return listings whose price changed, with the previous price and the change.

## `includeRemoved` (type: `boolean`):

In Monitor mode, return listings that disappeared from the search since the last run (sold, reserved and hidden, deleted, or no longer matching).

## `includeUnchanged` (type: `boolean`):

In Monitor mode, also return listings that did not change (charged like any other row).

## `watchName` (type: `string`):

Keeps separate memories for different schedules that use the same searches. Letters, digits, dot, dash and underscore.

## `resetMonitoringState` (type: `boolean`):

Forget what earlier runs saw for these searches and return every listing again as the new baseline.

## Actor input object example

```json
{
  "keywords": [
    "iphone"
  ],
  "location": "Madrid",
  "sortBy": "newest",
  "onlyShippable": false,
  "postedToday": false,
  "maxResultsPerSearch": 100,
  "onlyNew": false,
  "includePriceChanges": true,
  "includeRemoved": true,
  "includeUnchanged": false,
  "resetMonitoringState": false
}
```

# Actor output Schema

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

One row per Wallapop listing: ID, link, title, description, price with currency, condition, category, city, postcode, listed and updated dates, reserved and shipping flags, photos and seller ID. Monitor mode marks each row new, price_drop, price_up, removed or unchanged, with the previous price. A search with no listings, nothing new, a refused request or a run that hit its maximum charge comes back as a free row that says why.

# 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"
    ],
    "location": "Madrid"
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/wallapop-new-listings-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"],
    "location": "Madrid",
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/wallapop-new-listings-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"
  ],
  "location": "Madrid"
}' |
apify call neverempty/wallapop-new-listings-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/wallapop-new-listings-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/MdmRyBNMFXugvt7bn/builds/ciCkE22OJcDIlzHqM/openapi.json
