# Casa SAPO Scraper - Portugal Real Estate Listings (`studio-amba/casa-sapo-pt-scraper`) Actor

Scrape property listings from Casa SAPO (casa.sapo.pt), Portugal's national property portal. Extract prices, locations, areas, rooms, images, agencies, and geo-coordinates for apartments, houses, land, and commercial properties for sale or rent. No login or cookies required.

- **URL**: https://apify.com/studio-amba/casa-sapo-pt-scraper.md
- **Developed by:** [Studio Amba](https://apify.com/studio-amba) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 result scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Casa SAPO Scraper — Portugal Real Estate Listings

Scrape property listings from [Casa SAPO](https://casa.sapo.pt), Portugal's national real estate portal (part of the SAPO/Altice network). Search apartments, houses, land, and commercial properties for sale or rent, and get clean structured JSON with prices, areas, bedrooms, locations, geo-coordinates, photos, and agency details.

No login, no cookies, no API keys required.

### What does this actor do?

You give it a Portuguese location (district, city, or council), a transaction type (buy or rent), and a property type. It searches Casa SAPO and returns every matching listing as a flat JSON object, up to the limit you set.

It reads the same search-result cards a visitor sees in a browser: asking price, price per square meter, living area, bedroom count, full location string, latitude/longitude, publication photos, and whether the listing is posted by an agency or a private owner.

### Why use it?

- **Market research** — track asking prices per city, council, or parish over time.
- **Investment analysis** — compare price per m² across regions before buying, using the geo-coordinates for map-based analysis.
- **Lead generation** — find new listings from private owners or specific agencies the moment they appear.
- **Portfolio monitoring** — agencies can watch competitor listings and pricing in their area.
- **Data science** — build datasets of the Portuguese housing market for modeling and dashboards.

#### Why this scraper over the alternative on the Store

A Casa SAPO scraper already exists on the Apify Store built for single-property detail pages. This actor is built for search-and-scale: point it at a location and transaction type and it paginates the whole result set itself, deduplicates by listing ID, and returns geo-coordinates parsed straight from each listing's own structured data — not just what's visible in the card text. It also ships with a content-based block detector (Casa SAPO sits behind Cloudflare) so a challenge page fails the run loudly instead of silently returning zero or garbage rows.

### Input options

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `location` | string | `lisboa` | Portuguese district, city, or council slug (e.g. `lisboa`, `porto`, `braga`, `faro`, `cascais`). |
| `transaction` | string | `comprar` | `comprar` (buy) or `alugar` (rent). |
| `propertyType` | string | `casas` | One of `casas` (apartments + houses combined), `apartamentos`, `moradias`, `terrenos`, `lojas`, `escritorios`, `armazens`, `garagens`. |
| `priceMin` | integer | — | Minimum price in EUR (monthly rent for `alugar`). Applied client-side after fetch. |
| `priceMax` | integer | — | Maximum price in EUR (monthly rent for `alugar`). Applied client-side after fetch. |
| `maxResults` | integer | `50` | Maximum number of listings to return. Hard-capped at 20,000. |
| `proxyConfiguration` | object | PT residential | Apify proxy settings. Portuguese residential proxies are required — Casa SAPO rate-limits aggressively on a single IP. |

#### Example input

```json
{
    "location": "porto",
    "transaction": "comprar",
    "propertyType": "apartamentos",
    "priceMax": 300000,
    "maxResults": 100
}
```

### Output fields

| Field | Type | Example |
|-------|------|---------|
| `listingId` | string | `"b06c9aa8-2c71-11f1-9a8d-060000000057"` |
| `listingTitle` | string | `"Apartamento T3"` |
| `transactionType` | string | `"sale"` |
| `propertyType` | string | `"Apartamento T3"` |
| `price` | number | null | `820000` |
| `currency` | string | null | `"EUR"` |
| `pricePerSqm` | number | null | `6074.07` |
| `areaM2` | number | null | `135` |
| `rooms` | integer | null | `3` |
| `condition` | string | null | `"Recuperado"` |
| `city` | string | null | `"Lisboa"` |
| `district` | string | null | `"Belém"` |
| `locationFull` | string | null | `"Restelo (Santa Maria de Belém), Lisboa, Distrito de Lisboa"` |
| `latitude` | number | null | `38.69961` |
| `longitude` | number | null | `-9.22623` |
| `agencyName` | string | null | `"RB real estate"` |
| `isPrivateOwner` | boolean | null | `false` |
| `summary` | string | null | Listing description text |
| `imageUrl` | string | null | Main photo URL |
| `imageUrls` | array | null | All photo URLs from the search card |
| `url` | string | Full listing URL |
| `scrapedAt` | string | ISO timestamp |

#### Example output

```json
{
    "listingId": "b06c9aa8-2c71-11f1-9a8d-060000000057",
    "listingTitle": "Apartamento T3",
    "transactionType": "sale",
    "propertyType": "Apartamento T3",
    "price": 820000,
    "currency": "EUR",
    "pricePerSqm": 6074.07,
    "areaM2": 135,
    "rooms": 3,
    "condition": "Recuperado",
    "city": "Lisboa",
    "district": "Belém",
    "locationFull": "Restelo (Santa Maria de Belém), Lisboa, Distrito de Lisboa",
    "latitude": 38.69961,
    "longitude": -9.22623,
    "agencyName": "RB real estate",
    "isPrivateOwner": false,
    "summary": "T3 com 1 Suite + Pátio comum e alguma VISTA RIO | RESTELO - Lisboa...",
    "imageUrl": "https://media.casasapo.pt/Z360x270/OAYES/S5/C6631/P29773974/Tphoto/ID9650c601-0000-0500-0000-000018ef323f.jpg.webp",
    "imageUrls": ["https://media.casasapo.pt/Z360x270/OAYES/S5/C6631/..."],
    "url": "https://casa.sapo.pt/comprar-apartamento-t3-lisboa-restelo-(santa-maria-de-belem)-b06c9aa8-2c71-11f1-9a8d-060000000057.html",
    "scrapedAt": "2026-09-05T19:41:04.217Z"
}
```

### How to scrape Casa SAPO data

1. Open the actor on Apify and click **Try for free** (or run it via the Apify API).
2. Set `location` to a Portuguese district, city, or council: `lisboa`, `porto`, `braga`, `faro`, `cascais`, or a smaller council name.
3. Pick the transaction: `comprar` for sales listings, `alugar` for rentals.
4. Pick a property type: `casas` for apartments and houses together, or narrow to apartments, houses, land, shops, offices, warehouses, or garages.
5. Optionally set `priceMin` / `priceMax` to narrow the price band.
6. Set `maxResults` and press **Start**. Results land in the run's dataset, ready to download as JSON, CSV, or Excel, or to fetch via API.

The actor paginates through search results automatically (roughly 28 listings per page) and deduplicates listings by internal ID, so you get each property once.

### Cost estimate

The actor uses plain HTTP requests over a residential proxy (no browser), so it stays cheap per result even though the proxy itself costs more than a datacenter IP. It charges $0.005 per run start plus $0.002 per result scraped. 1,000 results costs about $2.01. A run's usage cost only settles after it reports SUCCEEDED — reading the dataset mid-run shows a cost far below the final total.

### Limitations and known issues

- The actor scrapes search-result cards. They contain price, area, bedrooms, location, geo-coordinates, photos, and agency, but not the full listing description beyond the card's summary text or the advertiser's phone number.
- `rooms` is parsed from the "T-number" convention in the property type label (`Apartamento T3` → 3 bedrooms). Listings labeled `T3+1` keep the base number (3) — the `+1` denotes an extra convertible room, not a confirmed bedroom, so it is never added on top.
- `latitude`/`longitude` are `null` when Casa SAPO publishes `(0, 0)` as a placeholder for an obscured location (common on some private-owner listings) — that is treated as "no coordinates," not a real point.
- Casa SAPO does not reject an unrecognized location slug outright — it can silently fall back to a nationwide result set. The actor detects this by checking whether the requested location actually appears in the returned listings and logs a warning if it doesn't, so a typo in `location` doesn't quietly return unrelated listings from anywhere in Portugal.
- `priceMin`/`priceMax` are applied after fetching each page (Casa SAPO's price filter is not a documented URL parameter), so a very narrow price band on a large search can still walk through more pages than the final result count suggests.
- Casa SAPO serves Portuguese property data only. For Portugal's other major portals, see our Imovirtual and Idealista scrapers.

### FAQ

**Do I need a Casa SAPO account?**
No. The actor reads public search results only.

**Which locations are supported?**
Districts, cities, and councils that Casa SAPO's own URL structure recognizes (e.g. `lisboa`, `porto`, `braga`, `faro`, `cascais`). The actor warns in the run log if a location doesn't seem to be recognized.

**Can I get rental prices?**
Yes. Set `transaction` to `alugar` and `price` becomes the monthly rent in EUR.

**Is this legal?**
The actor only collects publicly available listing data, the same information any visitor sees in a browser. You are responsible for how you use the data — respect Casa SAPO's terms of service and applicable data protection laws.

# Actor input Schema

## `location` (type: `string`):

Portuguese district, city, or municipality slug (e.g. 'lisboa', 'porto', 'braga', 'faro', 'cascais'). Leave empty for Lisboa. Unknown locations return 0 results — the actor fails loudly rather than silently returning an unrelated area.

## `transaction` (type: `string`):

Buy (comprar) or rent (alugar) listings.

## `propertyType` (type: `string`):

Type of property to search for. 'casas' covers apartments and houses together.

## `priceMin` (type: `integer`):

Only return listings priced at or above this amount (EUR; monthly rent for alugar).

## `priceMax` (type: `integer`):

Only return listings priced at or below this amount (EUR; monthly rent for alugar).

## `maxResults` (type: `integer`):

Maximum number of listings to scrape. Hard-capped at 20000 (roughly 700 search pages) to keep runs bounded.

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

Proxy settings. Casa SAPO sits behind Cloudflare and rate-limits aggressively on a single IP — Portuguese residential proxies are required for reliable access beyond a handful of requests.

## Actor input object example

```json
{
  "location": "lisboa",
  "transaction": "comprar",
  "propertyType": "casas",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "PT"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "location": "lisboa",
    "transaction": "comprar",
    "propertyType": "casas",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "PT"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/casa-sapo-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 = {
    "location": "lisboa",
    "transaction": "comprar",
    "propertyType": "casas",
    "maxResults": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "PT",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/casa-sapo-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 '{
  "location": "lisboa",
  "transaction": "comprar",
  "propertyType": "casas",
  "maxResults": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "PT"
  }
}' |
apify call studio-amba/casa-sapo-pt-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studio-amba/casa-sapo-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/hXpocE5LiTI7WyrJ5/builds/VkVMkbNpvRUlZOyvO/openapi.json
