# BureauxLocaux Scraper - French Commercial Property (`studio-amba/bureauxlocaux-scraper`) Actor

Scrape commercial property listings from BureauxLocaux.com, the French portal for immobilier d'entreprise: bureaux, locaux commerciaux, entrepôts, coworking and terrains for rent or sale. Rent per m2, sale prices, surfaces, agency contacts.

- **URL**: https://apify.com/studio-amba/bureauxlocaux-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/platform/actors/running/actors-in-store#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

## BureauxLocaux Scraper

Scrape commercial property listings from [BureauxLocaux.com](https://www.bureauxlocaux.com), one of the largest French portals for immobilier d'entreprise. The portal carries around 63,000 active listings from professional agencies across France: offices (bureaux), retail space (locaux commerciaux, boutiques), warehouses and industrial units (entrepôts, locaux d'activités), coworking space and commercial land (terrains), for rent and for sale. Paris inventory is particularly deep, with several thousand office listings alone.

Each result comes from the portal's own structured data feed, so you get clean numbers instead of parsed page text: rent in EUR per m2 per year (the standard French commercial quote), monthly rents, sale prices, surfaces, divisibility, full addresses, agency names and phone numbers, photos and building characteristics.

### Why use it

- **Market research** — track asking rents per m2 and sale prices per city, arrondissement or department across France.
- **Lead generation** — every listing includes the agency name and phone number.
- **Site selection** — filter offices, retail or warehouse space by location and surface, and compare rent per m2 across markets.
- **Investment screening** — sale listings include the asking price and a computed price per m2.
- **Monitoring** — run it on a schedule and diff against yesterday's dataset to catch new listings the day they appear.

### How to scrape BureauxLocaux data

1. Set **Location** to a French city (`Paris`, `Lyon`, `Bordeaux`), an arrondissement (`Paris 8`, `Marseille 12`), a 5-digit postal code (`75008`), or a department name or number (`Hauts-de-Seine`, `92`). Accents are optional (`Orléans` and `Orleans` both work). Leave it empty to scrape the whole country.
2. Pick a **Listing Type**: rent (location), sale (vente) or both.
3. Pick a **Property Segment**: office, retail, warehouse & industrial, coworking, land, or all segments.
4. Optionally set **Min/Max Surface** in m2.
5. Set **Max Results** and run. Results land in the dataset as one flat JSON object per listing.

Example: bureaux à louer in Paris:

```json
{
    "searchQuery": "Paris",
    "listingType": "rent",
    "segment": "office",
    "maxResults": 200
}
```

Example: entrepôts and locaux d'activités around Lyon, 200 m2 or more:

```json
{
    "searchQuery": "Lyon",
    "listingType": "rent",
    "segment": "warehouse",
    "minSurface": 200,
    "maxResults": 100
}
```

Example: locaux commerciaux for sale in Bordeaux:

```json
{
    "searchQuery": "Bordeaux",
    "listingType": "sale",
    "segment": "retail",
    "maxResults": 100
}
```

An empty input `{}` works as well — it returns listings from all of France up to the default cap of 100.

### Input options

| Field | Type | Description |
|-------|------|-------------|
| `searchQuery` | string | City, arrondissement (`Paris 8`), postal code (`75008`) or department (`Hauts-de-Seine`, `92`). Empty = all of France. |
| `listingType` | string | `all` (default), `rent` (location) or `sale` (vente). |
| `segment` | string | `all` (default), `office`, `retail`, `warehouse`, `coworking`, `land`. `warehouse` covers both entrepôts and locaux d'activités, as the site groups them. |
| `minSurface` | integer | Minimum floor area in m2. |
| `maxSurface` | integer | Maximum floor area in m2. |
| `maxResults` | integer | Cap on returned listings. Default 100, hard maximum 60,000. |
| `proxyConfiguration` | object | Apify proxy settings. French residential proxy is the default. |

If the location is not recognized by the portal, the run fails with a clear message instead of silently returning listings from all of France.

### Output fields

| Field | Example | Description |
|-------|---------|-------------|
| `title` | `"Location bureau Paris 75008"` | Listing headline |
| `segment` | `"office"` | `office`, `retail`, `warehouse`, `activity`, `coworking`, `land`, `ephemeral-retail` |
| `listingType` | `"rent"` | `rent`, `sale`, or `rent+sale` for dual listings |
| `price` | `850` | Rent: EUR per m2 per year. Sale: total EUR. Null when price on request |
| `priceUnit` | `"EUR/m2/an"` | `EUR/m2/an` for rentals, `EUR` (total) for sales |
| `currency` | `"EUR"` | Always EUR |
| `priceOnRequest` | `false` | True when the advertiser publishes no price at all |
| `rentPerM2Year` | `850` | Rent in EUR per m2 per year (rentals only) |
| `monthlyRent` | `5454.17` | Advertised monthly rent in EUR. For divisible buildings this corresponds to the smallest available lot |
| `salePrice` | `1200000` | Total asking price in EUR (sales only) |
| `salePricePerM2` | `4854.37` | Computed sale price per m2 |
| `seatPrice` / `numSeats` | `390` / `24` | Coworking listings |
| `surface` | `5711` | Total floor area in m2 |
| `minimalSurface` | `2000` | Smallest divisible lot in m2 |
| `isDivisible` | `true` | Space can be split into smaller lots |
| `address` | `"88-90 Rue Cardinet, 75017 Paris"` | Display address |
| `street` / `city` / `zipCode` / `department` | `"Rue Cardinet"` / `"Paris"` / `"75017"` / `"75"` | Address parts |
| `characteristics` | `{"has_optic_fiber": true}` | Raw building characteristics as published |
| `agencyName` | `"Savills"` | Agency or broker |
| `contactName` / `phone` | `"Jean Dupont"` / `"06 12 34 56 78"` | Listing contact |
| `buildingName` | `"SOLAIR"` | Branded office buildings |
| `imageUrl` / `imageUrls` | `"https://imagescdn.bureauxlocaux.com/..."` | Photos |
| `virtualTourUrls` | `["https://my.matterport.com/..."]` | Virtual tours when published |
| `adLevel` | `4` | Portal ad placement level (higher = paid placement) |
| `url` | `"https://www.bureauxlocaux.com/annonce/..."` | Listing on the portal |
| `id` | `38852046` | Portal listing id |
| `source` | `"bureauxlocaux.com"` | Data source |
| `scrapedAt` | `"2026-08-18T10:00:00.000Z"` | Scrape timestamp |

### Example output

```json
{
    "id": 41747344,
    "title": "Location bureau Paris 75001",
    "segment": "office",
    "listingType": "rent",
    "price": 692.31,
    "priceUnit": "EUR/m2/an",
    "currency": "EUR",
    "priceOnRequest": false,
    "rentPerM2Year": 692.31,
    "monthlyRent": 7500,
    "salePrice": null,
    "surface": 130,
    "minimalSurface": null,
    "isDivisible": false,
    "address": "Rue du Louvre, 75001 Paris",
    "street": "Rue du Louvre",
    "city": "Paris",
    "zipCode": "75001",
    "department": "75",
    "agencyName": "Arthur Loyd",
    "phone": "01 44 88 51 51",
    "imageUrl": "https://imagescdn.bureauxlocaux.com/i2/...",
    "url": "https://www.bureauxlocaux.com/annonce/location-bureau-paris-75001--41747344",
    "source": "bureauxlocaux.com",
    "scrapedAt": "2026-08-18T10:00:00.000Z"
}
```

### Pricing and cost estimate

The actor charges a small start fee plus a fee per result. On top of that, standard Apify platform usage applies (compute and proxy). Runs are light: the actor reads the portal's JSON feed directly, 60 listings per request, no browser.

As a working number, expect roughly **$0.005-0.006 per listing all-in** at the default settings. A 500-listing run is around $3, and a full-country office snapshot (~21,000 rental listings) stays in the tens of dollars, mostly per-result fees.

Note on cost timing: a run's usage cost only settles after the run reports SUCCEEDED. Reading the cost of a run that is still in progress shows a number far below the final one — don't size budgets off a partial run.

### Limitations

- `price` is null when an advertiser lists "prix sur demande" — this is the portal's data, not a scrape failure. In commercial real estate this is common; expect roughly 10% of rental listings without a published rent figure.
- `monthlyRent` is the figure as advertised. For divisible buildings it corresponds to the smallest available lot, not the total surface — use `rentPerM2Year` times `surface` for the full-building figure.
- The search feed does not carry publication dates or coordinates; those only exist on detail pages, which this actor does not fetch.
- Regions (e.g. Île-de-France) are not a supported location filter on the portal — use a department or run per city instead.
- The portal groups entrepôts and locaux d'activités into one warehouse segment; the output `segment` field still tells them apart per listing.

### Fair use

The actor scrapes only publicly visible listing data at a polite request rate. Respect BureauxLocaux's terms of service and French law when using the data, especially agency contact details.

# Actor input Schema

## `searchQuery` (type: `string`):

French city ('Paris', 'Lyon', 'Bordeaux'), arrondissement ('Paris 8'), 5-digit postal code ('75008'), or department name or number ('Hauts-de-Seine', '92'). Accents optional. Leave empty to search all of France.

## `listingType` (type: `string`):

Rent (location), sale (vente), or both.

## `segment` (type: `string`):

Commercial property segment. 'Warehouse' covers both entrepôts and locaux d'activités, as the site groups them.

## `minSurface` (type: `integer`):

Only listings with at least this floor area in m2.

## `maxSurface` (type: `integer`):

Only listings with at most this floor area in m2.

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

Maximum number of listings to return. The whole portal holds roughly 63,000 listings.

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

Proxy settings. The site sits behind Cloudflare (currently passive on the search API, see docs/site-recon/bureauxlocaux.md) — French residential proxy is the reliable default.

## Actor input object example

```json
{
  "searchQuery": "Paris",
  "listingType": "rent",
  "segment": "office",
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}
```

# 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 = {
    "searchQuery": "Paris",
    "listingType": "rent",
    "segment": "office",
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "FR"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("studio-amba/bureauxlocaux-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 = {
    "searchQuery": "Paris",
    "listingType": "rent",
    "segment": "office",
    "maxResults": 50,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "FR",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("studio-amba/bureauxlocaux-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 '{
  "searchQuery": "Paris",
  "listingType": "rent",
  "segment": "office",
  "maxResults": 50,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "FR"
  }
}' |
apify call studio-amba/bureauxlocaux-scraper --silent --output-dataset

```

## MCP server setup

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