Install

Get free appointment slots for a Doctolib practitioner.

Free Doctolib appointment slots for one practitioner, visit type (motive), agendas and practice, from a start date for up to 7 days, grouped by day, plus the next free slot when the window is empty. Takes visit_motive_id, agenda_ids and practice_id from doctolib.de/search-practitioners. Exact slot times can be passed to doctolib.de/book-slot.

No reports yetRead-only, no login
Site
Doctolib, Germany
Version
v1
Runs, last 7 days
No reports yet
sha256
1495ecf8e9b7…

Requests

sent from your browser, in order
  1. 1GET/availabilities.json?agenda_ids&insurance_sector&limit&practice_ids&start_date&telehealth&visit_motive_ids

Parameters

from your agent
agenda_idsinteger[]required
daysinteger = 7
insurance_sectorenumMust match the patient's insurance; a wrong sector returns empty days, not an error "public" | "private" = "public"
practice_idintegerrequired
start_datestringrequiredYYYY-MM-DD
telehealthbooleanVideo consultation slots = false
visit_motive_idintegerrequired

Returns

to your agent
daysobject[]date, slots, times
next_slotstringNext free slot after the window
reasonstringDoctolib's explanation when nothing is bookable
totalintegerFree slots in the window

Where it can send

  • www.doctolib.de

Anything else is blocked by the extension.

Reads from your session

Nothing. Your browser attaches the site's cookies by itself.

Used only in requests to the addresses on the left. Never sent to wield.run or your agent.

Checks

a run passes when
  • output.total > `0` || output.next_slot != `null` || output.reason != `null`
  • length(output.days) <= `7`

Raw recipe

Served as canonical JSON from /api/v1/recipes/doctolib.de/get-slots, so its sha256 matches the ETag and the index.

Check it yourself
curl -s https://wield.run/api/v1/recipes/doctolib.de/get-slots | shasum -a 256
# 1495ecf8e9b75cc6e0bcebda524b17876eab4f38c4f24c4b288c6bfc6568050d
doctolib.de/get-slots.json, v1
{
  "description": "Free Doctolib appointment slots for one practitioner, visit type (motive), agendas and practice, from a start date for up to 7 days, grouped by day, plus the next free slot when the window is empty. Takes visit_motive_id, agenda_ids and practice_id from doctolib.de/search-practitioners. Exact slot times can be passed to doctolib.de/book-slot.",
  "effect": "read",
  "id": "doctolib.de/get-slots",
  "keywords": [
    "doctolib",
    "arzt",
    "arzttermin",
    "termin buchen",
    "hautarzt",
    "hausarzt",
    "zahnarzt",
    "facharzt",
    "praxis",
    "doctor",
    "doctor appointment",
    "book appointment",
    "medical",
    "freie termine",
    "slots",
    "verfügbarkeit"
  ],
  "output": {
    "schema": {
      "properties": {
        "days": {
          "items": {
            "properties": {
              "date": {
                "format": "date",
                "type": "string"
              },
              "slots": {
                "description": "Exact slot timestamps, as book-slot expects them",
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "times": {
                "description": "HH:mm, Europe/Berlin",
                "items": {
                  "type": "string"
                },
                "type": "array"
              }
            },
            "type": "object"
          },
          "type": "array"
        },
        "next_slot": {
          "description": "Next free slot after the window",
          "type": [
            "string",
            "null"
          ]
        },
        "reason": {
          "description": "Doctolib's explanation when nothing is bookable",
          "type": [
            "string",
            "null"
          ]
        },
        "total": {
          "description": "Free slots in the window",
          "type": "integer"
        }
      },
      "required": [
        "total",
        "days"
      ],
      "type": "object"
    },
    "value": "{total: steps.slots.total, days: steps.slots.days[?length(slots) > `0`].{date: date, times: slots[*].join(':', split(split(@, 'T')[1], ':')[:2]), slots: slots}, next_slot: steps.slots.next_slot, reason: steps.slots.reason}"
  },
  "params": {
    "additionalProperties": false,
    "properties": {
      "agenda_ids": {
        "items": {
          "type": "integer"
        },
        "minItems": 1,
        "type": "array"
      },
      "days": {
        "default": 7,
        "maximum": 7,
        "minimum": 1,
        "type": "integer"
      },
      "insurance_sector": {
        "default": "public",
        "description": "Must match the patient's insurance; a wrong sector returns empty days, not an error",
        "enum": [
          "public",
          "private"
        ],
        "type": "string"
      },
      "practice_id": {
        "type": "integer"
      },
      "start_date": {
        "description": "YYYY-MM-DD",
        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
        "type": "string"
      },
      "telehealth": {
        "default": false,
        "description": "Video consultation slots",
        "type": "boolean"
      },
      "visit_motive_id": {
        "type": "integer"
      }
    },
    "required": [
      "visit_motive_id",
      "agenda_ids",
      "practice_id",
      "start_date"
    ],
    "type": "object"
  },
  "provenance": {
    "method": "browser-inspect",
    "notes": "spikes/doctolib.md. Plain same-origin JSON; agenda_ids are dash-joined and limit is the number of days. Slots were ISO strings in every sample; multi-step motives may return objects, so start_date is read when present. A wrong insurance_sector returns empty days, not an error.",
    "recorded_at": "2026-11-07T12:12:00+01:00",
    "recorded_by": "C4"
  },
  "requires_login": false,
  "schema": "https://wield.run/schema/recipe/v1.json",
  "site": {
    "country": "DE",
    "id": "doctolib.de",
    "language": "de",
    "login_url": "https://www.doctolib.de/auth/patient_de",
    "name": "Doctolib",
    "origin": "https://www.doctolib.de",
    "origins": [
      "https://www.doctolib.de"
    ]
  },
  "steps": [
    {
      "expect": {
        "assert": "body.availabilities != null",
        "content_type": "json",
        "status": [
          200
        ]
      },
      "extract": {
        "json": {
          "days": "availabilities[].{date: date, slots: map(&not_null(start_date, @), slots || `[]`)}",
          "next_slot": "next_slot || `null`",
          "reason": "reason || message || `null`",
          "total": "total"
        }
      },
      "id": "slots",
      "request": {
        "headers": {
          "Accept": "application/json"
        },
        "method": "GET",
        "query": {
          "agenda_ids": "{{params.agenda_ids | join:-}}",
          "insurance_sector": "{{params.insurance_sector}}",
          "limit": "{{params.days}}",
          "practice_ids": "{{params.practice_id}}",
          "start_date": "{{params.start_date}}",
          "telehealth": "{{params.telehealth | string}}",
          "visit_motive_ids": "{{params.visit_motive_id}}"
        },
        "url": "/availabilities.json"
      }
    }
  ],
  "task": "Get free appointment slots for a Doctolib practitioner",
  "tos": {
    "notes": "Doctolib's terms prohibit automated access and scraping. One user-initiated lookup at a time from the user's own browser (1 request); do not poll for slots.",
    "reviewed": false
  },
  "verify": {
    "checks": [
      "output.total > `0` || output.next_slot != `null` || output.reason != `null`",
      "length(output.days) <= `7`"
    ],
    "example_params": {
      "agenda_ids": [
        2359956
      ],
      "insurance_sector": "public",
      "practice_id": 723928,
      "start_date": "2026-11-09",
      "visit_motive_id": 14928354
    },
    "max_ms": 3000
  },
  "version": 1
}