# Wallapop Scraper | Spain Listings & Sellers (`datascraperes/wallapop-marketplace-scraper`) Actor

Collect public Wallapop listings in Spain by keyword or category and postal code or town. Get prices, images, locations and shipping information, with optional item details and seller ratings. Apply price, distance and condition filters, then export JSON, CSV or Excel.

- **URL**: https://apify.com/datascraperes/wallapop-marketplace-scraper.md
- **Developed by:** [DataScraperES](https://apify.com/datascraperes) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 listing saveds

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

Collect public Wallapop listings in Spain by keyword or category and a postal code or town. Get prices, images, locations and shipping information, with optional item details and seller ratings. Export structured results for marketplace and pricing research.

### What this Actor does

This Actor searches Wallapop's public marketplace and saves one row per unique listing. You can run keyword searches, category searches, or several of both in a single run, and apply Wallapop's own filters for price, distance, condition, shipping and sort order. Results are paginated automatically and deduplicated by listing ID.

The Actor reads only publicly visible listings. It does not log in, does not use your Wallapop account, and does not access messages, purchases, payments or any private data.

### Use cases

- Monitor second-hand prices for a product across several Spanish cities.
- Build a dataset of listings for market, pricing or demand analysis.
- Collect seller context (rating, review count, validation badges) for marketplace research.
- Feed a dashboard, spreadsheet or alerting workflow with fresh listings.

### How to use

For one search, fill in **Keywords**, **Location** and **Maximum items**. Start with three listings. Expand **Advanced filters** only when you need price, radius, condition, shipping or category filters. Optional item and seller information is in **Enrichment**.

For several searches, expand **Multiple searches (advanced)** and add entries there. A non-empty list uses each entry's own location, filters and limit and **replaces the single-search fields above**. The Actor never adds an extra search from those fields. Enrichment and language apply to both formats.

Click **Start**, inspect the Dataset and Run summary, then export the saved results.

### Input

Enter a keyword or category and a Spanish postal code or town. Locations and keywords shown in the initial form are examples, not automatic targets for API calls.

| Field | Value when omitted | Meaning |
| --- | --- | --- |
| `keywords` | none | What to find. May be empty when a category ID is supplied. |
| `location` | none; required for a search | Spanish postal code or town, such as `28001`, `Tarragona` or `Mojácar`. |
| `maxItems` | `100` | Maximum new unique listings for this single search, up to 2,000. The initial form suggests three. |
| `distanceKm` | `50` | Search radius, from zero to five hundred kilometres. Zero is preserved. |
| `minPrice` / `maxPrice` | no price filter | Optional nonnegative price limits in EUR. |
| `condition` | any | Optional item condition. |
| `orderBy` | `most_relevance` | Relevance, closest, price ascending/descending or newest. |
| `shippableOnly` | `false` | Keep only listings that can be shipped. |
| `categoryId` / `subcategoryIds` | none | Optional category and comma-separated subcategory IDs. Category can replace keywords. |
| `includeItemDetails` | `false` | Add public item detail where available. |
| `includeSellerDetails` | `false` | Add public seller profile, rating and validation badges where available. |
| `language` | `es-ES` | Category/condition labels: `es-ES` or `en-US`; this does not change the supported market. |
| `searches` | omitted | Optional advanced list of up to 20 independent searches. A non-empty list takes priority over all single-search fields. |

Complete single-search example:

```json
{
  "searches": [],
  "keywords": "bicicleta",
  "location": "28001",
  "distanceKm": 25,
  "categoryId": "",
  "subcategoryIds": "",
  "minPrice": 10,
  "maxPrice": 500,
  "condition": "",
  "shippableOnly": false,
  "orderBy": "newest",
  "maxItems": 3,
  "includeItemDetails": false,
  "includeSellerDetails": false,
  "language": "es-ES"
}
```

Existing API integrations using `searches` remain supported. Each entry accepts the same eleven search fields listed above; enrichment and language stay outside the list. For example:

```json
{
  "searches": [
    {
      "keywords": "bicicleta",
      "location": "28001",
      "distanceKm": 25,
      "categoryId": "",
      "subcategoryIds": "",
      "minPrice": 10,
      "maxPrice": 500,
      "condition": "",
      "shippableOnly": false,
      "orderBy": "newest",
      "maxItems": 3
    }
  ],
  "includeItemDetails": false,
  "includeSellerDetails": false,
  "language": "es-ES"
}
```

Use either format. If both are submitted, a non-empty `searches` list has priority and the top-level single-search fields do not add targets or override the entries. An empty list falls back to an explicitly supplied single search. Missing targets or locations are reported without inventing a search. Each format keeps the same filters, output and billing rules.

### Output

Results are saved to the default Dataset, one row per unique listing. The **Run summary** in the key-value store shows per-search counts, enrichment counts and any failed search. You can export the Dataset as JSON, CSV, Excel or XML.

Rows include listing identity, title, description, price, currency, category path, condition, shipping flags, approximate location, images, seller identifiers, timestamps and collection metadata. Enrichment fields are `null` when the corresponding switch is off or when the source does not expose them.

```json
{
  "id": "p618n9rv35z5",
  "url": "https://es.wallapop.com/item/bicicleta-infantil-btwin-alu-16-1314110407",
  "title": "Bicicleta Infantil BTWIN ALU 16",
  "description": "Bicicleta infantil BTWIN con cuadro de aluminio en un bonito color azul. Poco uso, bien cuidada.",
  "price": 100.0,
  "currency": "EUR",
  "categoryId": "17000",
  "categoryName": "Bicicletas Infantiles",
  "taxonomy": [
    { "id": "17000", "name": "Bicicletas" },
    { "id": "10056", "name": "Bicicletas y triciclos" },
    { "id": "10216", "name": "Bicicletas Infantiles" }
  ],
  "condition": "as_good_as_new",
  "conditionText": "Como nuevo",
  "shippingItemShippable": true,
  "shippingUserAllows": false,
  "location": {
    "city": "Madrid",
    "province": "Madrid",
    "region": "Comunidad de Madrid",
    "postalCode": "28041",
    "countryCode": "ES",
    "latitude": 40.3538304,
    "longitude": -3.6962713,
    "approximated": false
  },
  "city": "Madrid",
  "province": "Madrid",
  "images": [
    {
      "id": "v6g0vre5g1mj",
      "small": "https://cdn.wallapop.com/images/10420/lq/dy/__/c10420p1314110407/i6803291016.jpg?pictureSize=W320",
      "medium": "https://cdn.wallapop.com/images/10420/lq/dy/__/c10420p1314110407/i6803291016.jpg?pictureSize=W640",
      "big": "https://cdn.wallapop.com/images/10420/lq/dy/__/c10420p1314110407/i6803291016.jpg?pictureSize=W800"
    }
  ],
  "seller": {
    "id": "g0j2n597lvjy",
    "name": null,
    "type": null,
    "isTopProfile": false,
    "ratingAverage": null,
    "reviewCount": null,
    "validations": null
  },
  "sellerName": null,
  "sellerRating": null,
  "sellerReviewCount": null,
  "createdAt": "2026-10-17T14:24:18.150000+00:00",
  "modifiedAt": "2026-10-25T17:13:58.916000+00:00",
  "reserved": false,
  "favorited": false,
  "isRefurbished": false,
  "hasWarranty": false,
  "views": 44,
  "favorites": 1,
  "conversations": 0,
  "searchQuery": "bicicleta",
  "scrapedAt": "2026-10-26T19:52:57.816635+00:00",
  "detailEnriched": true,
  "sellerEnriched": false
}
```

### Pricing

This Actor uses pay-per-event billing. The billable event is **`item-scraped`**, charged once for each unique listing saved to the Dataset.

**$1.00 per 1,000 listings** ($0.001 per listing). Item-detail and seller enrichment do not create additional charge events.

Check the Pricing tab for the current effective price before starting a large run.

### Related Actors

| Actor | Best for |
|---|---|
| [OLX Brazil Scraper | Listings, Prices & Photos](https://apify.com/datascraperes/olx-brazil-listings-scraper) | Second-hand marketplace listings in Brazil, same research workflow in another market. |
| [Idealista Property Listings Scraper | Sale & Rent](https://apify.com/datascraperes/idealista-property-search-scraper) | Spanish real-estate listings when you need property data beyond Wallapop's real-estate category. |
| [Fotocasa Spain Property Listings Scraper](https://apify.com/datascraperes/fotocasa-property-search-scraper) | A second Spanish property source to cross-check prices and inventory. |

### Limits and data quality

- Only **public** listings are collected. Sold or removed listings may not appear in search results.
- Wallapop's date filter is not exposed because the source does not apply it reliably.
- Seller profile enrichment adds up to three requests per unique seller and is off by default to keep runs fast.
- Listings are deduplicated by ID across all searches in the same run.
- A temporary source problem, empty search or invalid input has a controlled terminal outcome. Check the Run summary: `completed`, `empty`, `partial`, `source_unavailable`, `time_limit`, `charge_limit` and stopped outcomes distinguish complete work from incomplete collection. Existing verified listings are retained; a successful platform status alone does not prove complete data.
- Interrupted runs verify the same input and saved listing identities before resuming. Unverified delivery or billing stops further paid work without blindly repeating writes or charges. A recovery record created by an older incompatible version needs verification before it can be resumed.
- Oversized optional content can be omitted while the base listing is retained; the summary records incomplete coverage.
- Prices, availability and descriptions change over time. The `scrapedAt` field records when each row was collected.
- Locations are geocoded at run time with Photon and Nominatim using OpenStreetMap data, and cached for 30 days. A geocoder outage can make a search fail.
- Very large runs take longer because requests are spaced out to stay polite to the source.

### Frequently asked questions

#### Do I need a Wallapop account?

No. The Actor collects public listings anonymously and never asks for credentials.

#### Can I search several cities in one run?

Yes. Add one entry per postal code or town to `searches`, up to 20 entries.

#### Can I search any town in Spain?

Yes. Enter any Spanish postal code or town name in `location`. The Actor geocodes it at run time, so there is no fixed city list.

#### Why are some seller fields empty?

Seller fields are only filled when `includeSellerDetails` is enabled. Without enrichment the row still contains the seller ID.

#### Can I filter by publication date?

No. The date filter is not offered because the source does not apply it consistently.

#### How many listings can I collect?

Up to 2000 per search and up to 20 searches per run, subject to matching public listings and the available execution budget. These are ceilings, not guaranteed counts. If the time or spending budget is reached, inspect the summary and retain the results already saved.

### Responsible use

You are responsible for using this Actor lawfully and in line with Wallapop's terms, applicable privacy rules and local regulations. The Actor collects only publicly visible marketplace data. Do not use it to contact sellers at scale, to build profiles of individuals, or for any purpose that requires data the source does not publish.

### Support

Open an issue in the **Issues** tab with a reproducible input and the run ID. Please avoid including personal data from listings in the report.

[Examples and integration guide on GitHub](https://github.com/datacrawler-edu/wallapop-listings-scraper).

# Actor input Schema

## `keywords` (type: `string`):

What to search for, for example bicicleta, iPhone 14 or sofá. Leave empty when you search by category ID.

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

Spanish postal code or town, for example 28001, Tarragona or Mojácar. It is geocoded at run time.

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

Maximum number of new listings to save for this search.

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

Search radius around the city centre, from 0 to 500 km.

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

Optional minimum sale price in euros.

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

Optional maximum sale price in euros.

## `condition` (type: `string`):

Optional item condition filter.

## `orderBy` (type: `string`):

Result order for this search.

## `shippableOnly` (type: `boolean`):

When enabled, only listings that can be shipped are returned.

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

Optional Wallapop category ID, for example 100 for cars, 200 for real estate or 13100 for heating and climate. Required when keywords are empty.

## `subcategoryIds` (type: `string`):

Optional comma-separated subcategory IDs used together with the category ID.

## `includeItemDetails` (type: `boolean`):

Fetch each listing detail page to add condition, view/favourite counters, hashtags and the public share URL. This makes one extra request per listing.

## `includeSellerDetails` (type: `boolean`):

Fetch the public seller profile, rating and validation badges for each unique seller. Adds up to three requests per seller, cached per run.

## `language` (type: `string`):

Language used for category and condition names returned by the public API.

## `searches` (type: `array`):

Optional advanced list of up to 20 searches. Each search uses its own keywords or category, location and filters. When non-empty, this list replaces the single-search fields above; it never adds an extra search.

## Actor input object example

```json
{
  "keywords": "bicicleta",
  "location": "28001",
  "maxItems": 3,
  "distanceKm": 25,
  "minPrice": 10,
  "maxPrice": 500,
  "orderBy": "newest",
  "includeItemDetails": false,
  "includeSellerDetails": false,
  "language": "es-ES"
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per unique public Wallapop listing.

## `summary` (type: `string`):

Counts, per-search status and bounded error information.

# 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": "bicicleta",
    "location": "28001",
    "maxItems": 3,
    "distanceKm": 25,
    "minPrice": 10,
    "maxPrice": 500,
    "condition": "",
    "orderBy": "newest",
    "shippableOnly": false,
    "categoryId": "",
    "subcategoryIds": "",
    "includeItemDetails": false,
    "includeSellerDetails": false,
    "language": "es-ES"
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascraperes/wallapop-marketplace-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 = {
    "keywords": "bicicleta",
    "location": "28001",
    "maxItems": 3,
    "distanceKm": 25,
    "minPrice": 10,
    "maxPrice": 500,
    "condition": "",
    "orderBy": "newest",
    "shippableOnly": False,
    "categoryId": "",
    "subcategoryIds": "",
    "includeItemDetails": False,
    "includeSellerDetails": False,
    "language": "es-ES",
}

# Run the Actor and wait for it to finish
run = client.actor("datascraperes/wallapop-marketplace-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 '{
  "keywords": "bicicleta",
  "location": "28001",
  "maxItems": 3,
  "distanceKm": 25,
  "minPrice": 10,
  "maxPrice": 500,
  "condition": "",
  "orderBy": "newest",
  "shippableOnly": false,
  "categoryId": "",
  "subcategoryIds": "",
  "includeItemDetails": false,
  "includeSellerDetails": false,
  "language": "es-ES"
}' |
apify call datascraperes/wallapop-marketplace-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datascraperes/wallapop-marketplace-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/7JyBeqolnCqpjASw7/builds/ZkwHGxpQdHm7LcB1c/openapi.json
