# SeLoger Scraper — France Real Estate Listings | $1.19/1K (`bovi/seloger-listings-scraper`) Actor

Scrape SeLoger.com (France's #1 real estate portal) from a search-results URL you paste. Returns title, price, transaction type, property type, rooms, surface m², location, agency contact, photos, and listing URL.

- **URL**: https://apify.com/bovi/seloger-listings-scraper.md
- **Developed by:** [Vitalii Bondarev](https://apify.com/bovi) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.10 / 1,000 seloger scraper — france real estate listings | $1.19/1ks

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?

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

## SeLoger Listings Scraper

Scrape **SeLoger.com** (France's #1 real estate portal) from a search-results URL you paste — no need to configure filters in this actor, just set them on seloger.com and copy the resulting URL.

### Features

- Scrape any SeLoger search (sale or rent, any city/price/room filter you set on their site)
- Returns per-listing: price, transaction type, property type, rooms/bedrooms, surface m², location, agency/contact, photos, and listing URL
- Uses SeLoger's own embedded search-results JSON (`window["initialData"]`) — no HTML scraping, no browser
- **Pay per result** — you only pay for listing records actually extracted
- Automatic pagination up to `maxItems`, with residential-proxy session rotation on a block

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `searchUrl` | string | *(required)* | A seloger.com search-results URL. Set your filters on seloger.com, then copy the URL from your browser. |
| `maxItems` | integer | 100 | Max listings to return (0 = unlimited) |
| `proxyConfiguration` | proxy | RESIDENTIAL FR | Apify Residential proxy, France — required for reliable access |

#### Why a search URL instead of filter fields?

SeLoger's location filter uses opaque internal place/geo IDs, not plain postal codes or city names — several of the Store's own traction-proven SeLoger scrapers (`azzouzana/seloger-mass-products-scraper-by-search-url`, `memo23/seloger-scraper`) use the same "paste your search URL" contract for exactly this reason. Guessing the ID scheme wrong would silently return 0 or wrong-city results, which is worse than asking the buyer to copy a URL.

### Output fields

| Field | Description |
|-------|-------------|
| `listing_id` | SeLoger internal listing ID |
| `url` | Direct URL to the listing (may be `null` — see caveats) |
| `title` / `description` | Listing headline / summary |
| `transaction_type` | `sale` or `rent` |
| `property_type` | `apartment`, `house`, etc. |
| `price` / `price_per_m2` / `monthly_price` | EUR |
| `rooms` / `bedrooms` / `surface_m2` / `floor` | |
| `energy_class` / `ges_class` | DPE/GES letter grade (search-card level; full detail needs the listing page) |
| `city` / `district` / `postal_code` / `department` | |
| `latitude` / `longitude` | |
| `agency_id` / `agency_name` / `agency_phone` / `agency_link` | `agency_name` is the contact display name shown on the search card, which may be an individual agent rather than the agency brand |
| `photos` / `photo_count` / `virtual_visit_url` | |
| `is_new` / `is_exclusive` / `tags` | |
| `publication_date` | |
| `page_number` / `source` / `parse_confidence` / `warnings` / `scraped_at` | Provenance/QA fields |

### Access path

SeLoger has no public search API. Its search-results page is server-rendered and ships the full result set as a double-encoded JSON literal inside an inline `<script>` tag: `window["initialData"] = JSON.parse("...")` — the argument is itself a JSON-encoded string, so it decodes twice (JS-string-unescape, then `json.loads`). This actor fetches that page with `curl_cffi` (Chrome TLS impersonation, no browser) and decodes it directly — no HTML/CSS-selector parsing on the happy path.

Third-party scraping vendors document SeLoger as running anti-bot fingerprinting/CAPTCHA challenges (DataDome-class) against non-residential traffic, so **Apify RESIDENTIAL (FR) proxy is required** for production reliability, per this fleet's access-ladder convention (see `_shared/transport.py`).

### Honest caveats (read before relying on this in production)

- **Field-name mapping is unverified against a live SeLoger response.** The `initialData` field names used here (`price`, `surface`, `cardType`, `cityLabel`, `zipCode`, `contactName`, `phoneNumber`, `classifiedURL`, …) come from ScrapFly's public engineering write-up on scraping SeLoger, not from an authenticated cloud run against the live site (out of scope for this build — no live/paid access path was exercised). `normalize.py` is defensive by design: every field falls back to `None` and deducts `parse_confidence` rather than raising, so a wrong/renamed key degrades gracefully instead of crashing the run — but the exact field set should be spot-checked against a real cloud run (with a real RESIDENTIAL FR proxy) before this actor is published or relied on for paid delivery.
- **The exact top-level "cards" container key is unconfirmed.** The parser locates the listing array by a tolerant structural walk (first list of dicts that "look like" listing cards — has an `id` plus signal fields), not a hardcoded dotted path, specifically because the wrapper key wasn't confirmed live. This is the same tolerant-fallback pattern already used by `immoscout24-listings` in this fleet.
- **`url` is never fabricated.** If no URL-shaped field is found on a card, `url` is `null` with a `url_missing` warning rather than guessing a possibly-wrong deep-link scheme.
- **Full DPE/GES, exact address, and co-ownership charges live on the detail page**, not the search-card payload — this actor is search-summary-only (matching the sibling `immoweb-properties-scraper`'s pre-`fetchDetails` scope). Detail-page enrichment (also JSON-embedded, via `__NEXT_DATA__` on the property page per the same ScrapFly source) is a straightforward v2 addition once the search-card path is verified live.
- Termination is by empty-page detection (not a parsed `totalItems` count), since the exact total-count key name is also unconfirmed — this trades a slightly less precise pagination estimate for correctness under an unverified schema.

### Tests

Unit tests (`tests/`) run entirely offline against synthetic fixtures — no live network calls: `cd actors/REAL_ESTATE/seloger-listings-scraper && python3 -m pytest -q`.

# Actor input Schema

## `searchUrl` (type: `string`):

Paste a seloger.com search-results URL (go to seloger.com, set your location/price/rooms/property-type filters, then copy the resulting URL from your browser's address bar, e.g. 'https://www.seloger.com/list.htm?projects=1\&types=1\&places=...'). SeLoger's location filter uses internal place IDs, not plain postal codes, so filters are set on their site, not in this actor's input.

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

Maximum number of listings to return. Set to 0 for all available results on the search. Default 100.

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

Required for reliable access: Apify Residential proxy with country France (FR). SeLoger runs anti-bot fingerprinting/CAPTCHA challenges (DataDome-class) that block datacenter IPs.

## Actor input object example

```json
{
  "searchUrl": "https://www.seloger.com/immobilier/achat/immo-paris-75/",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}
```

# Actor output Schema

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

Dataset containing SeLoger Listings Scraper records (title, price, transaction\_type, property\_type, rooms, surface\_m2, city, postal\_code, agency\_name, photo\_count, url, scraped\_at).

# 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 = {
    "searchUrl": "https://www.seloger.com/immobilier/achat/immo-paris-75/",
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "FR"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("bovi/seloger-listings-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 = {
    "searchUrl": "https://www.seloger.com/immobilier/achat/immo-paris-75/",
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "FR",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("bovi/seloger-listings-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 '{
  "searchUrl": "https://www.seloger.com/immobilier/achat/immo-paris-75/",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}' |
apify call bovi/seloger-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bovi/seloger-listings-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/ti1ScizWaQeuRU5mT/builds/u2b5SflsaHVXAPCds/openapi.json
