# OLX Portugal Scraper — Any Keyword (`reventadata/olx-pt-scraper`) Actor

Searches OLX.pt (Portugal) for any keyword and returns priced listings in EUR with description, location, condition, attributes and promoted-placement status. No proxy required, any category.

- **URL**: https://apify.com/reventadata/olx-pt-scraper.md
- **Developed by:** [reventadata](https://apify.com/reventadata) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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

> **Unofficial.** This Actor is not affiliated with, authorised by, endorsed by, or in any
> way officially connected to OLX. "OLX" is a trademark of its respective owner and is used
> here only to describe what this tool reads. It reads the same public pages a browser does.

OLX Portugal Scraper searches **any category** on [OLX.pt](https://www.olx.pt) - phones,
electronics, vehicles, home, fashion - and returns priced listings in EUR with the full ad
description, location, condition, structured attributes and promoted-placement status.

### What data can you extract from OLX Portugal (olx.pt)?

Every search result OLX.pt itself returns for your keyword: title, description, price and
whether it is negotiable, town and district, posting and refresh time, condition, the ad's
structured attributes (model, capacity, carrier and so on, as OLX.pt keys them), first
photo, whether the seller is a business, the seller's public name, id and member-since date,
whether the seller published a phone number, and the approximate map position OLX.pt shows. The Actor reads the data OLX.pt embeds in its
own search pages, so it needs no browser.

### Why scrape OLX Portugal (olx.pt)?

- **Every category, one Actor.** Search any keyword across the whole site.
- **No proxy needed.** 25 of 25 sequential pages came back with no proxy at all, so runs
  carry no proxy cost.
- **Condition filter is exact.** `new` returned 47 of 47 Novo rows and `used` 43 of 43 Usado
  rows on the pages sampled.
- **OLX.pt's fuzzy search is filtered.** On the first page for "iphone 13", 9 of 52 results
  were other models or accessories without the term. `requireKeywordMatch` (on by default)
  drops those.
- **Promoted placements are labelled, not hidden.** `isFeatured` marks paid placements, and
  `excludeFeatured` drops them if you want organic results only.
- **Zero-result searches return nothing.** OLX.pt shows unrelated "similar" ads when nothing
  matches; the Actor reads the page's own result count and does not return them.

### How to scrape OLX Portugal (olx.pt)

1. Put one or more search terms in `searchQueries`.
2. Optionally narrow with `condition`, a price range in EUR, or `sortBy`.
3. Run it. Results land in the dataset.

#### Input

| Field | Type | Meaning |
|---|---|---|
| `searchQueries` | array | One search per term. Required. |
| `condition` | string | `any` | `new` | `used`. |
| `minPrice` / `maxPrice` | integer | Optional, in EUR. Left out of the request when empty, never sent as 0. |
| `sortBy` | string | `relevance` | `priceAsc` | `priceDesc` | `newest`. Paid promoted listings ignore the sort, so treat it as best-effort ordering. |
| `excludeFeatured` | boolean | Drops paid promoted placements. Off by default. |
| `requireKeywordMatch` | boolean | Drops items whose title is missing any word of the query. Default on. |
| `maxItems` | integer | Cap on rows across all terms. Default 1000. |
| `maxPages` | integer | Pages per term, clamped to OLX.pt's ceiling of 25. |
| `proxyConfiguration` | object | Off by default. Turn on only if you see request failures. |

```json
{
    "searchQueries": ["iphone 13"],
    "condition": "used",
    "maxPrice": 400,
    "maxItems": 200
}
```

#### Output

One row, from a real search on 2026-10-20 (description shortened):

```json
{
    "id": "674535801",
    "title": "iPhone 13 Pro Max 256GB Azul Sierra – Aceito retomas",
    "description": "Descrição:\n\nVendo iPhone 13 Pro Max com 256GB, cor Azul Sierra, em óti ...",
    "url": "https://www.olx.pt/d/anuncio/iphone-13-pro-max-256gb-azul-sierra-aceito-retomas-IDJEhzH.html",
    "imageUrl": "https://ireland.apollo.olxcdn.com:443/v1/files/idc8xrp1kuw-PT/image;s=563x1000",
    "price": 370.0,
    "priceNegotiable": true,
    "currency": "EUR",
    "location": "Parque das Nações",
    "region": "Lisboa",
    "postedAt": "2026-10-16T12:59:55+01:00",
    "refreshedAt": "2026-10-19T13:03:09+01:00",
    "condition": "Usado",
    "attributes": {
        "state": "Usado",
        "model": "iPhone 13 Pro Max",
        "capacity": "256GB"
    },
    "isFeatured": true,
    "isBusiness": true,
    "sellerName": "Junior",
    "sellerId": "182082168",
    "sellerCompanyName": null,
    "sellerMemberSince": "2021-08-25T09:31:15+01:00",
    "sellerPhoneAvailable": false,
    "latitude": 38.77772,
    "longitude": -9.09526,
    "locationRadiusKm": 1,
    "matchedKeyword": "iphone 13"
}
```

### Notes and limits

- **25 pages per search, up to about 1,000 ads.** That is OLX.pt's own ceiling: page 26
  redirects back to page 1. Narrow with `condition` or a price range to see more.
- **Seller name, id and member-since date are returned; the phone number is not.** OLX.pt
  shows phone numbers only to logged-in accounts, so the row carries `sellerPhoneAvailable`
  (whether the seller published one) and the ad `url` to reveal it.
- **`latitude`/`longitude` are approximate**: the centre of the area OLX.pt shows on the ad
  map, within `locationRadiusKm`, never an exact address.
- **`price` and `condition` are `null`, never 0 or empty, when the ad states neither.**
- **A run that returns zero rows fails** instead of succeeding with an empty dataset, so a
  misconfigured search cannot look like an empty market.
- Reads the same public pages a browser does. See the disclaimer at the top.

### How much does it cost to scrape OLX Portugal (olx.pt)?

Pay-per-event: you pay per result written to the dataset, with no subscription. The current
price is on the Actor's Pricing tab.

### FAQ

**Is a proxy required?** No. 25 of 25 sequential pages came back with no proxy, so
`proxyConfiguration` defaults to off.

**How many listings can one search return?** Up to 25 pages, about 1,000 ads, which is
OLX.pt's own limit.

**Does it cover other OLX countries?** This Actor is OLX.pt only. OLX Romania has its own
Actor, below.

### More second-hand marketplace scrapers

| Actor | What it scrapes |
|---|---|
| [OLX Romania Scraper](https://apify.com/reventadata/olx-ro-scraper) | OLX Romania (olx.ro) — any category, no proxy required |
| [OLX Brazil Scraper](https://apify.com/reventadata/olx-scraper) | OLX Brazil (olx.com.br) — any category, structured attributes from the site's own detail chips |
| [Wallapop Scraper](https://apify.com/reventadata/wallapop-scraper) | Wallapop, Spain — any keyword, phone mode with battery-health parsing |
| [Vinted Spain Scraper](https://apify.com/reventadata/vinted-scraper) | Vinted Spain (vinted.es) — any keyword or category, no proxy required |
| [eBay Spain Scraper](https://apify.com/reventadata/ebay-scraper) | eBay Spain (ebay.es) — active listings with seller type |

# Actor input Schema

## `searchQueries` (type: `array`):

One search per term. Required, with no default — the Actor never guesses what to look for. Works across any OLX.pt category.

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

Novo (new) or Usado (used). Verified as a clean filter: 47 of 47 rows Novo and 43 of 43 Usado on the sampled pages, no leakage.

## `sortBy` (type: `string`):

Order only; changes nothing about which ads exist. A small number of paid promoted listings are interspersed regardless of sort order, so priceAsc/priceDesc/newest are best-effort, not an exact guarantee.

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

Optional. Omitted from the request entirely when empty — never defaulted to 0, which would drop every free listing.

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

Optional. Omitted from the request entirely when empty.

## `excludeFeatured` (type: `boolean`):

Drops listings whose card link is tagged search_reason=promoted. Off by default, so they are returned and labelled with isFeatured.

## `requireKeywordMatch` (type: `boolean`):

Drop items whose title is missing any word of the query. OLX.pt search is fuzzy: 9 of 52 first-page results for 'iphone 13' were other models or accessories without the term.

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

Hard cap on rows pushed across all search terms.

## `maxPages` (type: `integer`):

OLX.pt serves at most 25 pages per search (up to about 1,000 ads at 40-52 per page); page 26 silently redirects back to page 1 rather than serving more. Clamped to that ceiling.

## `proxyConfiguration` (type: `object`):

Optional. Measured over 25 sequential pages, OLX.pt needed no proxy at all: plain requests completed 25 of 25. Left off by default; only turn this on if you see request failures.

## Actor input object example

```json
{
  "searchQueries": [
    "iphone"
  ],
  "condition": "any",
  "sortBy": "relevance",
  "excludeFeatured": false,
  "requireKeywordMatch": true,
  "maxItems": 10,
  "maxPages": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `matches` (type: `string`):

Matching listings: price, location, condition, promoted status, and a link to the ad.

# 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 = {
    "searchQueries": [
        "iphone"
    ],
    "maxItems": 10,
    "maxPages": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("reventadata/olx-pt-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 = {
    "searchQueries": ["iphone"],
    "maxItems": 10,
    "maxPages": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("reventadata/olx-pt-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 '{
  "searchQueries": [
    "iphone"
  ],
  "maxItems": 10,
  "maxPages": 1
}' |
apify call reventadata/olx-pt-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reventadata/olx-pt-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/VKG6zYOMkokidgYz0/builds/RqPP8sKtx8cOdQt35/openapi.json
