curl --request GET \
--url https://api.maxcare.ai/v2/schedule/finder \
--header 'Authorization: Bearer <token>' \
--header 'X-Organization-Id: <x-organization-id>'import requests
url = "https://api.maxcare.ai/v2/schedule/finder"
headers = {
"X-Organization-Id": "<x-organization-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'X-Organization-Id': '<x-organization-id>', Authorization: 'Bearer <token>'}
};
fetch('https://api.maxcare.ai/v2/schedule/finder', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.maxcare.ai/v2/schedule/finder",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Organization-Id: <x-organization-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.maxcare.ai/v2/schedule/finder"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Organization-Id", "<x-organization-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.maxcare.ai/v2/schedule/finder")
.header("X-Organization-Id", "<x-organization-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.maxcare.ai/v2/schedule/finder")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Organization-Id"] = '<x-organization-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"code": "success",
"data": {
"groups": [
{
"providerId": "prv_35103413aaaa4bbbccccddddeeeeffff",
"facilityId": "fac_2706aaaa4bbbccccddddeeeeffff1111",
"timezone": "America/Detroit",
"truncated": false,
"slots": [
{
"startsAt": "2026-10-26T08:15:00-04:00",
"endsAt": "2026-10-26T08:30:00-04:00",
"durationMinutes": 15,
"remainingCapacity": 1,
"totalCapacity": 2,
"overrideAllowed": false
}
]
}
],
"meta": {
"actingAs": "user",
"resolvedBy": "personal_connector",
"sourceKind": "live_read",
"strategy": "native",
"generatedAt": "2026-10-26T12:00:00.000Z",
"unmappedGroupCount": 0
}
}
}{
"code": "bad_request",
"message": "'id' must be a valid UUID",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "unauthorized",
"message": "Invalid or missing API key",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "forbidden",
"message": "Insufficient scope",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "not_found",
"message": "Resource not found",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Maximum 1000 requests per 60 seconds.",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "capability_unsupported",
"message": "This organization's EHR has no appointment finder.",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "unexpected_integration_error",
"message": "EHR sync failed",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "server_unresponsive",
"message": "EHR integration is not available for this note",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}Search the EHR's appointment finder
One search of the EHR’s own appointment finder with every option it offers, grouped by provider and facility — the view staff see on the EHR’s finder screen. It is the EHR’s view, not the bookable one: Max AI holds and pending bookings are NOT subtracted, and override-only slots appear when allowOverride=true. Book from GET /schedule/slots, which applies both; a booking is re-checked against the EHR either way. Each group may be truncated (the EHR caps each provider per call; ModMed at 5 open days). Groups for providers or facilities Max AI has not mirrored are dropped and counted in meta.unmappedGroupCount. Options the EHR does not honour are a 400 naming the parameter (see availabilitySearchOptions on GET /schedule/capabilities); EHRs without a finder return 501 capability_unsupported. The search stops when the client disconnects and returns 502 ehr_unavailable if the EHR does not answer within 8 seconds.
curl --request GET \
--url https://api.maxcare.ai/v2/schedule/finder \
--header 'Authorization: Bearer <token>' \
--header 'X-Organization-Id: <x-organization-id>'import requests
url = "https://api.maxcare.ai/v2/schedule/finder"
headers = {
"X-Organization-Id": "<x-organization-id>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'X-Organization-Id': '<x-organization-id>', Authorization: 'Bearer <token>'}
};
fetch('https://api.maxcare.ai/v2/schedule/finder', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.maxcare.ai/v2/schedule/finder",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"X-Organization-Id: <x-organization-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.maxcare.ai/v2/schedule/finder"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-Organization-Id", "<x-organization-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.maxcare.ai/v2/schedule/finder")
.header("X-Organization-Id", "<x-organization-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.maxcare.ai/v2/schedule/finder")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-Organization-Id"] = '<x-organization-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"code": "success",
"data": {
"groups": [
{
"providerId": "prv_35103413aaaa4bbbccccddddeeeeffff",
"facilityId": "fac_2706aaaa4bbbccccddddeeeeffff1111",
"timezone": "America/Detroit",
"truncated": false,
"slots": [
{
"startsAt": "2026-10-26T08:15:00-04:00",
"endsAt": "2026-10-26T08:30:00-04:00",
"durationMinutes": 15,
"remainingCapacity": 1,
"totalCapacity": 2,
"overrideAllowed": false
}
]
}
],
"meta": {
"actingAs": "user",
"resolvedBy": "personal_connector",
"sourceKind": "live_read",
"strategy": "native",
"generatedAt": "2026-10-26T12:00:00.000Z",
"unmappedGroupCount": 0
}
}
}{
"code": "bad_request",
"message": "'id' must be a valid UUID",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "unauthorized",
"message": "Invalid or missing API key",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "forbidden",
"message": "Insufficient scope",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "not_found",
"message": "Resource not found",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Maximum 1000 requests per 60 seconds.",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "capability_unsupported",
"message": "This organization's EHR has no appointment finder.",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "unexpected_integration_error",
"message": "EHR sync failed",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}{
"code": "server_unresponsive",
"message": "EHR integration is not available for this note",
"trace_id": "550e8400-e29b-41d4-a716-446655440000"
}Authorizations
Marketplace API key
Headers
The App Bridge token of the logged-in user (maxcare.appToken()), verified for this app and organization. With it the EHR is read through that user's own EHR login when they have a usable one on this EHR instance, else through the organization's system connector; without it, always the system connector. meta.actingAs says which. A token that does not verify is 401 authorizer_unverified. Sent as a header so GET requests keep it out of URLs.
Target clinic organization ID
Query Parameters
Appointment type ID (atp_…)
Search mode. first_available: the earliest openings. specific_date: one clinic day (date). time_frame: within offset offsetUnit from today. date_range: from–to, both inclusive. A mode's own parameters are required and any other mode's parameters are refused.
first_available, specific_date, time_frame, date_range "date_range"
Facility ID (fac_…). Repeat or comma-separate for several. Defaults to every facility on the EHR instance; facility-limited API keys must pass it.
Comma-separated provider IDs (prv_…). Applied to the EHR's answer: the finder itself always searches every provider.
Clinic calendar day (YYYY-MM-DD). specific_date only.
"2026-11-02"
Window start (YYYY-MM-DD, inclusive). date_range only.
"2026-11-02"
Window end (YYYY-MM-DD, inclusive). date_range only; at most 120 days after from.
"2026-11-15"
How far ahead to search, in offsetUnit. time_frame only.
2
Unit of offset. time_frame only.
days, weeks, months Only these weekdays. Repeat or comma-separate for several.
monday, tuesday, wednesday, thursday, friday, saturday, sunday Part of the day. Defaults to anytime.
anytime, morning, afternoon, evening Result order as the EHR presents it. Defaults to by_provider; the slots are the same.
by_provider, by_time Slot length in minutes (5–480). Defaults to the appointment type's default.
15
Let the type's alternate durations match. Defaults to true.
true
Also return slots that need a staff override; they carry overrideAllowed: true. Defaults to false.
false
Passed to the EHR's search as is; its effect is unverified. Defaults to false.
false
Passed to the EHR's search as is (0–240); presumably minutes kept free around booked appointments, effect unverified. Defaults to 0.
0
