# French Physician Directory Database (RPPS) - Doctolib (`jungle_synthesizer/doctolib-practitioner-directory-scraper`) Actor

Crawls Doctolib's France, Germany and Italy practitioner and establishment directories for identity, RPPS/ADELI national IDs, conventionnement sector, published tariffs, languages spoken, diplomas and experience.

- **URL**: https://apify.com/jungle\_synthesizer/doctolib-practitioner-directory-scraper.md
- **Developed by:** [BowTiedRaccoon](https://apify.com/jungle_synthesizer) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 record 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

## France, Germany & Italy Physician Directory Database — Doctolib

Scrape practitioner and clinic profiles from [Doctolib](https://www.doctolib.fr), the booking platform behind most outpatient care in France, Germany and Italy. Returns identity, the RPPS/ADELI national practitioner ID, conventionnement sector, published tariffs, languages spoken and more — for both individual practitioners and establishments across all three countries.

***

### Doctolib Practitioner Directory Scraper Features

- Extracts the RPPS or ADELI national practitioner ID for every French record — the join key into Annuaire Sante, SNDS and other national health datasets.
- Returns conventionnement sector, Carte Vitale acceptance and published per-act tariffs where the practice lists them.
- Covers practitioners and establishments (clinics, labs, centres de sante) across doctolib.fr, doctolib.de and doctolib.it from one run.
- Collects languages spoken, diplomas, professional experience and payment methods per profile.
- Flags whether a listing is a paying, bookable Doctolib customer or a directory-only entry — useful for scoring lead quality before you call anyone.
- Runs against the full site sitemap, not a search query, so results aren't capped by what a single search term happens to surface.

***

### Who Uses Doctolib Practitioner Data?

- **Healthcare analytics teams** — join RPPS-tagged records into SNDS or Annuaire Sante datasets for prescriber-level analysis.
- **Medical device and pharma sales** — build territory lists filtered by speciality and city, or at least the raw material for one.
- **Health-tech vendors** — identify practices without an online booking system as sales leads for practice-management software.
- **Insurance and billing analysts** — track conventionnement sector and Carte Vitale acceptance across a region.
- **Market researchers** — measure tariff ranges for a given procedure across cities or specialities.
- **Recruiters and staffing firms** — source practitioners by speciality and language spoken.

***

### How Doctolib Practitioner Directory Scraper Works

1. Pick which of the three country domains to crawl — fr, de, it, or all three — and set a record limit.
2. The scraper walks Doctolib's sitemap to discover every practitioner and establishment profile URL in scope.
3. Each profile page is parsed for identity, national IDs, conventionnement/tariff data, and the rest of the field set below.
4. Results land in your dataset as they're collected, so you can start using them before a large run finishes.

***

### Input

```json
{
  "maxItems": 200,
  "countries": ["fr", "de"]
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `maxItems` | integer | `10` | Maximum number of practitioner/establishment records to return. `0` = unlimited — pair with `resumeCursor` for a full-corpus crawl across multiple runs. |
| `countries` | array | `["fr", "de", "it"]` | Which Doctolib national domains to crawl. Leave empty for all three — France, Germany and Italy share the same sitemap layout and profile template, so widening costs nothing extra. |

#### Resuming a large crawl

Every run emits a `resumeCursor` in its Output. If a large crawl stops before it finishes — because it hit `maxItems`, your spend cap (`maxTotalChargeUsd`), or was aborted — start a new run with **the same input** plus that `resumeCursor` to continue from where it left off. The crawl resumes from the queued work the previous run didn't reach.

- You are **not re-charged** for records the earlier run already delivered.
- Resume within your account's run-retention window — on the free tier, roughly your 10 most recent runs. Once the source run is pruned, its `resumeCursor` is no longer valid.
- `resumeCursor` is opaque — supply it unmodified.

***

### Doctolib Practitioner Directory Scraper Output Fields

```json
{
  "country": "fr",
  "profile_url": "https://www.doctolib.fr/medecin-generaliste/paris/anne-moga",
  "profile_slug": "anne-moga",
  "rpps": "10000378991",
  "adeli": "751497041",
  "full_name": "Anne Vaillant Moga",
  "name_with_title": "Dr Anne Vaillant Moga",
  "speciality": "Medecin generaliste",
  "speciality_slug": "medecin-generaliste",
  "secondary_specialities": ["Acne", "Allergie", "Apnee du sommeil"],
  "practice_addresses": ["Dr Anne Moga (Paris) | 76 avenue Raymond Poincare, 75116 Paris |"],
  "phone": null,
  "convention_sector": "Conventionne secteur 2",
  "accepts_carte_vitale": true,
  "payment_methods": ["Especes", "cartes bancaires", "virement bancaire"],
  "languages_spoken": ["Anglais", "Francais"],
  "insurance_agreements": [],
  "online_booking_available": true,
  "video_consultation": true,
  "new_patients_accepted": true,
  "price_list": ["Consultation prealable de medecine esthetique: de 50 EUR a 80 EUR"],
  "diplomas": ["2013: Implants de cheveux synthetiques - Medicap"],
  "professional_experience": ["depuis 2025: Cabinet - Epinay-sur-Orge"],
  "association_memberships": [],
  "is_doctolib_customer": true,
  "establishment_type": null,
  "scraped_at": "2026-09-01T18:28:43.124Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `country` | string | Country domain the record was crawled from (`fr`, `de`, `it`). |
| `profile_url` | string | Canonical Doctolib profile URL. |
| `profile_slug` | string | Last path segment of the profile URL. |
| `rpps` | string | French national practitioner ID (Repertoire Partage des Professionnels de Sante). |
| `adeli` | string | Legacy French practitioner ID, used where RPPS is absent (paramedical professions). |
| `full_name` | string | Practitioner or establishment name, no title. |
| `name_with_title` | string | Display name including title (e.g. "Dr", "M."). |
| `speciality` | string | Primary medical speciality or establishment category. |
| `speciality_slug` | string | URL slug of the primary speciality. |
| `secondary_specialities` | array | Additional procedures or services listed on the profile. |
| `practice_addresses` | array | Practice location(s), formatted as `name \| street, postal city \| phone`. |
| `phone` | string | Primary practice phone number. |
| `convention_sector` | string | French conventionnement sector (e.g. "Conventionne secteur 1/2", "Non conventionne"). |
| `accepts_carte_vitale` | boolean | Whether the practice accepts Carte Vitale tele-transmission. |
| `payment_methods` | array | Accepted payment methods. |
| `languages_spoken` | array | Languages spoken at the practice. |
| `insurance_agreements` | array | German insurance agreements (gesetzlich / privat), where published. |
| `online_booking_available` | boolean | Whether the profile has a live Doctolib booking widget. |
| `video_consultation` | boolean | Whether teleconsultation is offered. |
| `new_patients_accepted` | boolean | Whether the practice is accepting new patients via Doctolib. |
| `price_list` | array | Published per-act tariffs, formatted as `act name: price range`. |
| `diplomas` | array | Diplomas/formations, formatted as `year: description`. |
| `professional_experience` | array | Professional experience periods, formatted as `period: description`. |
| `association_memberships` | array | Published works, publications or professional affiliations. |
| `is_doctolib_customer` | boolean | Whether the practice has a paying, bookable Doctolib agenda — `false` means a directory-only listing. |
| `establishment_type` | string | Establishment category for non-individual profiles (e.g. laboratoire, clinique) — empty for individual practitioners. |
| `scraped_at` | string | Timestamp the record was collected. |

***

### FAQ

#### How do I scrape Doctolib practitioner data?

Doctolib Practitioner Directory Scraper needs no account and no API key. Set `maxItems` and, optionally, which countries to cover — the default is all three — and it returns practitioner and establishment records as structured JSON.

#### What is RPPS and why does it matter?

RPPS (Repertoire Partage des Professionnels de Sante) is the French national practitioner identifier. It's the field that lets you join Doctolib records into Annuaire Sante, SNDS prescriber data, and most other French health datasets — a plain name-and-city listing can't do that.

#### Can I filter by country?

Yes. Pass `countries: ["fr"]`, `["de"]`, `["it"]`, or any combination. Leave it empty and you get all three — the sitemap layout and profile template are identical across domains.

#### Does this include establishments, or just individual practitioners?

Both. Establishment profiles (clinics, labs, centres de sante) come back with `establishment_type` set; individual practitioner records leave that field empty.

#### Do I need an account or API key for Doctolib?

No. Doctolib Practitioner Directory Scraper works against publicly listed profile pages — no login, no API key, no scraping credentials to manage.

***

### Need More Features?

Need custom fields, filters, or a different target site? [File an issue](https://console.apify.com/actors/issues) or get in touch.

### Why Use Doctolib Practitioner Directory Scraper?

- **RPPS included** — the national practitioner ID that turns a name-and-address list into a joinable spine for French healthcare analytics.
- **Three countries, one run** — France, Germany and Italy share a sitemap layout and profile template, so covering all three costs nothing extra over covering one.
- **Richer than a search-page scrape** — profile pages carry conventionnement sector, Carte Vitale acceptance and per-act tariffs that a search results list never shows.

# Actor input Schema

## `sp_intended_usage` (type: `string`):

What will this data feed? E.g. lead lists, KYB checks, price tracking.

## `sp_improvement_suggestions` (type: `string`):

Provide any feedback or suggestions for improvements.

## `sp_contact` (type: `string`):

We'll personally help with your use case. No spam.

## `resumeCursor` (type: `string`):

Leave empty for a fresh crawl. To CONTINUE a previous run where it stopped — without paying again for records you already received — paste the `resumeCursor` value from that run's Output (the run's OUTPUT key). Resume promptly: the previous run's data expires with your account's retention window (free tier: your ~10 most recent runs).

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

Maximum number of practitioner/establishment records to scrape. 0 = unlimited (resumeCursor lets a full-corpus crawl continue across runs).

## `countries` (type: `array`):

Doctolib national domains to crawl. Leave empty to crawl all three (fr, de, it) — the sitemap layout and profile template are identical across domains, so widening costs nothing extra per run.

## Actor input object example

```json
{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "maxItems": 10,
  "countries": [
    "fr",
    "de",
    "it"
  ]
}
```

# 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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "maxItems": 10,
    "countries": [
        "fr",
        "de",
        "it"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jungle_synthesizer/doctolib-practitioner-directory-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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "maxItems": 10,
    "countries": [
        "fr",
        "de",
        "it",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("jungle_synthesizer/doctolib-practitioner-directory-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 '{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "maxItems": 10,
  "countries": [
    "fr",
    "de",
    "it"
  ]
}' |
apify call jungle_synthesizer/doctolib-practitioner-directory-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jungle_synthesizer/doctolib-practitioner-directory-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/N7FzqPtzzNkn0MZEG/builds/2LIQsZKkMwJuvyfaP/openapi.json
