# Doctolib (`anchor/doctolib`) Actor

Scraping Doctolib is now super easy and cheap! Extract phones, names, contact, timings, image and addresses of medics, doctors, hospitals... Best part : you can even customize what info to extract from Doctolib!

- **URL**: https://apify.com/anchor/doctolib.md
- **Developed by:** [Anchor](https://apify.com/anchor) (community)
- **Categories:** Automation
- **Stats:** 244 total users, 2 monthly users, 85.7% runs succeeded, 5 bookmarks
- **User rating**: 3.25 out of 5 stars

## Pricing

$9.00/month + usage

To use this Actor, you pay a monthly rental fee to the developer. The rent is subtracted from your prepaid usage every month after the free trial period.You also pay for the Apify platform usage, which gets cheaper the higher Apify subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#rental-actors

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Doctolib

Data can be exported to various formats such as JSON or CSV. You have to option to **customise the information you want to extract** by modifying the javascript function which is executed on each page.

### 💡 Why scrape Doctolib ?
Doctolib has thousands of doctors and health centers. **Extracting information from Doctolib.fr** is a great source of data for business, recruiting or research.

Here are just some of the ways you could use that data:

- use your own filters to find a doctors
- hunt a doctor to recruit him in your health center
- enrich your lead database with Doctors or Medics for prospection
If you would like more inspiration on how scraping Doctolib could help your business or organization, check out [this page](https://apify.com/industries/healthcare-and-pharma).


### 📊 Results
It will look like that if you search for medic like "médecin généraliste" in Paris

| nom                 | tarif       | description       | horaire et contact       | specialite       | expertise       | phone       | URL       | Website  |
|-------------------|------------------------------|----------------|----------------|----------------|----------------|----------------|----------------|----------------|
| Frederic Bidon | Chèque, espèce et carte bancaire | Le médecin généraliste reçoit les enfants de plus de sept ans et les adultes pour tous types de soins médicaux généraux. | tous les jours. 9h-18h       | généraliste       | Vaccination Covid19       | 01 45 05 66 77 88       | https://www.doctolib.fr/medecin-generaliste/paris/frederic-bidon | https://justpageit.lt |


### 🔍 What does Doctolib Scraper do?
The web interface of Doctolib is not enoug for you ? You need more results, and maybe different filters ? Doctolib website is good, but does not fit everyone. However, it is the most widespread platform for mdeical networking, prospecting...

This Doctolib Scrapper will extract information you need. It's a simple.

- No limits
- Exauhstive results
- No authentification required
- API available
- stealth mode IP

[![Watch the video](https://i.postimg.cc/s2JTzCHT/billgates.png)](https://youtu.be/fBk1bnE5vB4)

### 👨🏻‍🎓 How do I crawl Doctolib?

It's easy to scrape Doctolib. Just follow these few steps and you'll get your data in a few minutes.

- On your laptop, simply go to Doctolib and make a search, and location you want if necessary. Dont' forget to launch the search  
- Then copy the URL of first page of search results, and paste it in the start-URL of this Actor. Please add the root url first, so that it mimics the user behaviour.
- If you encounter issues, set up a french residential proxy. It helps because Doctolib bans non-french request from its website. Go to "Advanced Configuration"
- Click _Start_

Then wait for the result to appear in the "dataset" section and download them.

### ⚙️ Advanced Configuration

- Max pages : it's a limit, to prevent the actor from crawling too many pages.
- Function : It's the function extracting the result on each Doctolib.fr pages. If the default is not enough for you, just update it using 
- <a href="https://www.w3schools.com/jquery/jquery_ref_selectors.asp">jQuery selectors</a> to fetch other piece of information from Doctolib.fr !

### 🫰🏻 How much will it cost me to scrape Doctolib ?
Apify provides you with $5 free usage credits to use every month on the Apify Free plan and you can get up to 20,000 Doctolib results for those credits. So 20,000 results will be completely free!

But if you need to get more data or to get your data regularly you should grab an [Apify subscription](https://apify.com/pricing). We recommend our $49/month Personal plan - you can get up to 200,000 Doctolib results every month with the $49 monthly plan! Or 2 million with $499 Team plan, wow!

# Actor input Schema

## `startUrls` (type: `array`):

Search URLs to start with. We automatically do the pagination. Works for France 🇫🇷, Germany 🇩🇪 and Italy 🇮🇹
## `maxPagesPerCrawl` (type: `integer`):

Maximum number of pages before we stop. This limit prevents excess platform usage for misconfigured scrapers.<br><br><code>0</code> is no limit.<br><br><code>10</code> would stop after 10 pages for instance.
## `hideSearchPages` (type: `boolean`):

If true, search pages data won't show in the result dataset. Useful if you are only interested by Doctor pages, not in the search URLs
## `pageFunction` (type: `string`):

JavaScript function executed on every page (inside the browser). Customise it if necessary. <br><br>For details, see <a href='https://playwright.dev/docs/api/class-page' target='_blank' rel='noopener'>See Playwright page, available as 'context.page'</a> 

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.doctolib.fr/infectiologue/75001-paris"
    }
  ],
  "maxPagesPerCrawl": 10,
  "hideSearchPages": true,
  "pageFunction": "async function pageFunction(context) {\n\n    let data = {}\n    let userData = context.request.userData\n    data.url = context.request.url\n\n    const isDoctorProfile = userData && userData.label === 'doctor'\n    let isProbablyDoctorProfile\n    if(!isDoctorProfile){\n        isProbablyDoctorProfile = await context.innerTextwrapper(context,'body.profiles, body.online_booking-drafts')\n    }\n    const isDoctorPage = isDoctorProfile || isProbablyDoctorProfile\n\n    data.isDoctorPage = isDoctorPage\n    \n    if(isDoctorPage){\n        context.log.info(`Doctor page ${isDoctorProfile ? 'from search' : '' } ${isProbablyDoctorProfile ? 'guessing' : '' }`);\n        data.nom = await context.page.locator('#main-content h1').innerText({timeout:6000})\n        data.tarif = await context.innerTextwrapper(context,'#payment_means')\n        data.horaire_contact = await context.innerTextwrapper(context,'#openings_and_contact')\n        data.description = await context.innerTextwrapper(context,'.dl-profile-bio')\n        data.specialite = await context.innerTextwrapper(context,'.dl-profile-header-speciality')\n        data.expertise = await context.innerTextwrapper(context,'#skills')\n        try{\n            data.website = await context.page.locator('.dl-profile-row-section div', { hasText: 'Website' }).locator('a').getAttribute('href',{timeout:2000})\n        }catch(e){\n            context.log.info('Website not found',e);     \n        }\n\n        try{\n            data.phones = await context.getPhones(data.horaire_contact)\n        }catch(e){\n            context.log.info('Phones not found',e);     \n        }\n        try{\n            data.image = await context.page.locator('.dl-profile img').first().getAttribute('src',{timeout:2000})\n            if(data.image.startsWith('/')){ data.image = 'https:' + data.image}\n        }catch(e){\n            context.log.info('Image not found',e);     \n        }        \n    }else{\n        context.log.info('we are not on a doctor page: so a search or pagination page.');\n        data.message = 'you can remove these rows in the settings with \"hideSearchPages\" '\n        userData.label = 'doctor';\n        const elements = context.page.locator('.dl-card a[href]');\n        const links = await elements.evaluateAll(elems => elems.map(elem => elem.getAttribute('href')));\n        let extenstion = 'fr'\n        if(context.request.url.includes('doctolib.de')){ extenstion = 'de' }\n        if(context.request.url.includes('doctolib.it')){ extenstion = 'it' }\n        links.forEach(async link => {\n            if(link.startsWith('/')){ link = `https://www.doctolib.${extenstion}${link}` }\n            await context.enqueueRequest(link, userData , true);\n        })\n\n    }\n    context.log.info(`ending this page now`);\n\n    return data;\n}\n"
}
````

# 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 = {
    "startUrls": [
        {
            "url": "https://www.doctolib.fr/infectiologue/75001-paris"
        }
    ],
    "pageFunction": async function pageFunction(context) {
    
        let data = {}
        let userData = context.request.userData
        data.url = context.request.url
    
        const isDoctorProfile = userData && userData.label === 'doctor'
        let isProbablyDoctorProfile
        if(!isDoctorProfile){
            isProbablyDoctorProfile = await context.innerTextwrapper(context,'body.profiles, body.online_booking-drafts')
        }
        const isDoctorPage = isDoctorProfile || isProbablyDoctorProfile
    
        data.isDoctorPage = isDoctorPage
        
        if(isDoctorPage){
            context.log.info(`Doctor page ${isDoctorProfile ? 'from search' : '' } ${isProbablyDoctorProfile ? 'guessing' : '' }`);
            data.nom = await context.page.locator('#main-content h1').innerText({timeout:6000})
            data.tarif = await context.innerTextwrapper(context,'#payment_means')
            data.horaire_contact = await context.innerTextwrapper(context,'#openings_and_contact')
            data.description = await context.innerTextwrapper(context,'.dl-profile-bio')
            data.specialite = await context.innerTextwrapper(context,'.dl-profile-header-speciality')
            data.expertise = await context.innerTextwrapper(context,'#skills')
            try{
                data.website = await context.page.locator('.dl-profile-row-section div', { hasText: 'Website' }).locator('a').getAttribute('href',{timeout:2000})
            }catch(e){
                context.log.info('Website not found',e);     
            }
    
            try{
                data.phones = await context.getPhones(data.horaire_contact)
            }catch(e){
                context.log.info('Phones not found',e);     
            }
            try{
                data.image = await context.page.locator('.dl-profile img').first().getAttribute('src',{timeout:2000})
                if(data.image.startsWith('/')){ data.image = 'https:' + data.image}
            }catch(e){
                context.log.info('Image not found',e);     
            }        
        }else{
            context.log.info('we are not on a doctor page: so a search or pagination page.');
            data.message = 'you can remove these rows in the settings with "hideSearchPages" '
            userData.label = 'doctor';
            const elements = context.page.locator('.dl-card a[href]');
            const links = await elements.evaluateAll(elems => elems.map(elem => elem.getAttribute('href')));
            let extenstion = 'fr'
            if(context.request.url.includes('doctolib.de')){ extenstion = 'de' }
            if(context.request.url.includes('doctolib.it')){ extenstion = 'it' }
            links.forEach(async link => {
                if(link.startsWith('/')){ link = `https://www.doctolib.${extenstion}${link}` }
                await context.enqueueRequest(link, userData , true);
            })
    
        }
        context.log.info(`ending this page now`);
    
        return data;
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("anchor/doctolib").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 = {
    "startUrls": [{ "url": "https://www.doctolib.fr/infectiologue/75001-paris" }],
    "pageFunction": """async function pageFunction(context) {

    let data = {}
    let userData = context.request.userData
    data.url = context.request.url

    const isDoctorProfile = userData && userData.label === 'doctor'
    let isProbablyDoctorProfile
    if(!isDoctorProfile){
        isProbablyDoctorProfile = await context.innerTextwrapper(context,'body.profiles, body.online_booking-drafts')
    }
    const isDoctorPage = isDoctorProfile || isProbablyDoctorProfile

    data.isDoctorPage = isDoctorPage
    
    if(isDoctorPage){
        context.log.info(`Doctor page ${isDoctorProfile ? 'from search' : '' } ${isProbablyDoctorProfile ? 'guessing' : '' }`);
        data.nom = await context.page.locator('#main-content h1').innerText({timeout:6000})
        data.tarif = await context.innerTextwrapper(context,'#payment_means')
        data.horaire_contact = await context.innerTextwrapper(context,'#openings_and_contact')
        data.description = await context.innerTextwrapper(context,'.dl-profile-bio')
        data.specialite = await context.innerTextwrapper(context,'.dl-profile-header-speciality')
        data.expertise = await context.innerTextwrapper(context,'#skills')
        try{
            data.website = await context.page.locator('.dl-profile-row-section div', { hasText: 'Website' }).locator('a').getAttribute('href',{timeout:2000})
        }catch(e){
            context.log.info('Website not found',e);     
        }

        try{
            data.phones = await context.getPhones(data.horaire_contact)
        }catch(e){
            context.log.info('Phones not found',e);     
        }
        try{
            data.image = await context.page.locator('.dl-profile img').first().getAttribute('src',{timeout:2000})
            if(data.image.startsWith('/')){ data.image = 'https:' + data.image}
        }catch(e){
            context.log.info('Image not found',e);     
        }        
    }else{
        context.log.info('we are not on a doctor page: so a search or pagination page.');
        data.message = 'you can remove these rows in the settings with \"hideSearchPages\" '
        userData.label = 'doctor';
        const elements = context.page.locator('.dl-card a[href]');
        const links = await elements.evaluateAll(elems => elems.map(elem => elem.getAttribute('href')));
        let extenstion = 'fr'
        if(context.request.url.includes('doctolib.de')){ extenstion = 'de' }
        if(context.request.url.includes('doctolib.it')){ extenstion = 'it' }
        links.forEach(async link => {
            if(link.startsWith('/')){ link = `https://www.doctolib.${extenstion}${link}` }
            await context.enqueueRequest(link, userData , true);
        })

    }
    context.log.info(`ending this page now`);

    return data;
}
""",
}

# Run the Actor and wait for it to finish
run = client.actor("anchor/doctolib").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.doctolib.fr/infectiologue/75001-paris"
    }
  ],
  "pageFunction": "async function pageFunction(context) {\\n\\n    let data = {}\\n    let userData = context.request.userData\\n    data.url = context.request.url\\n\\n    const isDoctorProfile = userData && userData.label === '\''doctor'\''\\n    let isProbablyDoctorProfile\\n    if(!isDoctorProfile){\\n        isProbablyDoctorProfile = await context.innerTextwrapper(context,'\''body.profiles, body.online_booking-drafts'\'')\\n    }\\n    const isDoctorPage = isDoctorProfile || isProbablyDoctorProfile\\n\\n    data.isDoctorPage = isDoctorPage\\n    \\n    if(isDoctorPage){\\n        context.log.info(`Doctor page ${isDoctorProfile ? '\''from search'\'' : '\'''\'' } ${isProbablyDoctorProfile ? '\''guessing'\'' : '\'''\'' }`);\\n        data.nom = await context.page.locator('\''#main-content h1'\'').innerText({timeout:6000})\\n        data.tarif = await context.innerTextwrapper(context,'\''#payment_means'\'')\\n        data.horaire_contact = await context.innerTextwrapper(context,'\''#openings_and_contact'\'')\\n        data.description = await context.innerTextwrapper(context,'\''.dl-profile-bio'\'')\\n        data.specialite = await context.innerTextwrapper(context,'\''.dl-profile-header-speciality'\'')\\n        data.expertise = await context.innerTextwrapper(context,'\''#skills'\'')\\n        try{\\n            data.website = await context.page.locator('\''.dl-profile-row-section div'\'', { hasText: '\''Website'\'' }).locator('\''a'\'').getAttribute('\''href'\'',{timeout:2000})\\n        }catch(e){\\n            context.log.info('\''Website not found'\'',e);     \\n        }\\n\\n        try{\\n            data.phones = await context.getPhones(data.horaire_contact)\\n        }catch(e){\\n            context.log.info('\''Phones not found'\'',e);     \\n        }\\n        try{\\n            data.image = await context.page.locator('\''.dl-profile img'\'').first().getAttribute('\''src'\'',{timeout:2000})\\n            if(data.image.startsWith('\''/'\'')){ data.image = '\''https:'\'' + data.image}\\n        }catch(e){\\n            context.log.info('\''Image not found'\'',e);     \\n        }        \\n    }else{\\n        context.log.info('\''we are not on a doctor page: so a search or pagination page.'\'');\\n        data.message = '\''you can remove these rows in the settings with \\"hideSearchPages\\" '\''\\n        userData.label = '\''doctor'\'';\\n        const elements = context.page.locator('\''.dl-card a[href]'\'');\\n        const links = await elements.evaluateAll(elems => elems.map(elem => elem.getAttribute('\''href'\'')));\\n        let extenstion = '\''fr'\''\\n        if(context.request.url.includes('\''doctolib.de'\'')){ extenstion = '\''de'\'' }\\n        if(context.request.url.includes('\''doctolib.it'\'')){ extenstion = '\''it'\'' }\\n        links.forEach(async link => {\\n            if(link.startsWith('\''/'\'')){ link = `https://www.doctolib.${extenstion}${link}` }\\n            await context.enqueueRequest(link, userData , true);\\n        })\\n\\n    }\\n    context.log.info(`ending this page now`);\\n\\n    return data;\\n}\\n"
}' |
apify call anchor/doctolib --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=anchor/doctolib",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Doctolib",
        "description": "Scraping Doctolib is now super easy and cheap! Extract phones, names, contact, timings, image and addresses of medics, doctors, hospitals... Best part : you can even customize what info to extract from Doctolib!",
        "version": "0.7",
        "x-build-id": "BbhjAkOaqbMgrwEmv"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/anchor~doctolib/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-anchor-doctolib",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/anchor~doctolib/runs": {
            "post": {
                "operationId": "runs-sync-anchor-doctolib",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/anchor~doctolib/run-sync": {
            "post": {
                "operationId": "run-sync-anchor-doctolib",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "startUrls"
                ],
                "properties": {
                    "startUrls": {
                        "title": "Doctolib search URLs",
                        "type": "array",
                        "description": "Search URLs to start with. We automatically do the pagination. Works for France 🇫🇷, Germany 🇩🇪 and Italy 🇮🇹",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "maxPagesPerCrawl": {
                        "title": "Max Pages",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum number of pages before we stop. This limit prevents excess platform usage for misconfigured scrapers.<br><br><code>0</code> is no limit.<br><br><code>10</code> would stop after 10 pages for instance.",
                        "default": 10
                    },
                    "hideSearchPages": {
                        "title": "Hide Search Pages",
                        "type": "boolean",
                        "description": "If true, search pages data won't show in the result dataset. Useful if you are only interested by Doctor pages, not in the search URLs",
                        "default": true
                    },
                    "pageFunction": {
                        "title": "Function",
                        "type": "string",
                        "description": "JavaScript function executed on every page (inside the browser). Customise it if necessary. <br><br>For details, see <a href='https://playwright.dev/docs/api/class-page' target='_blank' rel='noopener'>See Playwright page, available as 'context.page'</a> "
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
