Wallapop Scraper [$0.80/1K💰] | ES, PT & IT Listings
Pricing
from $0.80 / 1,000 results
Wallapop Scraper [$0.80/1K💰] | ES, PT & IT Listings
Scrape Wallapop secondhand listings across Spain, Portugal and Italy. Get title, price, condition, category, seller type, location, photos, shipping, view and favourite counts. Geo-targeted keyword search with optional detail enrichment.
Pricing
from $0.80 / 1,000 results
Rating
0.0
(0)
Developer
Ahmed Jasarevic
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a month ago
Last modified
Categories
Features
Share
Wallapop Scraper — Secondhand Listings Across Spain, Portugal and Italy
Extract Wallapop listings — the leading secondhand marketplace in Spain and one of the largest in Southern Europe — with title, price, condition, category, seller type, location, photos, shipping flags, view and favourite counters.
Keyword search across ES, PT and IT, geo-targeted to the coordinates you choose, with optional detail enrichment. Built for price monitoring, resale arbitrage research, competitive pricing, market-entry analysis and secondhand inventory data.
The one header nobody else sets
Wallapop's search API 403s every request that does not send X-Platform: web. It is a single header, and it is mandatory — without it, a scraper that is otherwise perfectly written returns nothing at all. This Actor sends it on every request.
That is the difference between a Wallapop scraper that works and one that returns an empty dataset on day one.
What you get that makes cross-country analysis possible
Wallapop changes its interface language per market — Como nuevo in Spain, Como novo in Portugal, Come nuovo in Italy. Sorting or grouping on the localized label means three different vocabularies that do not match.
This Actor returns condition and conditionValue together: the localized label and a stable machine slug that does not change with the front-end language. One group-by works across all three markets.
Also included, because they decide whether a listing is actionable:
sellerType—professionalorprivate, which separates genuine consumer resale from business inventoryisReserved/isRefurbished/hasWarranty— availability and refurbishment flagsviews/favorites/conversations— real engagement counters: how much interest a listing actually drawsageDays— listing age computed at scrape time, so "new arrivals" is a filter, not a guesssellerLocationwith full coordinates, city, region and postal code_summary— a run-level record with totals, unique listing count and whether detail enrichment ran
Main use cases
- Secondhand price monitoring — track prices for a product category in a city, week over week
- Resale and arbitrage research — find underpriced stock before it sells, compare professional vs private sellers
- Competitive pricing — see what a product sells for across three countries at once
- Market entry analysis — measure secondhand market depth and saturation in Spain, Portugal or Italy before launching
- New-arrival detection —
ageDayspluspostedAtmakes "just listed" a filter, not a manual sort - Sourcing leads — seller type, location and engagement on high-view listings surface the best counterparty candidates
- Category and assortment research — category taxonomy plus the price distribution of live inventory
- Listing quality benchmarking — use views and favorites per
ageDaysas an engagement benchmark
Input
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
keywords | array | no | ["iphone 14"] | Search keywords. One search run per keyword. |
startUrls | array | no | [] | Wallapop URLs. /search?keywords=... URLs are converted into API requests; /item/<id>/<slug> URLs are fetched detail-only. Use instead of keywords. |
country | string | no | ES | ES, PT or IT. Sets the default coordinates and Accept-Language. |
latitude | number | no | country default | Override latitude (e.g. 41.3874 for Barcelona). |
longitude | number | no | country default | Override longitude (e.g. 2.1686 for Barcelona). |
minPrice | number | no | — | Only listings at or above this price (EUR). |
maxPrice | number | no | — | Only listings at or below this price (EUR). |
orderBy | string | no | most_relevance | most_relevance, newest or closest. |
maxItems | integer | no | 40 | Max listings per keyword or search URL. Capped at 1000. Free-plan users are limited to 10 per run. |
enrichWithDetails | boolean | no | true | Fetch api/v3/items/<id> per listing. Required for condition, seller type, engagement counters and warranty flags. Turning it off roughly halves the requests. |
includeReserved | boolean | no | false | When false, listings already marked reserved are skipped. |
maxRequestsPerCrawl | integer | no | 1000 | Hard cap on total HTTP requests for the run. |
proxy | object | no | useApifyProxy: false | Standard Apify proxy picker. Not needed — the first-party API does not block. |
Default coordinates per country
Wallapop results are geo-targeted, so the coordinates decide which listings come back.
country | Default point | Language |
|---|---|---|
ES | Madrid (40.4168, -3.7038) | es-ES |
PT | Lisbon (38.7223, -9.1393) | pt-PT |
IT | Rome (41.9028, 12.4964) | it-IT |
Always override the coordinates for a specific city. Searching "iphone 14" with the Madrid default returns Madrid-area results, not Barcelona's.
Examples
City price sweep in Barcelona, detailed:
{"keywords": ["iphone 14", "iphone 15"],"country": "ES","latitude": 41.3874,"longitude": 2.1686,"maxItems": 200,"orderBy": "most_relevance"}
Cheapest possible run — search payload only, no per-item detail calls:
{"keywords": ["bicicleta"],"country": "ES","latitude": 41.3874,"longitude": 2.1686,"enrichWithDetails": false,"maxItems": 500}
New arrivals in Lisbon, reserved excluded, price-banded:
{"keywords": ["portatil"],"country": "PT","latitude": 38.7223,"longitude": -9.1393,"minPrice": 100,"maxPrice": 800,"orderBy": "newest","includeReserved": false,"maxItems": 300}
Cross-border price comparison for one product:
{"keywords": ["playstation 5"],"startUrls": ["https://wallapop.com/search?keywords=playstation%205","https://wallapop.com/search?keywords=playstation%205&latitude=41.3874&longitude=2.1686"],"enrichWithDetails": true,"maxItems": 150}
Detail-only on known listing URLs:
{"keywords": [],"startUrls": ["https://wallapop.com/item/123456789/iphone-14-pro-256gb"],"enrichWithDetails": true}
Output
One dataset — one row per listing. The Listings view gives you title, price, currency, condition, category, seller, city, country, posted date, age in days, shipping, views, favorites and the listing link.
Listing identity
| Field | Description |
|---|---|
id | Wallapop item ID — the stable join key |
title | Listing title |
description | Full listing description |
url / listingUrl | Canonical listing page URL |
shareUrl | Wallapop's own share link, if the seller set one |
Price and condition
| Field | Description |
|---|---|
price | Price as a number |
currency | Currency code, normally EUR |
condition | Localized condition label, e.g. Como nuevo, Buen estado |
conditionValue | Stable machine slug for the same condition, e.g. as_good_as_new |
isRefurbished / hasWarranty | Refurbished and warranty flags |
condition + conditionValue is the pair that makes cross-country analysis work. Group on conditionValue — the labels change between the Spanish, Portuguese and Italian front ends, the slugs do not.
Category
| Field | Description |
|---|---|
category | Leaf category name |
categoryPath[] | Full taxonomy path from root to leaf |
categoryId / itemType | Taxonomy identifiers and item type |
Seller
| Field | Description |
|---|---|
sellerId | Seller account ID |
sellerType | professional or private |
bump | Whether the listing is promoted, and how |
Location
| Field | Description |
|---|---|
sellerLocation.city / .region / .country / .postalCode | Seller location |
sellerLocation.latitude / .longitude | Coordinates |
Dates and availability
| Field | Description |
|---|---|
postedAt | ISO publish timestamp |
postedAtMs | Raw epoch milliseconds |
updatedAt | ISO last-modified timestamp |
ageDays | Age in days, computed at scrape time |
isReserved | Whether the listing is already reserved |
Engagement
| Field | Description |
|---|---|
views | View count |
favorites | Favourite count |
conversations | Message-thread count |
Media and shipping
| Field | Description |
|---|---|
photos[] | Full-size photo URLs |
photoCount | Number of photos |
shipping / itemIsShippable | Whether the item can be shipped |
supportsShipping | Shipping support flag from the detail record |
characteristics | Free-text characteristics line |
characteristicsDetails[] | attribute / value pairs from the detail record |
Provenance and run summary
| Field | Description |
|---|---|
query | The keyword or search that produced this row |
country | Target marketplace (ES / PT / IT) |
scrapedAt | ISO timestamp |
_summary.totalListings | Total listings collected in the run |
_summary.uniqueListings | Unique listing IDs seen |
_summary.detailEnrichment | Whether per-item detail calls ran |
_summary.isPayingUser | Whether the run had a paid account |
_summary.endpoint | Endpoints used |
_summary.finishedAt | Run finish time |
The two flags that change your analysis
enrichWithDetails: falsemeanscondition,sellerType,views,favorites,conversations,hasWarrantyandbumpwill benull. These fields come from the per-item detail endpoint, not the search payload. The run summary'sdetailEnrichmentfield tells you which mode produced the data — check it before you trust those columns.isReservedis filtered out by default. If you need reserved listings (to measure demand rather than availability), setincludeReserved: true.
Example output
{"id": "123456789","title": "iPhone 14 Pro 256GB estado perfecto con caja","description": "Vendido con caja y cable. Bateria al 100%. Sin marcas de uso.","url": "https://wallapop.com/item/123456789/iphone-14-pro-256gb","listingUrl": "https://wallapop.com/item/123456789/iphone-14-pro-256gb","price": 749.0,"currency": "EUR","condition": "Como nuevo","conditionValue": "as_good_as_new","isRefurbished": false,"hasWarranty": false,"category": "Móviles y Telefonía","categoryPath": ["Electrónica", "Móviles y Telefonía", "Smartphones"],"categoryId": "12403","itemType": "MOBILE_PHONE","photos": ["https://pre-cdn.wallapop.com/123456789/1.jpg"],"photoCount": 4,"sellerId": "u-987654","sellerType": "private","sellerLocation": {"city": "Barcelona","region": "Cataluña","country": "ES","postalCode": "08012","latitude": 41.3874,"longitude": 2.1686},"bump": null,"postedAt": "2026-09-28T09:12:00.000Z","postedAtMs": 1790590320000,"updatedAt": "2026-09-28T09:12:00.000Z","ageDays": 2,"shipping": true,"itemIsShippable": true,"supportsShipping": true,"isReserved": false,"views": 412,"favorites": 27,"conversations": 5,"characteristics": "128GB, 6.1 pulgadas, iOS 18","characteristicsDetails": [{ "attribute": "Capacidad", "value": "256 GB" },{ "attribute": "Color", "value": "Morado oscuro" }],"query": "iphone 14","country": "ES","scrapedAt": "2026-09-30T11:40:08.000Z"}
Run summary row:
{"_summary": {"totalListings": 200,"uniqueListings": 198,"isPayingUser": true,"detailEnrichment": true,"endpoint": "https://api.wallapop.com/api/v3/search + https://api.wallapop.com/api/v3/items/{id}","finishedAt": "2026-09-30T11:41:52.000Z"}}
Monitor secondhand prices on a schedule
Secondhand prices move fast and listings disappear quickly. Recurring runs are how you keep up:
- Daily — same keyword and city; diff
priceto catch drops and new bargain stock - Weekly — same coordinates; diff
idagainst the previous run to find newly listed items - Monthly — whole-country sweeps with
orderBy: newestto measure market depth - Cross-border arbitrage watch — run the same keyword in ES, PT and IT each week and diff
pricefor the samecategoryId - Engagement benchmarks —
views,favoritesandageDaystogether give you a listings-quality benchmark by category - Wire into your stack — Apify webhooks, Zapier, Make, Google Sheets and 100+ integrations fire on run completion; the dataset exports as JSON, CSV, Excel, XML, HTML or RSS
Keep enrichWithDetails: true on the daily run if you care about condition and engagement; drop it if you only track headline prices.
Related Actors
- Wallapop Scraper by fayoussef (
fayoussef/wallapop-scraper) — the most-used Wallapop Actor on the Store; worth comparing if you need its specific field set - Wallapop Scraper by igolaizola (
igolaizola/wallapop-scraper) — second-most-used, simpler extraction - Wallapop Scraper by blackfalcondata (
blackfalcondata/wallapop-scraper) — adds a cars vertical, seller enrichment, incremental mode and notifications; covers FR and UK as well - Wallapop Product Search by alvaraaz (
alvaraaz/wallapop-product-search) — product-search oriented - Vinted Arbitrage Scraper (
ahmed_jasarevic/vinted-cross-country-arbitrage-scraper) — the same arbitrage and cross-country price-comparison play on a different resale platform
FAQ
Why do other Wallapop scrapers return nothing?
Almost always the same reason: they do not send the mandatory X-Platform: web header. Wallapop's api.wallapop.com/api/v3 responds 403 to requests without it. It is one header, and it is the whole difference between a working scraper and an empty dataset.
Why are my results all from one city?
Because Wallapop is geo-targeted and country only sets a default coordinate — Madrid for ES, Lisbon for PT, Rome for IT. To target Barcelona you must pass latitude: 41.3874 and longitude: 2.1686 explicitly. The country field alone is never a city filter.
Why is condition in Spanish when I searched in English?
Because condition is the localized label — Como nuevo, Buen estado, Correcto. That is what a local buyer sees, so it is the right field for display. For analysis across ES, PT and IT, group on conditionValue instead: it is a stable machine slug that does not change with the front-end language.
Why is condition null?
You set enrichWithDetails: false. The search payload does not carry condition — it only comes from the per-item detail endpoint. Turning enrichment off roughly halves your requests but costs you condition, conditionValue, sellerType, views, favorites, conversations, hasWarranty and bump. Check _summary.detailEnrichment to see which mode produced a dataset.
Is maxItems really unlimited?
Not on a free account. maxItems is capped at 1000, but free-plan users are limited to 10 listings per run regardless of what they set. The run summary's _summary.isPayingUser records which tier ran. Raise maxItems on a paid plan.
Can I include reserved listings?
Yes, set includeReserved: true. By default they are skipped, because a reserved listing is no longer buyable. If you are measuring demand rather than available stock, including reserved is the correct choice.
Does it need a browser or a proxy?
No. It is API-only against Wallapop's own JSON endpoints, with the required headers, a session pool and 3 parallel requests. No headless browser, no CAPTCHA solving, no residential proxy needed. Default run uses no proxy at 256 MB.
Can I scrape a specific listing without searching?
Yes — pass the /item/<id>/<slug> URL in startUrls. It is resolved detail-only, skipping the search pagination entirely. This is also the cheapest way to refresh a roster of listings you already track.
Why does the dataset include a _summary row?
It is a run-level record, not a listing: totals, unique listing count, whether detail enrichment ran, the account tier and the endpoints used. It is convenient for logging a monitoring run and it is flagged with the _ prefix so it is trivially filterable out of analysis. Exclude rows where _summary exists when you build a listing-level dataset.
For AI Agents & LLM Apps
Callable via the Apify MCP server and the Apify API. Returns one dataset — one row per listing — plus a _summary run-record row.
Minimal valid input — one keyword, default ES coordinates, search payload only:
{ "keywords": ["iphone 14"] }
Full detailed run targeted at a specific city:
{"keywords": ["iphone 14"],"country": "ES","latitude": 41.3874,"longitude": 2.1686,"enrichWithDetails": true,"maxItems": 200}
Output field list — id, title, description, url, listingUrl, shareUrl, price, currency, condition, conditionValue, isRefurbished, hasWarranty, category, categoryPath, categoryId, itemType, photos, photoCount, sellerId, sellerType, sellerLocation, bump, postedAt, postedAtMs, updatedAt, ageDays, shipping, itemIsShippable, supportsShipping, isReserved, views, favorites, conversations, characteristics, characteristicsDetails, query, country, scrapedAt.
Behaviors an agent should know
countryonly sets a default coordinate (ES→Madrid,PT→Lisbon,IT→Rome). To target a city you must passlatitudeandlongitude. Omitting them returns results around the capital.enrichWithDetailsdefaults totrue. Setting it tofalsehalves the requests but nullscondition,conditionValue,sellerType,views,favorites,conversations,hasWarranty,bump,shareUrlandcharacteristics. Read_summary.detailEnrichmentbefore using those fields.- Free-plan users are capped at 10 listings per run, regardless of
maxItems(maxItemsitself caps at 1000)._summary.isPayingUserrecords the tier. - One billing event per dataset row (
apify-default-dataset-item), so the_summaryrow is billed like any other row. If you need to strip it for analysis, filterWHERE _summary IS NULL. startUrlsandkeywordsboth accepted./search?keywords=URLs are converted into API search requests;/item/<id>/<slug>URLs are detail-only and skip pagination. Providing both runs both.- Reserved listings are excluded by default (
includeReserved: false). conditionValueis the analysis-safe field, notcondition, which is localized per front end.- Deduplicate on
idacross keywords and start URLs —uniqueListingsin_summarytells you how much overlap a run had. - Prices are EUR across all three marketplaces;
currencycomes from the API rather than being assumed.
Billing model — pay per event: result ($0.0008–$0.001 per dataset row) plus a per-run Actor Start event scaled by memory. Free-plan users are limited to 10 rows per run.
Legal & Compliance Disclaimer
This Actor is an independent third-party tool. It is not affiliated with, endorsed by, sponsored by, or connected with Wallapop, S.L.U. or its parent group.
It reads only publicly accessible data — the JSON endpoints that power Wallapop's own public website, and publicly visible listing pages. It does not log in, does not create an account, does not use the official Wallapop business API or any partner token, does not solve CAPTCHAs, and does not use proxies by default. It reads the same public search endpoint a logged-out browser visitor uses.
Output includes seller identity data — seller account IDs, and city, region, postal code and coordinates of the seller's location. This is personal data about individuals where sellers are private individuals. You are responsible for your own lawful basis for processing it, for complying with the EU/UK GDPR and applicable national data protection law, and for honouring deletion requests.
Wallapop's terms of use prohibit automated scraping and scraping circumvention, and prohibit using scraped data to build competing directories or for unsolicited contact of sellers. Review their terms carefully before any collection, and do not use this data to contact sellers in breach of applicable marketing law (including GDPR/ePrivacy and, where applicable, LSSI-equivalent rules in Spain, Portugal and Italy). Commercial or high-volume extraction should go through Wallapop's official business or data partnerships.
This Actor is a data-collection tool; it does not provide legal advice and makes no claim that any particular use is permitted.
SEO Keywords
wallapop scraper, wallapop api, secondhand marketplace data, wallapop spain, wallapop portugal, wallapop italy, resale price tracking, used iphone prices, wallapop alternatives, marketplace scraping spain, product condition data, seller type analysis, resale arbitrage research, classified ads data, second hand market research, wallapop listings, cheap scraper wallapop, spanish ecommerce data, european marketplace data, buying and selling app data, market entry analysis spain, price monitoring tools, wallapop export, mercadona wallapop, wallapop search api