By the end of this tutorial, you will have a short Python script that turns a Yapo.cl category search into structured rows: ad title, asking price in Chilean pesos, region and commune, posting and edit dates, seller and photo links.
Yapo.cl is a Chilean classifieds site. It sits behind Cloudflare: in testing on 2026-10-21, requests with no proxy and through a datacenter proxy got a Cloudflare challenge page, while Apify Unblocker returned real listing pages.
In this guide, we use Python and the Apify Actor Yapo.cl Scraper (reventadata/yapo-scraper), an unofficial tool not affiliated with Yapo.cl. You will configure a search, run the Actor and narrow the rows by keyword and place. The sample row shown below is a sample from a capture dated 2026-10-21, parsed with the Actor's own parser.
⏱️ Time estimate: one sitting: install one package, set one token, run three short Python blocks.
Prerequisites
To follow this tutorial, you will need:
-
Python 3.11+ (
apify-client3.3.0 requires it). - The Apify API client for Python.
- An Apify account and its API token, which sits on the API & Integrations page in Apify Console.
Install the client:
pip install "apify-client~=3.0"
The code reads the token from an environment variable:
export APIFY_API_TOKEN="your_apify_token_here"
What you'll achieve
You will turn a Yapo.cl category, optionally searched by keyword, into one row per ad. Each row carries id, url, title, description, price and currency (CLP), location with its region and commune parts, the category, postedAt and updatedAt as ISO 8601 dates, available, the site's featured tier, the seller's sellerId, sellerName and sellerVerified, and photo URLs.
Note: Phone and WhatsApp numbers are not in the rows. Per the README, the listing page scrambles them and the Actor does not decode them. It reads listing pages only, never an individual ad page.
Example use case: Suppose you want iPhones listed in the Santiago area. You would set categories: ["electronica-telefono-movil"], searchQueries: ["iphone"], includeKeywords: ["iphone"] and locationContains: "Santiago", and every saved row would mention "iphone" in its title or description and list a location containing Santiago.
Step 1: Access Yapo.cl Scraper
Go to the Yapo.cl Scraper page on Apify Store. The README lists every input and output field. In this tutorial we drive the Actor from Python, so everything after this step happens in code.
Step 2: Configure the search
The required input is categories: one or more Yapo.cl category pages. Open a category on yapo.cl and copy it from the address bar, either the last part or the whole address. Slugs the Actor's recon read as real listings include:
-
electronica-telefono-movil(mobile phones) andelectronica(all technology) -
autos-usados(used cars) -
anuncios-clasificados-deportes-y-ocio(sports and leisure)
A category is required because the site has no all-categories listing; marketplace, autos, bienes-raices and empleos are landing pages, not listings, and the Actor rejects them at the start of the run.
searchQueries adds one keyword search per term inside each category. Each result page holds 20 ads; maxPages (1 to 2,000, default 50) bounds how many pages each category and keyword walks, and maxItems (default 100) caps the rows saved across all of them.
Here is the setup. It searches iphone among mobile phones, reads one result page and saves at most 20 rows:
import os
from apify_client import ApifyClient
# 1. Initialize ApifyClient with the token from the environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])
# 2. Define the search: one category, one keyword, one result page, at most 20 rows
run_input = {
"categories": ["electronica-telefono-movil"],
"searchQueries": ["iphone"],
"maxPages": 1,
"maxItems": 20,
}
No proxy setting is needed in run_input: when none is given, the Actor uses Apify Unblocker, which returned real pages in testing on 2026-10-21.
Step 3: Add optional settings and filters
Some settings go to Yapo.cl in the URL, so the site does the work; others run on the rows after each page is read. Rows a filter drops never reach your dataset:
| Parameter | Type | Where it runs | What it does |
|---|---|---|---|
sort |
string | site |
RELEVANCE (the site's popularity order), NEWEST, OLDEST, PRICE_ASC, PRICE_DESC
|
minPrice / maxPrice
|
number | site | Price range in CLP; either end can stay empty |
includeKeywords |
array | rows | Keep ads whose title or description contains every word |
excludeKeywords |
array | rows | Drop ads whose title or description contains any word |
locationContains |
string | rows | Keep ads whose region and commune text contains this |
onlyAvailable |
boolean | rows | On by default: drops ads the site marks "Anuncio no disponible" |
Row filters ignore case and accents, so vina del mar matches Viña del Mar.
Three combinations that work:
-
Newest phones under a budget:
sort: "NEWEST",maxPrice: 300000. -
Used cars in one region:
categories: ["autos-usados"],locationContains: "Biobío". -
Fewer accessories in an iPhone search:
searchQueries: ["iphone"],includeKeywords: ["iphone"].
⚠️ A
sortoutside the five values is refused before the run starts. AminPriceabovemaxPrice, a landing page or an ad URL incategoriesstops the run with an error at its start.
Step 4: Run the Actor
Now start the run with client.actor("reventadata/yapo-scraper").call(run_input=run_input). The call starts the Actor on Apify and waits for it to finish before returning the run:
# 3. Start the Actor run and wait for it to finish
print("Starting Yapo.cl extraction...")
run = client.actor("reventadata/yapo-scraper").call(run_input=run_input)
if run is None or run.status != "SUCCEEDED":
status = run.status if run else "unknown"
raise SystemExit(f"Run did not succeed (status: {status}).")
# 4. Fetch the saved ads
items = client.dataset(run.default_dataset_id).list_items().items
print(f"Retrieved {len(items)} ads.\n")
Behind the scenes:
- The Actor builds the listing URL (
https://www.yapo.cl/electronica-telefono-movil?q=keyword.iphonehere; page 2 on is/electronica-telefono-movil.2) and fetches it through Apify Unblocker. - It reads the 20 ads embedded in each page's data, applies your row filters and saves the rows.
- It stops at
maxItems, atmaxPages, or at the last page the site has, whichever comes first. Past the last page Yapo.cl serves page 1 again; the Actor detects that and stops.
Step 5: View and download results
Loop over the rows to print the parts you need:
for item in items:
print(item["title"])
price = item.get("price")
if price is not None:
print(f" Price: {price:,} {item.get('currency')}")
else:
print(" Price: not stated")
print(f" Location: {item.get('location')}")
print(f" Seller: {item.get('sellerName')} (verified: {item.get('sellerVerified')})")
print(f" Posted: {item.get('postedAt')}")
print(f" Link: {item['url']}")
print("-" * 40)
Here is one row, a sample from a capture dated 2026-10-21 (page 1 of the iphone search among mobile phones) parsed with the Actor's own parser. A saved row also carries scrapedAt, added at save time:
{
"id": "27323403",
"url": "https://www.yapo.cl/electronica-telefono-movil/iphone-7/27323403",
"title": "Iphone 7",
"description": "VENDO!\nIphone 7 32gb liberado, en excelente estado, sin desgastes de bolsillo, solo un detalle estético en la esquina inferior izquierda, pantalla en perfecto estado (rayones/piquetes son de la mica que ya posee), entrada lightning en perfecto estado, botones de volumen, bloqueo y silencio sin problemas, cámara frontal y trasera sin rayones. \nParlantes en perfecto estado, batería al 75% funcionando al máximo rendimiento (ver últimas fotos) \nPRECIO CONVERSABLE. \nSe entrega con su caja, sin audífonos, con cargador alternativo y 3 carcasas de regalo. \nEntrego en Metro de Santiago en L1, L2, o L3.",
"price": 100000,
"currency": "CLP",
"location": "Región Metropolitana, Santiago",
"region": "Región Metropolitana",
"commune": "Santiago",
"category": "Marketplace / Tecnología / Teléfonos Celulares",
"categorySlug": "electronica-telefono-movil",
"vertical": "marketplace",
"postedAt": "2022-08-05T08:47:46.000Z",
"updatedAt": "2025-09-25T16:12:30.000Z",
"available": true,
"featured": null,
"sellerId": "14275316",
"sellerName": "Santiago Zuñiga",
"sellerVerified": true,
"imageUrl": "https://photos.encuentra24.com/t_or_fh_m/f_auto/v1/cl/27/32/34/03/27323403_3cf161",
"imageUrls": [
"https://photos.encuentra24.com/t_or_fh_m/f_auto/v1/cl/27/32/34/03/27323403_3cf161",
"https://photos.encuentra24.com/t_or_fh_m/f_auto/v1/cl/27/32/34/03/27323403_71ef84",
"https://photos.encuentra24.com/t_or_fh_m/f_auto/v1/cl/27/32/34/03/27323403_db1ab8",
"https://photos.encuentra24.com/t_or_fh_m/f_auto/v1/cl/27/32/34/03/27323403_7dce94",
"https://photos.encuentra24.com/t_or_fh_m/f_auto/v1/cl/27/32/34/03/27323403_bf4f31",
"https://photos.encuentra24.com/t_or_fh_m/f_auto/v1/cl/27/32/34/03/27323403_7c719f"
],
"searchCategory": "electronica-telefono-movil",
"matchedKeyword": "iphone",
"page": 1
}
Apify datasets export as JSON, CSV, XML, Excel, HTML table, RSS or JSONL.
Understanding your results
The fields that matter most:
-
priceandcurrency: the asking price as a number in Chilean pesos (CLP). Both are null when the ad states no price. -
location,region,commune: the site writes the place as region, then commune. When it gives onlyChile,regionandcommuneare null. -
postedAtandupdatedAt: creation date and last edit.NEWESTandOLDESTorder bypostedAt. In the capture, 4 of the first 6 ads in the popularity order were created in 2022. -
available: false for an ad the site marks "Anuncio no disponible". WithonlyAvailableon (the default) these never reach the dataset. -
featured: the site's highlight tier,silver,goldorplatinum; null for a regular ad. -
sellerVerified: the site's verified mark on the seller account. 5 of the 6 captured rows had it. -
searchCategory,matchedKeyword,page: which category, keyword and result page produced the row.
Pro tips
-
Pair a search with
includeKeywords. Yapo.cl's search is broad. In the capture, 3 of the first 6 ads foriphonewere chargers (titles start "Cargador De Celular").includeKeywords: ["iphone"]drops exactly those 3. -
Mind
excludeKeywordson descriptions. It reads the description too.excludeKeywords: ["cargador"]dropped the chargers in the capture, and also two iPhone ads that mention a charger in the box. -
Sort for freshness. The default popularity order is not by date; use
NEWESTwhen you track new listings. -
Check price edges yourself. Per the README, the site's price sort is not strictly by price, and a price range can let through ads priced a little under the minimum. Filter the
pricecolumn when it matters. -
Batch categories in one run. Put several slugs in
categoriesand several terms insearchQueries; an ad kept under one of them is not saved again under the next. KeepmaxPages: 1andmaxItems: 20until your downstream code handles the fields.
Troubleshooting
-
Run FAILED with zero rows: a run that is blocked, cannot read a page, or names only categories that do not exist ends
FAILED; an over-strict filter on pages that load normally endsSUCCEEDEDwith an empty dataset and a status message saying so. An unknown category gets the site's "page does not exist" answer and a log warning; check the slug against a realhttps://www.yapo.cl/<category>address. -
Run stops at once:
categorieswas empty, held a landing page or an ad URL, orminPricewas abovemaxPrice. -
Unrelated ads: the site's search is broad. Add
includeKeywords. -
Fewer rows than expected: read the summary line,
Run finished: N rows pushed. Rejections: ...; it counts each reason a row was dropped (unavailable, include or exclude words, location, already seen). From Python:client.log(run.id).get().
For help, use the Actor's Store page.
Pricing and next steps
For current pricing and details, see the Store page.
To repeat a search, save the input as a task and add an Apify schedule, which runs an Actor or task on a cron expression (0 8 * * * is every day at 8 AM). To react when a run finishes, add a webhook on ACTOR.RUN.SUCCEEDED; its payload carries the run object, including defaultDatasetId.
Which categories or prices are you planning to track on Yapo.cl? Drop a comment! 💬
Top comments (0)