# Wallapop New Listings Alert (Spain, Italy, Portugal) (`superslowsloth/wallapop-listings`) Actor

Newest Wallapop listings for a keyword or category near a city in Spain, Italy or Portugal, one flat row each: title, price in EUR, previous price, location, shipping, photos, item URL - and whether each listing is new, cheaper or dearer than on the last run.

- **URL**: https://apify.com/superslowsloth/wallapop-listings.md
- **Developed by:** [Superslow Sloth](https://apify.com/superslowsloth) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.03 / 1,000 listing scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 New Listings Alert (Spain, Italy, Portugal)

Watch [Wallapop](https://es.wallapop.com) for new second-hand listings. Give it
a keyword or a category, a city (or exact coordinates) and optionally a radius
and a price band; it returns the newest listings first, one flat row each, and
tells you which ones are **new since your last run** and which ones **changed
price**. Schedule it hourly and turn on *Only new and re-priced listings* to
get an alert feed that costs nothing for listings you have already seen.

### Input

| Field | Notes |
|---|---|
| `keyword` | What you would type into Wallapop's search box. Required unless you give `categoryId`. |
| `city` | Madrid (default), Barcelona, Valencia, Sevilla, Málaga, Bilbao, Zaragoza, Milano, Roma, Torino, Napoli, Lisboa, Porto. |
| `latitude`, `longitude` | Search from exactly here instead of a city. Give both, as text (`"40.4168"`), not as JSON numbers. |
| `distanceKm` | Only listings within this radius. See *Italy and Portugal* below. |
| `categoryId` | One Wallapop category id (e.g. `24200` Technology & electronics). Every row carries its `category_id`. |
| `minPrice`, `maxPrice` | Asking-price band in whole euros. |
| `maxItems` | Stop after this many rows (default 100). Wallapop serves 40 per request. |
| `changesOnly` | Return and charge only listings that are new or re-priced since the last run of the same search. |

#### Italy and Portugal

Without a radius, Wallapop's newest-first search ignores which country the
location is in: on 2026-10-05, a search from Milan and one from Lisbon both
returned 40 of 40 **Spanish** listings. With `distance_in_km=50` the same
searches returned only Italian (20) and only Portuguese (5) listings. So the
Italian and Portuguese city presets use a **50 km radius** unless you set one,
and if you pass raw coordinates outside Spain, set `distanceKm` yourself. A
Spanish location with no radius gets Wallapop's default, country-wide feed.

### Output

| Field | Notes |
|---|---|
| `change` | `new`, `price_drop`, `price_rise` or `unchanged`, compared with the previous run of the same search. |
| `item_id`, `url`, `web_slug` | `url` is `https://es.wallapop.com/item/<web_slug>`. |
| `title`, `description` | As the seller wrote them. |
| `price`, `currency` | Asking price as Wallapop sends it (a decimal, EUR). |
| `previous_price`, `discount_percentage` | Only when the seller cut the price on Wallapop itself; null otherwise. |
| `category_id`, `category_name`, `category_path` | Wallapop's own id and its taxonomy, deepest name and full path. |
| `image_urls`, `thumbnail_url` | Photo URLs (large) and the first small one. Nothing is downloaded. |
| `reserved` | Wallapop's "reserved" flag. |
| `latitude`, `longitude`, `postal_code`, `city`, `region`, `region2`, `country_code` | The approximate location Wallapop publishes for the listing. |
| `shippable`, `seller_allows_shipping` | Whether the item can ship with Wallapop, and whether this seller offers it. |
| `bump_type`, `bumped` | `none`, or `zone` / `city` / `country` for a listing the seller paid to promote. |
| `is_refurbished`, `is_top_profile`, `has_warranty` | Wallapop's own flags. |
| `seller_id` | Wallapop's opaque user id. No seller name or rating - the search does not return them. |
| `created_at`, `modified_at` | Unix **milliseconds**, plus `created_at_iso` / `modified_at_iso` in UTC. |
| `query` | The keyword this row came from. |

#### Honest nulls

A field Wallapop did not send is `null`, never `0` or `""`. `previous_price` is
null for a listing that was never discounted - a `0` would read as a price cut
to nothing. Flags (`reserved`, `shippable`, ...) are null when the block was
missing, not `false`.

#### Promoted listings

Paid promotions ("bumps") appear in a newest-first search whatever their age.
In an unfiltered search nearly every row is one, which is why this actor needs
a keyword or a category. Filter on `bumped` if you only want organic listings.

#### Personal data

Sellers often write a phone number or a first name into the description. The
description is carried as Wallapop publishes it, because it is the listing,
but nothing is extracted from it. You are responsible for handling it under the
GDPR if you store it.

### Change tracking

Each search (keyword, location, radius, category and price band) has its own
memory, kept in a named key-value store on **your** Apify account
(`wallapop-listings-watch`), so it is never visible to anyone else. The first
run of a search has nothing to compare against: it labels every listing `new`.
From the second run on, a listing is `new` if the last run did not see it,
`price_drop` / `price_rise` if its price moved, and `unchanged` otherwise.

With `changesOnly`, `unchanged` listings are neither returned nor charged, and
the run stops at the first page on which nothing has changed - the feed is
newest first, so the rest of it is what you already have.

**What a price change can be seen on.** Wallapop's newest-first order is by
the date a listing was *created*, not by when it was edited (checked on four
probed pages, 2026-10-05). A run reads only the newest `maxItems` listings, so a
price cut is reported while the listing is still inside that window - an
old listing re-priced weeks later is not re-read and will not show up as
`price_drop`. This is a new-listing alert first; price moves are a bonus on
recent listings.

### Pricing

Pay per event:

- **$0.002** per run (`actor-start`), charged only after your input is
  validated - a run that fails on bad input costs nothing.
- **$0.000028** per listing row written (`item-scraped`), i.e. $0.028 per
  1,000 listings. With `changesOnly`, unchanged listings are not charged.

### How it fetches

It calls the same JSON search API Wallapop's website uses
(`api.wallapop.com/api/v3/search`), newest first, 40 rows per request, with
the browser's own headers. No account and no login. Apify datacenter proxy is
the default; it was answered with HTTP 200 on 2026-10-05.

# Actor input Schema

## `keyword` (type: `string`):

What you would type into Wallapop's search box, in any language. Leave empty only if you search by category id instead.

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

Where to search from. Spanish cities search all of Spain unless you set a radius. Italian and Portuguese cities default to a 50 km radius: without one, Wallapop ignores the country and returns Spanish listings (measured 2026-10-05 - Milan and Lisbon both returned 40 of 40 Spanish listings). Ignored when you give latitude and longitude.

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

Search from exactly here instead of a city, written as text such as 40.4168 (via the API, send a string, not a JSON number). Give longitude too. Outside Spain, also set a radius - see City.

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

Search from exactly here instead of a city, written as text such as -3.7038. Give latitude too.

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

Only listings within this many kilometres of the location. Leave empty for Wallapop's default: country-wide for a Spanish location, 50 km for an Italian or Portuguese city preset.

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

One Wallapop category id, such as 24200 (Technology & electronics) or 12465 (Fashion & accessories). Every row carries its category_id, so a first run tells you the id to use here. Leave empty for any category.

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

Lowest asking price to return, in whole euros. Leave empty for no lower bound.

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

Highest asking price to return, in whole euros. Leave empty for no upper bound.

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

Stop after this many listings. Wallapop serves 40 per request, newest first.

## `changesOnly` (type: `boolean`):

Return (and charge for) only listings that are new, or whose price changed while still among the newest listings read, since the last run of this same search; unchanged ones are skipped and cost nothing. The memory is per search (keyword, location, radius, category, price band). The FIRST run has nothing to compare against, so it labels every listing new and returns them all.

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

Apify datacenter proxy is the default and is what this actor was measured on: Wallapop's API answered Apify datacenter exits with HTTP 200 on 2026-10-05. A proxy is what lets a refused request be retried from a fresh address.

## Actor input object example

```json
{
  "keyword": "iphone",
  "city": "madrid",
  "maxItems": 100,
  "changesOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `items` (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 = {
    "keyword": "iphone",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("superslowsloth/wallapop-listings").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 = {
    "keyword": "iphone",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("superslowsloth/wallapop-listings").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 '{
  "keyword": "iphone",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call superslowsloth/wallapop-listings --silent --output-dataset

```

## MCP server setup

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

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/Cc0WZzMrh25GY170Y/builds/aM9iohKeFAGX128oZ/openapi.json
