Project

toc_doc

0.0
No release in over 3 years
A standalone Ruby gem providing a Faraday-based client to interact with the (unofficial) Doctolib API.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

>= 1, < 3
 Project Readme

TocDoc

A Ruby gem for interacting with the (unofficial) Doctolib API. A thin, Faraday-based client with configurable defaults, model-driven resource querying, and a clean error hierarchy.

Gem Version CI Coverage Status License: GPL v3 YARD Docs

Heads-up: Doctolib™ does not publish a public API. This gem reverse-engineers the endpoints used by the Doctolib™ website. Behaviour may change at any time without notice. This project is for entertainment purposes only. Doctolib is a trademark of Doctolib. This project is not affiliated with, endorsed by, or sponsored by Doctolib.


Contents

  1. Installation
  2. Quick start
  3. Configuration
    • Module-level
    • Per-client
    • All options
    • ENV variables
  4. Endpoints
    • Availabilities
    • Search
  5. Response objects
  6. Pagination
  7. Error handling
  8. Development
    • Generating documentation
  9. Contributing
  10. Code of Conduct
  11. License

Installation

Add the gem to your Gemfile:

gem 'toc_doc'

then run:

bundle install

or install it directly:

gem install toc_doc

Quick start

require 'toc_doc'

collection = TocDoc::Availability.where(
  visit_motive_ids: 7_767_829,
  agenda_ids:       1_101_600,
  practice_ids:     377_272,
  telehealth:       false
)

collection.total      # => 5
collection.next_slot  # => "2026-02-28T10:00:00.000+01:00"

collection.each do |avail|
  puts "#{avail.date}: #{avail.slots.map { |s| s.strftime('%H:%M') }.join(', ')}"
end

Configuration

Module-level configuration

Set options once at startup and every subsequent call will share them:

TocDoc.configure do |config|
  config.api_endpoint = 'https://www.doctolib.de'   # target country
  config.per_page     = 10
end

TocDoc::Availability.where(visit_motive_ids: 123, agenda_ids: 456)

Calling TocDoc.reset! restores all options to their defaults.
Use TocDoc.options to inspect the current configuration hash.

Per-client configuration

Instantiate independent clients with different options and query via TocDoc::Availability.where:

# Germany
TocDoc.configure { |c| c.api_endpoint = 'https://www.doctolib.de'; c.per_page = 3 }
TocDoc::Availability.where(visit_motive_ids: 123, agenda_ids: 456)

# Reset and switch to Italy
TocDoc.reset!
TocDoc.configure { |c| c.api_endpoint = 'https://www.doctolib.it' }
TocDoc::Availability.where(visit_motive_ids: 789, agenda_ids: 101)

Alternatively, use TocDoc::Client directly for lower-level access ().

client = TocDoc::Client.new(api_endpoint: 'https://www.doctolib.de', per_page: 5)
client.get('/availabilities.json', query: { visit_motive_ids: '123', agenda_ids: '456', start_date: Date.today.to_s, limit: 5 })

All configuration options

Option Default Description
api_endpoint https://www.doctolib.fr Base URL. Change to .de / .it for other countries.
user_agent TocDoc Ruby Gem 1.3.0 User-Agent header sent with every request.
default_media_type application/json Accept and Content-Type headers.
per_page 15 Default number of availability dates per request (capped at 15).
middleware Retry + RaiseError + JSON + adapter Full Faraday middleware stack. Override to customise completely.
connection_options {} Options passed directly to Faraday.new.

Environment variable overrides

All primary options can be set via environment variables before the gem is loaded:

Variable Option
TOCDOC_API_ENDPOINT api_endpoint
TOCDOC_USER_AGENT user_agent
TOCDOC_MEDIA_TYPE default_media_type
TOCDOC_PER_PAGE per_page
TOCDOC_RETRY_MAX Maximum Faraday retry attempts (default 3)

Endpoints

Availabilities

Retrieve open appointment slots for a given visit motive and agenda.

TocDoc::Availability.where(
  visit_motive_ids: visit_motive_id,   # Integer, String, or Array
  agenda_ids:       agenda_id,         # Integer, String, or Array
  start_date:       Date.today,        # Date or String (default: today)
  limit:            5,                 # override per_page for this call
  # any extra keyword args are forwarded verbatim as query params:
  practice_ids:     377_272,
  telehealth:       false
)

TocDoc.availabilities(...) is a module-level shortcut with the same signature.

Multiple IDs are accepted as arrays; the gem serialises them with the dash-separated format Doctolib expects:

TocDoc::Availability.where(
  visit_motive_ids: [7_767_829, 7_767_830],
  agenda_ids:       [1_101_600, 1_101_601]
)
# → GET /availabilities.json?visit_motive_ids=7767829-7767830&agenda_ids=1101600-1101601&…

Return value: a TocDoc::Availability::Collection (see Response objects).

Search

Query the Doctolib autocomplete endpoint to look up practitioners, organizations, and specialities.

result = TocDoc::Search.where(query: 'dentiste')
result.profiles      # => [#<TocDoc::Profile::Practitioner ...>, ...]
result.specialities  # => [#<TocDoc::Speciality ...>, ...]

Pass type: to receive a filtered array directly:

# Only specialities
TocDoc::Search.where(query: 'cardio', type: 'speciality')
# => [#<TocDoc::Speciality name="Cardiologue">, ...]

# Only practitioners
TocDoc::Search.where(query: 'dupont', type: 'practitioner')
# => [#<TocDoc::Profile::Practitioner ...>, ...]

Valid type: values: 'profile' (all profiles), 'practitioner', 'organization', 'speciality'.

TocDoc.search(...) is a module-level shortcut with the same signature.

Return value: a TocDoc::Search::Result when type: is omitted, or a filtered Array otherwise (see Response objects).


Response objects

All API responses are wrapped in lightweight Ruby objects that provide dot-notation access and a #to_h round-trip helper.

TocDoc::Availability::Collection

Returned by TocDoc::Availability.where; also accessible via the TocDoc.availabilities module-level shortcut. Implements Enumerable, yielding TocDoc::Availability instances that have at least one slot.

Method Type Description
#total Integer Total number of available slots across all dates.
#next_slot String | nil ISO 8601 datetime of the nearest available slot. nil when none remain.
#each Yields each TocDoc::Availability that has at least one slot (excludes empty-slot dates).
#raw_availabilities Array<TocDoc::Availability> All date entries, including those with no slots.
#to_h Hash Plain-hash representation (only dates with slots in the availabilities key).

TocDoc::Availability

Represents a single availability date entry. Each element yielded by the collection.

Method Type Description
#date Date Parsed date object.
#slots Array<DateTime> Parsed datetime objects for each bookable slot on that date.
#to_h Hash Plain-hash representation.

Example:

collection = TocDoc::Availability.where(visit_motive_ids: 123, agenda_ids: 456)

collection.total      # => 5
collection.next_slot  # => "2026-02-28T10:00:00.000+01:00"

collection.first.date   # => #<Date: 2026-02-28>
collection.first.slots  # => [#<DateTime: 2026-02-28T10:00:00+01:00>, ...]

collection.to_h
# => {
#      "total"          => 5,
#      "next_slot"      => "2026-02-28T10:00:00.000+01:00",
#      "availabilities" => [{ "date" => "2026-02-28", "slots" => [...] }, ...]
#    }

TocDoc::Search::Result

Returned by TocDoc::Search.where when type: is omitted.

Method Type Description
#profiles Array<TocDoc::Profile::Practitioner, TocDoc::Profile::Organization> All profile results, typed via Profile.build.
#specialities Array<TocDoc::Speciality> All speciality results.
#filter_by_type(type) Array Narrows results to 'profile', 'practitioner', 'organization', or 'speciality'.

TocDoc::Profile

Represents a search profile result (practitioner or organization). Use Profile.build(attrs) to obtain the correctly typed subclass instance.

Method Type Description
Profile.build(attrs) Profile::Practitioner | Profile::Organization Factory: returns Practitioner when owner_type is "Account", Organization otherwise.
#practitioner? Boolean true when this is a Profile::Practitioner.
#organization? Boolean true when this is a Profile::Organization.

TocDoc::Profile::Practitioner and TocDoc::Profile::Organization are thin subclasses that inherit dot-notation attribute access from TocDoc::Resource.

TocDoc::Speciality

Represents a speciality returned by the autocomplete endpoint. Inherits dot-notation attribute access from TocDoc::Resource.

Method Type Description
#value Integer Numeric speciality identifier.
#slug String URL-friendly identifier.
#name String Human-readable speciality name.

Example:

result = TocDoc::Search.where(query: 'dermato')

result.profiles.first.class          # => TocDoc::Profile::Practitioner
result.profiles.first.practitioner?  # => true
result.profiles.first.name           # => "Dr. Jane Smith"

result.specialities.first.slug   # => "dermatologue"
result.specialities.first.name   # => "Dermatologue"

Pagination

The Doctolib availability endpoint is window-based: each request returns up to limit dates starting from start_date.

Automatic next-slot resolution

TocDoc::Availability.where automatically follows next_slot once: if the first API response contains a next_slot key (indicating no available slots in the requested window), a second request is issued transparently from that date before the collection is returned.

Manual window advancement

To fetch additional date windows, call TocDoc::Availability.where again with a later start_date:

first_page = TocDoc::Availability.where(
  visit_motive_ids: 7_767_829,
  agenda_ids:       1_101_600,
  start_date:       Date.today
)

if first_page.any?
  next_start = first_page.raw_availabilities.last.date + 1
  next_page  = TocDoc::Availability.where(
    visit_motive_ids: 7_767_829,
    agenda_ids:       1_101_600,
    start_date:       next_start
  )
end

Error handling

All errors raised by TocDoc inherit from TocDoc::Error < StandardError, so you can rescue the whole hierarchy with a single clause:

begin
  TocDoc::Availability.where(visit_motive_ids: 0, agenda_ids: 0)
rescue TocDoc::Error => e
  puts "Doctolib error: #{e.message}"
end

The default middleware stack also includes Faraday::Response::RaiseError for HTTP-level failures, and a Faraday::Retry::Middleware that automatically retries (up to 3 times, with exponential back-off) on:

  • 429 Too Many Requests
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable
  • 504 Gateway Timeout
  • network timeouts

Development

Clone the repository and install dependencies:

git clone https://github.com/01max/toc_doc.git
cd toc_doc
bin/setup

Run the test suite:

bundle exec rake spec
# or
bundle exec rspec

Run the linter:

bundle exec rubocop

Open an interactive console with the gem loaded:

bin/console

Install the gem locally:

bundle exec rake install

Adding new endpoints

  1. Create lib/toc_doc/models/<resource>.rb with a model class inheriting from TocDoc::Resource. Add a class-level .where (or equivalent) query method that calls TocDoc.client.get / .post to issue requests.
  2. If the endpoint is paginated, create lib/toc_doc/models/<resource>/collection.rb with an Enumerable collection class (see TocDoc::Availability::Collection for the pattern).
  3. Require the new files from lib/toc_doc/models.rb.
  4. Add specs under spec/toc_doc/models/.

Generating documentation

The codebase uses YARD for API documentation. All public methods are annotated with @param, @return, and @example tags.

Generate the HTML docs:

bundle exec yard doc

The output is written to doc/. To browse it locally:

bundle exec yard server
# → http://localhost:8808

To check documentation coverage without generating files:

bundle exec yard stats

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/01max/toc_doc. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.


Code of Conduct

Everyone interacting in the TocDoc project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.


License

The gem is available as open source under the terms of the GNU General Public v3 License.