Classified Listings
Pricing
from $8.00 / 1,000 results
Classified Listings
Search and track listings across Facebook Marketplace, OLX, Wallapop, Leboncoin and Custojusto through one unified API — one request shape, five sources, normalized results, plus price alerts that notify you when something new matches.
Pricing
from $8.00 / 1,000 results
Rating
0.0
(0)
Developer
Pinto Studio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 hours ago
Last modified
Categories
Share
Classifieds Listings Search Actor
The Classifieds Listings Search Actor is a web scraping tool deployed on the Apify platform that searches and extracts listing data from Facebook Marketplace, OLX, Wallapop, Leboncoin and Custojusto through one unified input, normalizing every result into the same listing shape regardless of source.
Overview
None of these five sites publish an official public API, so every integration here is a reverse-engineered one — real endpoints and page structures discovered and verified live, not guesses. The Actor supports four operations: searching one or many sources at once, fetching a single listing's full detail, calling a source's own unique endpoint (category trees, etc.), and a schedulable "alert check" that reports only genuinely new listings since the last run.
Features
- Five sources, one shape: every listing comes back with the same fields, whichever site it's from
- Multi-source search: query several sources in one run, merged and sorted together
- Price filtering & sorting:
minPrice/maxPrice, andrelevance/price_asc/price_desc/date - Listing detail: fetch one listing's full description, images and seller info
- Per-source extras: category trees (Leboncoin, Custojusto), a richer raw listing record (OLX), a dedicated vehicles search (Wallapop)
- Price alerts: a schedulable operation that only reports listings it hasn't seen before, with optional webhook delivery when new matches show up
- Resilient by design: a source failing never fails the whole run — partial results are normal
Input Configuration
The operation field decides which other fields matter. All operations accept the proxy fields
at the bottom (strongly recommended — see Rate Limits and Best Practices).
operation: "search" (default)
| Parameter | Type | Default | Description |
|---|---|---|---|
sources | array | all 5 | Which sites to search: facebook, olx, wallapop, leboncoin, custojusto |
query | string | — | Search keywords |
category | string | — | Source-specific category id/slug |
location | string | — | Source-specific location (see Parameter Options) |
minPrice / maxPrice | integer | — | Price range, in the source's own currency |
sortBy | string | relevance | relevance | price_asc | price_desc | date |
page | integer | 1 | Page number (has no effect on Facebook) |
limit | integer | 30 | Max listings per source, 1-100 |
cursor | string | — | Wallapop-only opaque pagination token (see below) |
compareBySource | boolean | false | Include min/max/avg price stats grouped by source |
operation: "listingDetail"
| Parameter | Type | Description |
|---|---|---|
source | string | One of the 5 source ids |
listingId | string | For Wallapop/Facebook: the plain id. For OLX/Custojusto/Leboncoin: the listing's full url from a search result instead — these sites need the slug, not just the numeric id |
operation: "sourceExtra"
| Parameter | Type | Description |
|---|---|---|
source | string | One of the 5 source ids |
extraName | string | categories (Leboncoin, Custojusto) | rawState (OLX) | cars (Wallapop) |
extraParams | object | Parameters for that extra, e.g. {"id": "<olx-listing-url>"} |
operation: "alertCheck"
| Parameter | Type | Default | Description |
|---|---|---|---|
sources, query, category, location, minPrice, maxPrice, sortBy | — | — | Same as search |
webhookUrl | string | — | If new matches are found, they're POSTed here as { query, sources, matches } |
alertStateKey | string | default | Key this saved search's "seen listing ids" are kept under — use a different key per saved search |
Proxy configuration (all operations)
| Parameter | Description |
|---|---|
baseProxyUrl | A generic HTTP(S) proxy URL, e.g. http://user:pass@host:port |
rotatingResidentialProxyUrl | A residential proxy that rotates IP per connection |
spainResidentialProxyUrl | A residential proxy pinned to Spain (Wallapop) |
portugalResidentialProxyUrl | A residential proxy pinned to Portugal (OLX, Custojusto) |
impitProxyMethodUrl | A service that fetches a URL with a real browser TLS fingerprint — needed for reliable Leboncoin/Wallapop access |
wallapopAppVersion, wallapopDeviceId, wallapopDeviceOs | Advanced overrides for Wallapop's required headers |
facebookLocationId | Advanced override for the default Facebook location scope |
requestBudgetMs | Advanced override for the per-request time budget |
Parameter Options
Sources (sources / source)
facebook— Facebook Marketplace (international)olx— OLX (Portugal)wallapop— Wallapop (Spain)leboncoin— Leboncoin (France)custojusto— Custojusto (Portugal)
Location (location)
Format is source-specific:
- OLX / Custojusto: a city name
- Leboncoin: a zip code or city
- Wallapop / Facebook:
"lat,lon"(e.g."40.4168,-3.7038"); Facebook also accepts a raw Facebook-internal location id
Sort order (sortBy)
relevance— defaultprice_asc— lowest price firstprice_desc— highest price firstdate— newest first
Usage Examples
Basic search across all sources
{"query": "iphone 13"}
Search two sources with price filtering
{"operation": "search","sources": ["olx", "custojusto"],"query": "iphone 13","minPrice": 100,"maxPrice": 400,"sortBy": "price_asc","limit": 20}
Compare prices across sources
{"operation": "search","query": "playstation 5","compareBySource": true}
Get one listing's full detail
{"operation": "listingDetail","source": "wallapop","listingId": "wzvg1qglddzl"}
Call a source's unique endpoint
{"operation": "sourceExtra","source": "wallapop","extraName": "cars","extraParams": { "brand": "Renault", "minYear": 2018 }}
Set up a price alert (run this on a schedule)
{"operation": "alertCheck","sources": ["olx", "custojusto"],"query": "iphone 13","maxPrice": 400,"alertStateKey": "my-iphone-alert","webhookUrl": "https://your-server.example.com/hooks/new-listing"}
Output Format
search
{"operation": "search","query": { "query": "iphone 13", "minPrice": 100, "maxPrice": 400, "page": 1, "limit": 20, "sortBy": "price_asc" },"sources": {"olx": { "ok": true, "count": 20, "hasMore": true },"custojusto": { "ok": true, "count": 18, "hasMore": true }},"errors": [],"count": 38,"listings": [{"id": "custojusto:45469559","sourceId": "45469559","source": "custojusto","title": "iPhone 14 Midnight 128GB com Caixa e Pin","description": "iPhone 14 Midnight (Meia-noite)...","price": 300,"currency": "EUR","url": "https://www.custojusto.pt/...","images": ["https://prod-images.custojusto.pt/..."],"location": { "district": "Braga", "county": "Esposende" },"postedAt": "2026-10-27T13:05:33Z","category": "Telefones e Acessórios","seller": { "name": "filipa07silva", "isCompany": false },"attributes": {}}]}
location, category, seller and attributes vary in detail by source — every other field is
always present, null when the source didn't provide it. A source failing (e.g. a transient
anti-bot block) shows up under sources.<id>.error/errors rather than failing the whole run —
200/success with partial results is the normal, by-design outcome.
listingDetail
{ "operation": "listingDetail", "source": "wallapop", "listingId": "wzvg1qglddzl", "listing": { "...": "a listing object" } }
sourceExtra
{ "operation": "sourceExtra", "source": "leboncoin", "extraName": "categories", "data": [ "...": "source-specific" ] }
alertCheck
{"operation": "alertCheck","query": { "...": "echoed params" },"sources": ["olx", "custojusto"],"errors": [],"newMatchCount": 2,"newMatches": [ "...": "listing objects" ]}
Data Storage
- Dataset: the full result of the run (as shown above) is pushed once per run.
- Key-value store: the same result is also saved under
OUTPUTfor quick viewing in the Apify console. ForalertCheckspecifically, "seen listing ids" are kept in a separate, persistent named store (CL-LISTINGS-ALERTS) keyed byalertStateKey, so a later scheduled run only reports what's genuinely new — this is not the same as the run's own default key-value store, which is fresh every run.
Error Handling
- A single source failing doesn't fail the run — check
errors/sources.<id>.errorin the output. - A hard input error (unknown source, missing required field for the chosen operation) fails the run with a descriptive message, visible in the run log and as the run's status message.
listingDetailandsourceExtrareturn a clear error if the listing/extra doesn't exist, rather than an empty success.
Performance Tips
- Narrow
sourcesto what you need — searching all 5 is slower and more likely to hit a transient per-source failure than querying one or two. - Use
limitto cap how much each source returns per run. - For Wallapop pagination beyond the first page, use the
cursora previous run's output gave you (nextCursor) instead of incrementingpage— it's a single request instead of walking forward one request per page. - For
alertCheck, give each saved search its ownalertStateKeyso they don't share seen-listing state.
Rate Limits and Best Practices
- Proxies matter more than for most scrapers here. Leboncoin and Wallapop sit behind active
anti-bot protection; without a configured proxy (and for Leboncoin specifically, the
impitProxyMethodUrlTLS-fingerprint fallback), requests to those two sources will frequently fail. OLX, Custojusto and Facebook work without any proxy configured, but a proxy still helps reliability under sustained use. - Expect occasional transient
500/failed-source entries from Leboncoin/Wallapop even with good proxies configured — these sources are actively protected and not every request gets through. - Don't run
alertCheckmore often than every few minutes per saved search — these sites don't publish new listings fast enough to need it, and it increases the chance of hitting anti-bot protection. - Facebook Marketplace only ever returns its first page (roughly 24-40 listings) — there is no anonymous way to page further, by design of this Actor (deeper pagination there requires a logged-in session, which this Actor doesn't use).
Integration Examples
JavaScript/Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const input = {operation: 'search',sources: ['olx', 'custojusto'],query: 'iphone 13',maxPrice: 400,};const run = await client.actor('YOUR_ACTOR_ID').call(input);const { items } = await client.dataset(run.defaultDatasetId).listItems();items.forEach((run) => {run.listings.forEach((listing) => {console.log(`${listing.title} — ${listing.price} ${listing.currency} (${listing.source})`);});});
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run_input = {"operation": "search","sources": ["olx", "custojusto"],"query": "iphone 13","maxPrice": 400,}run = client.actor("YOUR_ACTOR_ID").call(run_input=run_input)items = client.dataset(run["defaultDatasetId"]).list_items().itemsfor item in items:for listing in item["listings"]:print(f"{listing['title']} — {listing['price']} {listing['currency']} ({listing['source']})")
Support
If you have any questions or encounter any issues, please consult the Apify documentation or reach out to us through one of the following channels:
- Telegram: @pintoflow
- Email: pintoflowpt@gmail.com
- Apify Platform: You can also contact us directly through this platform.