# Wallapop Scraper — Listings, Prices & Sellers (`tortuga/wallapop-scraper`) Actor

Scrape Wallapop listings (second-hand items on Wallapop.com) by keyword, category or search URL: title, price, condition, location, seller, posted date, shipping, photos and listing URL, with optional item details and seller ratings. No login needed.

- **URL**: https://apify.com/tortuga/wallapop-scraper.md
- **Developed by:** [Trevor Ortega](https://apify.com/tortuga) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.20 / 1,000 listings

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 Scraper: Listings, Prices & Sellers

Scrape Wallapop listings - the second-hand items people sell on Wallapop.com, the largest second-hand marketplace in Spain, also live in Italy, Portugal and the UK. Type a keyword, pick a category, or paste a Wallapop search URL, and get every listing as clean data: title, description, price and previous price, category path, city, postcode, province and region, approximate map point, seller id, shipping and reserved flags, posted and modified dates, all photos and the listing URL. For cars and flats the attributes shown in the search results (brand, model, year, mileage, engine, gearbox, rooms, surface) come as their own columns.

It reads the same JSON API the Wallapop website uses, so a request returns 40 complete listings at once and runs take seconds. Turn on **Include item and seller details** to add the condition, view and favourite counts, hashtags and the seller's public profile (display name, private or professional, verified, rating, number of reviews and sales, member since). Export to JSON, CSV or Excel, or pull the results through the API into your app, a spreadsheet or an LLM pipeline.

### What data does Wallapop Scraper extract?

| Field | Description |
|---|---|
| `itemId`, `legacyId`, `url` | Wallapop's item id (e.g. `pj94opempv6e`), the numeric id in the web address, and the listing URL |
| `title`, `description` | Title and the seller's description text |
| `price`, `currency`, `previousPrice`, `discountPercent` | Price (EUR; GBP in the UK), the crossed-out previous price and the discount when the seller lowered it |
| `condition`, `conditionCode` | `"Buen estado"` / `good`, `"Como nuevo"` / `as_good_as_new`, ... (with details on) |
| `category`, `categoryId`, `categoryPath`, `subcategory`, `subcategoryId` | e.g. `"Bicicletas"`, `17000`, `"Bicicletas > Bicicletas y triciclos > MTB"`, `"MTB"`, `10214` |
| `availability`, `isReserved`, `isSold` | `available`, `reserved`, `sold` or `removed`; sold / removed are known when details are fetched |
| `city`, `postalCode`, `province`, `region`, `countryCode` | e.g. `"Móstoles"`, `"28937"`, `"Madrid"`, `"Comunidad de Madrid"`, `"ES"` |
| `latitude`, `longitude`, `locationPrecision` | The listing's map point rounded to about 1 km (`approximate`); a professional seller's exact point with details on (`exact`) |
| `sellerId`, `sellerIsTopProfile` | Seller id and Wallapop's "top profile" badge |
| `sellerName`, `sellerType`, `sellerVerified`, `sellerRating`, `sellerReviewCount`, `sellerSoldCount`, `sellerItemsPublished`, `sellerRegisteredAt`, `sellerCity`, `sellerUrl` | The seller's public profile (with details on): display name as shown, `private` or `professional`, verified flag, rating 0-5, reviews, items sold and published, member since, city, profile URL |
| `shippingAvailable`, `itemIsShippable` | Whether the seller ships (Wallapop Envíos) and whether the item can be shipped at all |
| `isRefurbished`, `hasWarranty`, `isBumped`, `bumpType` | Refurbished / warranty badges and paid promotion (`city`, `zone`, `country`) |
| `attributes` | Car and property attributes as `{key: value}`: `{"brand": "Mazda", "model": "CX-5", "year": 2018, "km": 155246, "engine": "Diésel", "gearbox": "Manual", "horsepower": 150, ...}` |
| `brand`, `model`, `year`, `mileageKm`, `engine`, `gearbox`, `horsepower`, `doors`, `seats`, `bodyType`, `surfaceM2`, `rooms`, `bathrooms`, `operation`, `propertyType` | The same attributes as typed columns when the listing has them |
| `views`, `favorites`, `hashtags`, `characteristics`, `itemType` | View and favourite counts, hashtags, condition text and item type (`consumer_goods`, `car`, ...) with details on |
| `postedAt`, `modifiedAt` | Posting and last-modification time, ISO 8601 UTC |
| `photos`, `photoCount`, `thumbnailUrl` | All photos (800 px) and a thumbnail |
| `searchTerm`, `searchUrl`, `position`, `scrapedAt`, `detailsFetched` | The search the listing came from, its rank in that search, when it was scraped, whether details were fetched |

### How to scrape Wallapop listings

1. Type one or more **Search terms** (one per line), as in the Wallapop search box: `bicicleta`, `iphone 15`, `ps5`.
2. Optionally narrow them down: **Category**, **Min / Max price**, **Condition**, **Only listings with shipping**, a **Location** (a city such as Madrid, Barcelona, Valencia, Milano or Lisboa, or coordinates) and a **Distance (km)**. Choose a **Sort**: newest first (the default, country-wide unless you set a distance), relevance, price, or closest.
3. Or open [wallapop.com](https://es.wallapop.com), search with any filters, and paste the address bar URL into **Start URLs**; the filters, sort and location in the URL are used as they are.
4. Set **Max items** and **Max pages per search** (40 listings per page), turn on **Include item and seller details** if you need condition, counters and seller ratings, and click **Start**. Download the results as JSON, CSV or Excel, or read them through the API.

### Input example

```json
{
  "searchTerms": ["bicicleta eléctrica", "bicicleta de carretera"],
  "location": "Barcelona",
  "distanceKm": 30,
  "minPrice": 200,
  "maxPrice": 1500,
  "condition": ["new", "as_good_as_new", "good"],
  "sort": "newest",
  "startUrls": [
    { "url": "https://es.wallapop.com/app/search?keywords=iphone%2015&min_sale_price=300&order_by=price_low_to_high&is_shippable=true" }
  ],
  "maxItems": 200,
  "maxPagesPerSearch": 3,
  "includeDetails": false
}
```

### Output example

```json
{
  "itemId": "pj94opempv6e",
  "legacyId": 1319296713,
  "title": "Bicicleta de Montaña Mondraker Finalist",
  "description": "Bicicleta de montaña Mondraker modelo Finalist, con un diseño estilizado en color gris y detalles en rojo.\n\n- Frenos de disco.\n- Suspensión delantera.\n- Neumáticos Geax Gato.",
  "price": 280,
  "currency": "EUR",
  "previousPrice": null,
  "discountPercent": null,
  "condition": "Buen estado",
  "conditionCode": "good",
  "categoryId": 17000,
  "category": "Bicicletas",
  "categoryPath": "Bicicletas > Bicicletas y triciclos > MTB",
  "subcategory": "MTB",
  "subcategoryId": 10214,
  "availability": "available",
  "isReserved": false,
  "isSold": false,
  "city": "Móstoles",
  "postalCode": "28937",
  "province": "Madrid",
  "region": "Comunidad de Madrid",
  "countryCode": "ES",
  "latitude": 40.33,
  "longitude": -3.85,
  "locationPrecision": "approximate",
  "sellerId": "8z8159dk7o63",
  "sellerName": "Occ C.",
  "sellerType": "private",
  "sellerVerified": true,
  "sellerRating": 5,
  "sellerReviewCount": 6,
  "sellerSoldCount": 2,
  "sellerRegisteredAt": "2023-09-16T19:42:34Z",
  "sellerUrl": "https://www.wallapop.com/user/oscarc-442177822",
  "shippingAvailable": false,
  "itemIsShippable": true,
  "isRefurbished": false,
  "hasWarranty": false,
  "isBumped": true,
  "bumpType": "zone",
  "attributes": null,
  "views": 22,
  "favorites": 2,
  "itemType": "consumer_goods",
  "postedAt": "2026-11-01T09:35:34Z",
  "modifiedAt": "2026-11-01T16:52:33Z",
  "photos": ["https://cdn.wallapop.com/images/10420/lt/h3/__/c10420p1319296713/i6833232008.jpg?pictureSize=W800", "..."],
  "photoCount": 3,
  "thumbnailUrl": "https://cdn.wallapop.com/images/10420/lt/h3/__/c10420p1319296713/i6833232008.jpg?pictureSize=W320",
  "url": "https://es.wallapop.com/item/bicicleta-de-montana-mondraker-finalist-1319296713",
  "detailsFetched": true,
  "searchTerm": "bicicleta",
  "position": 1,
  "scrapedAt": "2026-11-01T19:40:12Z"
}
```

A car listing adds `brand`, `model`, `year`, `mileageKm`, `engine`, `gearbox` and `horsepower` (e.g. `"Mazda"`, `"CX-5"`, `2018`, `155246`, `"Diésel"`, `"Manual"`, `150`), plus `doors`, `seats` and `bodyType` with details on.

### Which Wallapop URLs work?

Search result pages from any Wallapop country site: `https://es.wallapop.com/app/search?keywords=...` and `https://es.wallapop.com/search?category_id=...&subcategory_ids=...` with every filter the site puts in the URL (`min_sale_price`, `max_sale_price`, `order_by`, `distance_in_km`, `condition`, `is_shippable`, `brand`, `model`, `min_year`, `max_km`, ...), including `latitude` / `longitude`. URLs from it.wallapop.com, pt.wallapop.com and uk.wallapop.com work the same way; the location decides which country's listings you get, so a Milan search returns Italian listings in EUR and a London search British ones in GBP. Category landing pages such as `/c/motor/...` and single item pages (`/item/...`) are not readable through Wallapop's API: for an item page, search for its title, or give its `itemId` from an earlier run in **Item IDs**.

### Monitor new Wallapop listings on a schedule

Turn on **Only new or changed items** to watch a search: each run remembers what earlier runs returned and outputs only listings that are new or whose price or availability changed (a price drop, a listing marked as reserved or available again, or sold when details are on). Unchanged listings are dropped before any detail request and before they are saved, so they are not charged. **Max items** then sets how many listings are checked per run (keep the newest-first sort, e.g. the newest 50-100 of a search). Save the input as a task and add a Schedule in Apify (every 15 minutes, hourly or daily); give each watch its own **Monitoring key** if several schedules use the same search. Every output item has `changeType` (`new` or `changed`), and changed items carry `previousValues` with the old values, e.g. `{"price": 280}`. To follow specific listings, put their ids in **Item IDs** with monitoring on.

### Does it need a login or a proxy?

No login, account or API key: the Actor reads Wallapop's public search and item API, the same one the website calls for every visitor. Apify's datacenter proxy (the default) is enough; a search page is about 100 KB, so even thousands of listings move only a few megabytes. Nothing is solved or bypassed: when Wallapop refuses a request, the Actor retries on another IP and reports the failure in the run summary.

### How much does it cost?

You pay per listing saved to the dataset, plus a small extra per listing when **Include item and seller details** is on (one item request and the seller's profile), or per item id fetched directly. There is no start fee and no subscription; see the Pricing tab for the current prices. Platform usage is included in the price.

### Is it legal to scrape Wallapop?

This Actor collects publicly visible listing data that any visitor sees on Wallapop without an account: the listing itself, the seller's display name and public profile figures, and the approximate location Wallapop shows for a listing (rounded to about 1 km; exact points only for professional sellers). It does not log in, does not collect phone numbers or anything behind a click or login, and does not export private sellers' exact locations. Seller names are personal data under the GDPR when they belong to private individuals: you are responsible for having a legitimate purpose and a legal basis for processing them, and for respecting Wallapop's terms of use. If in doubt, ask a lawyer.

### Integrations and API

Run it from the Apify API, schedule it, and send the results to Google Sheets, Slack, Make, Zapier, n8n or a webhook. Use `maxItems` and `maxPagesPerSearch` to cap the cost of each run.

### Support

Found a bug or need a field that is not there? Open an issue on the Issues tab with the input you used and the run link.

### Related scrapers

- [Marktplaats Scraper](https://apify.com/tortuga/marktplaats-scraper): Listings, Prices & Sellers
- [Gumtree UK Scraper](https://apify.com/tortuga/gumtree-scraper): Listings, Prices & Sellers
- [Kleinanzeigen Scraper](https://apify.com/tortuga/kleinanzeigen-scraper): Listings, Prices & Sellers
- [Vinted Scraper](https://apify.com/tortuga/vinted-scraper): Listings, Prices & Sellers (All Countries)
- [OLX Brazil Scraper](https://apify.com/tortuga/olx-brazil-scraper): Listings, Prices & Sellers

### Found it useful?

A ★ review on this Actor's Apify Store page helps other users find it and tells us what to improve. Issues and feature requests are welcome in the Actor's **Issues** tab.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords as you would type them into the Wallapop search box, one search per line, e.g. "bicicleta", "iphone 15", "ps5". Each term is searched with the filters below (category, price, condition, shipping, location).

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

Wallapop search pages copied from the browser with the filters and sort you set, e.g. https://es.wallapop.com/app/search?keywords=iphone\&min_sale_price=100\&max_sale_price=500\&order_by=newest or https://es.wallapop.com/search?category_id=17000\&subcategory_ids=10214. Searches on it.wallapop.com, pt.wallapop.com and uk.wallapop.com work too. Single item pages (/item/...) cannot be read through Wallapop's API: search for the listing instead, or give its id in Item IDs.

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

Used with the search terms, or alone to browse the whole category. Other categories (e.g. Empleo = 21000, Otros = 12485) and subcategories: paste the search URL into Start URLs.

## `minPrice` (type: `number`):

Used with the search terms: lowest price, in the currency of the country searched (EUR; GBP on uk.wallapop.com).

## `maxPrice` (type: `number`):

Used with the search terms: highest price.

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

Used with the search terms: only listings in these conditions. Empty = all.

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

Used with the search terms: only listings the seller is willing to ship (Wallapop Envíos).

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

Where to search from: a big city of Spain, Italy, Portugal or the UK (Madrid, Barcelona, Valencia, Sevilla, Bilbao, Milano, Roma, Lisboa, London, ...) or "latitude, longitude". The country of the point decides which Wallapop country you search. Default: central Madrid, the point Wallapop uses for visitors without a location. Relevance and "closest" sorting prefer nearby listings; the newest and price sorts cover the whole country unless you set a distance.

## `latitude` (type: `number`):

Alternative to Location: the latitude of the point to search from (e.g. 41.3874 for Barcelona). Give longitude too.

## `longitude` (type: `number`):

Alternative to Location: the longitude of the point to search from (e.g. 2.1686 for Barcelona).

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

Only listings within this many km of the location. Empty = no limit (relevance still ranks nearby listings first).

## `sort` (type: `string`):

Order of the search results. Newest first is the best choice for monitoring new listings (country-wide unless you set a distance); relevance and closest rank by distance from the location. Start URLs keep the order_by in their own URL.

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

Stop after this many listings in total (across all searches). Keeps the cost predictable. A search page holds 40 listings.

## `maxPagesPerSearch` (type: `integer`):

How many result pages (40 listings each) to read for each search term or URL, so one busy search does not use up Max items for the others. Up to 250.

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

Also open every listing for its condition, car / property attributes, view and favourite counts and hashtags, and the seller's public profile (display name, private or professional, verified, rating, review and sold counts, member since). One item request per listing plus two small requests per distinct seller; charged as an extra 'detail' event per listing (see pricing).

## `itemIds` (type: `array`):

Wallapop item ids to fetch directly, e.g. "pj94opempv6e" (the itemId column of an earlier run). Each is one detail request (charged as a listing plus a detail event); useful to watch specific listings for price drops or reservations with Only new or changed items.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this search returned and output only new listings or listings whose price or availability (available / reserved / sold) changed. Use with a Schedule; unchanged listings are not charged. With this on, Max items is the number of listings checked per run (e.g. the newest 100).

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same search stay separate; defaults to a key derived from the search input

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

Apify Proxy (datacenter) is the default and is enough: Wallapop's API answers datacenter IPs. Each search page is about 100 KB; traffic is small.

## Actor input object example

```json
{
  "searchTerms": [
    "bicicleta"
  ],
  "startUrls": [],
  "categoryId": "",
  "condition": [],
  "sort": "newest",
  "maxItems": 20,
  "maxPagesPerSearch": 1,
  "includeDetails": false,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "searchTerms": [
        "bicicleta"
    ],
    "startUrls": [],
    "condition": [],
    "maxItems": 20,
    "maxPagesPerSearch": 1,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/wallapop-scraper").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 = {
    "searchTerms": ["bicicleta"],
    "startUrls": [],
    "condition": [],
    "maxItems": 20,
    "maxPagesPerSearch": 1,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/wallapop-scraper").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 '{
  "searchTerms": [
    "bicicleta"
  ],
  "startUrls": [],
  "condition": [],
  "maxItems": 20,
  "maxPagesPerSearch": 1,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call tortuga/wallapop-scraper --silent --output-dataset

```

## MCP server setup

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

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/4b2NAIjrn1Sha20ZO/builds/wpjCWi8xobrtPWUkr/openapi.json
