By the end of this tutorial, you will have a Python script that turns Chollometro's deal feed or one of its categories into structured rows: deal title, link, price in euros, reference price, community temperature, merchant, shipping, coupon note and whether the deal has expired.
Chollometro (chollometro.com) is a Spanish deals community. Members post deals, other members vote on them, and the votes add up to a "temperature" in degrees. Each deal card carries its data as JSON in the page.
In this guide, we use Python and the Apify Actor Chollometro Scraper (reventadata/chollometro-scraper). You will pick a source, run it and filter deals by price and expiry. The row shown below is a sample from a capture dated 2026-10-24 (page 1 of the main deal feed), 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 -U "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 Chollometro listing into one row per deal. Each row carries the deal's threadId and url, title, price and nextBestPrice in euros, temperature, commentCount, merchantName, shipping fields, voucherCode, isExpired, a publishedAt Unix timestamp and the poster's public username.
Note: The Actor is not affiliated with Chollometro. Per its README, it extracts no emails, phone numbers or private identities; the only person-related field is the public community username of whoever posted the deal.
Example use case: Suppose you want live deals from the main feed at 20 euros or less. You would set sources: ["ofertas"], hideExpired: true and maxPrice: 20. On the captured page, 16 of the 30 deals would pass that filter.
Step 1: Access Chollometro Scraper
Go to the Chollometro 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 source
The main input is sources, a list. Each entry is one of two things:
-
ofertas: the main deal feed at/ofertas. - A category slug such as
electronica,videojuegosoralimentacion, read from/categorias/<slug>.
There is no keyword search. Chollometro's robots.txt disallows its /search path, so the Actor only reads the feed and category pages. Each page holds 30 deals; maxPages (default 50) bounds how many pages each source walks, and maxItems (default 1000) caps the rows saved across all sources.
Here is the setup. It reads one page of the main feed and saves at most 10 deals:
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 input: the main deals feed, one page, at most 10 deals run_input = { "sources": ["ofertas"], "maxPages": 1, "maxItems": 10, } 💡 Tip: take a category slug from the address bar of the category page on chollometro.com: the part after /categorias/. A slug with characters other than letters, digits, hyphens or underscores is skipped, and the run moves on to the next source.
No proxy setting exists or is needed: when the Actor was built, plain requests with no proxy reached the site (September 2026).
Step 3: Add optional filters
Chollometro's robots.txt also disallows the filter parameters its own pages use (priceFrom, priceTo, hide_expired, sortBy and others). So the Actor requests only plain ?page=N pages and applies these filters to the rows itself. Rows a filter drops never reach your dataset:
| Parameter | Type | What it keeps |
|---|---|---|
hideExpired | boolean | Deals not marked as expired. |
minPrice | number | Deals priced at or above this many euros. |
maxPrice | number | Deals priced at or below this many euros. |
Three combinations that work:
- Live gaming deals:
sources: ["videojuegos"],hideExpired: true. - Groceries under 10 euros:
sources: ["alimentacion"],maxPrice: 10. - Feed plus a category in one run:
sources: ["ofertas", "electronica"],minPrice: 10,maxPrice: 150.
⚠️
minPriceandmaxPricemust be 0 or more; a negative value is refused before the run starts. A deal listed at 0.0 euros (in the capture, a loyalty-club offer and a 2x1 promotion) fails anyminPriceabove zero.
Step 4: Run the Actor
Now start the run with client.actor("reventadata/chollometro-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 Chollometro extraction...") run = client.actor("reventadata/chollometro-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 deals items = client.dataset(run.default_dataset_id).list_items().items print(f"Retrieved {len(items)} deals.\n") Behind the scenes:
- The Actor builds the page URL (
/ofertas?page=1here) and fetches it with a plain HTTP client. - It reads the JSON embedded in each deal card, plus the page's pagination block, which says how many pages exist.
- It skips any deal it has already seen in this run, applies your filters, and saves the rows.
- It stops at
maxItems, atmaxPages, or at the last page the site reports, whichever comes first.
Step 5: View and download results
Loop over the rows to print the parts you need:
for item in items: print(item["title"]) print(f" Price: {item['price']} EUR (reference: {item['nextBestPrice']})") print(f" Temperature: {item['temperature']} Comments: {item['commentCount']}") print(f" Merchant: {item.get('merchantName')}") if item.get("voucherCode"): print(f" Coupon note: {item['voucherCode']}") if item["isExpired"]: print(" (expired)") print("-" * 40) Here is one row, a sample from a capture dated 2026-10-24 (page 1 of /ofertas), parsed with the Actor's own parser:
{ "threadId": "2042175", "title": "Chubasquero Alpinestars Resist III", "url": "https://www.chollometro.com/ofertas/chibasquero-alpinestars-resist-iii-2042175", "type": "Deal", "source": "ofertas", "price": 42.95, "nextBestPrice": 144.95, "temperature": 283.31, "isExpired": false, "isNew": false, "voucherCode": null, "merchantName": "Motoin", "merchantUrlName": "motoin.de", "mainGroup": "Moda y accesorios", "mainImage": "threads/raw/yOD5s", "publishedAt": 1792846764, "commentCount": 1, "shippingPrice": 10.4, "isFreeShipping": false, "posterUsername": "Seplost" } Apify datasets export as JSON, CSV, XML, Excel, HTML table, RSS or JSONL.
Understanding your results
The fields that matter most:
-
price: the deal price in euros. Every captured row had one; two read 0.0 (a Carrefour loyalty-club offer and a 2x1 offer). -
nextBestPrice: the price the deal is compared against. It was above zero on 21 of the 30 captured rows and 0.0 on the other 9, so treat 0.0 as "no reference". -
temperature: the community vote score in degrees. The captured page ranged from 198.51 to 761.05. -
voucherCode: the coupon field. In the capture, 7 rows had one, and all 7 held checkout notes rather than bare codes, such asAplicar cupón 3€("apply the 3 € coupon") orCompra recurrente("recurring purchase"). -
shippingPrice/isFreeShipping:isFreeShippingwas true on 6 rows, false on 4 and null on 20, so check for null before you filter on it. -
merchantName/merchantUrlName: Amazon on 15 of the 30 captured rows; the slug is the shop's domain, such asamazon.es. -
publishedAt: Unix seconds.1792846764is 2026-10-24 UTC. -
mainImage: a relative image path, not a full URL.
Pro tips
- Combine sources in one run. Put the feed and a few category slugs in
sources. A deal saved under one source is not saved again under the next, somaxItemscounts distinct deals. - Drop expired deals early.
hideExpired: trueremoves them before saving; 2 of the 30 captured feed deals were already expired. - Mind zero prices. Loyalty-club and 2x1 deals list 0.0. If you set
minPrice: 0.01, they disappear; if you want them, leaveminPriceunset. - Rank by temperature yourself. Sorting is not an input, because the site's
sortByparameter is robots-disallowed. Sort the rows bytemperaturein your own code. - Spot new deals between runs. Keep the
threadIdvalues you have already stored and compare each run's rows against them;publishedAttells you when each new one was posted. - Start small. Keep
maxPages: 1andmaxItems: 10until your downstream code handles the fields, then raise them.
Troubleshooting
- Run FAILED: the Actor raises an error when a page no longer has the structure it expects, so a site change shows up as a failed run, not as quietly empty data.
- Run SUCCEEDED but the dataset is empty: a filter dropped everything. Check the slugs (letters, digits, hyphens and underscores only) and the price band.
- Input refused before the run starts: a negative
minPriceormaxPrice, or amaxItems/maxPagesbelow 1. - Fewer rows than expected: the run stops at the last page the site reports, and categories end sooner than the main feed (
alimentacionhad 23 pages when the Actor was built). Read the run log from Python withclient.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 watch a category, 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 Chollometro categories are you planning to track? Drop a comment! 💬

