Epic FHIR R4 REST API

raw JSON →
N/A verified Tue May 12 auth: no python install: verified quickstart: verified

Epic's FHIR R4 API enables access to electronic health record (EHR) data including patients, encounters, observations, conditions, medications, and more. Epic supports SMART on FHIR and OAuth 2.0 for authorization. The open sandbox at fhir.epic.com allows testing with synthetic data. Production access requires registration with Epic's App Orchard and approval from individual health systems.

pip install requests
error 401 Unauthorized
cause This error typically occurs during the OAuth 2.0 token exchange or when making API calls, indicating that the access token is missing, expired, invalid, or the client credentials (e.g., client ID, client secret, JWT) are incorrect or not yet synced after registration.
fix
Ensure your JWT is correctly signed (RS384 algorithm) and has not expired. Verify the client_id is correct for the environment (sandbox vs. production). If using backend services, ensure your public key is uploaded and synced with Epic (which can take hours). For token requests, confirm Content-Type: application/x-www-form-urlencoded and data in the request body, not the querystring.
error 403 Forbidden
cause This error means your application's access token does not have the necessary scopes or permissions to access the requested FHIR resource or perform the desired operation.
fix
Review your application's configuration in Epic's App Orchard or fhir.epic.com to ensure all required API endpoints and corresponding scopes (e.g., patient.read, Observation.read) are enabled for your app. Remember that changes to app configurations can take time to sync to the sandbox environment.
error 404 Not Found
cause This error frequently arises from an incorrect base URL for the FHIR server, an invalid `aud` (audience) parameter in the OAuth request, or attempting to access a resource with an ID that does not exist in the Epic environment (e.g., an incorrect patient FHIR ID).
fix
Verify that your FHIR server base URL is accurate (e.g., https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4 for R4 in sandbox). Ensure the aud parameter in your authorization request matches the correct FHIR endpoint for the version you are targeting. For resource-specific lookups, use valid test patient or resource IDs available in the Epic sandbox documentation.
error { "error": "invalid_client", "error_description": null }
cause This specific error during token exchange indicates that the client authentication itself failed. This is often due to an unsynced public key, an incorrect `client_id`, or an issue with the JWT assertion.
fix
After creating your developer app and uploading your public key, you *must* wait some time (potentially several hours or even a day) for the credentials to become active and sync across Epic's systems. Double-check that you are using the correct client_id for the sandbox or production environment and that your JWT is properly formed and signed (RS384).
error User authentication is blocked for your account. Contact your system administrator to unblock your account.
cause This message is specific to Epic's sandbox environment and indicates that a test user account (like FHIR or FHIRTWO) has been temporarily blocked, often due to repeated failed login attempts or maintenance on Epic's side.
fix
This usually requires contacting Epic support (open@epic.com or Vendor Services) as it's an issue with the sandbox user account itself, not your application's code. Occasionally, these accounts may be unblocked automatically after a period, or Epic might reset them.
breaking Epic FHIR endpoints require a valid OAuth 2.0 Bearer token for every request. There is no API key fallback. Unauthenticated requests return 401.
fix Implement a SMART on FHIR authorization flow or backend service JWT-based token exchange before making API calls.
breaking The open sandbox base URL (fhir.epic.com) is for testing only. Production endpoints vary per health system and require App Orchard registration and approval.
fix Register your app at appmarket.epic.com and obtain endpoint URLs from each organization's FHIR metadata endpoint.
gotcha Epic's FHIR server requires the Accept header to be 'application/fhir+json'. Omitting it or using 'application/json' may return XML or errors depending on the Epic version.
fix Always set headers={'Accept': 'application/fhir+json'} on every request.
gotcha Access tokens are short-lived (typically 5 minutes). Caching tokens without refresh logic causes sudden 401 errors.
fix Implement token refresh using the refresh_token grant or re-authenticate via the backend JWT flow before expiry.
gotcha Search results are paginated via FHIR Bundle 'link' entries. Epic defaults to small page sizes (often 10-20 resources). Not following pagination misses most results.
fix Check for bundle.get('link') entries with relation 'next' and follow them until no next link is returned.
gotcha Epic scopes are strictly enforced. Requesting a resource outside your granted SMART scopes returns 403 Forbidden, not an empty result set.
fix Request only the SMART scopes you need (e.g. patient/Patient.read, patient/Observation.read) and verify they are granted in the token response.
gotcha The test output indicates pip warnings related to running as the 'root' user and an available pip update. These are environment-specific and not direct API interaction failures.
fix Avoid running pip as the 'root' user by utilizing a Python virtual environment. To resolve the update notification, run: `pip install --upgrade pip`.
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.53s 70.4M
3.10 slim (glibc) - - 0.37s 142M
3.11 alpine (musl) - - 0.74s 77.1M
3.11 slim (glibc) - - 0.58s 148M
3.12 alpine (musl) - - 0.63s 67.6M
3.12 slim (glibc) - - 0.65s 139M
3.13 alpine (musl) - - 0.64s 64.1M
3.13 slim (glibc) - - 0.62s 137M
3.9 alpine (musl) - - 0.51s 69.3M
3.9 slim (glibc) - - 0.41s 141M

Fetch a test patient resource from the Epic FHIR R4 open sandbox using a Bearer token.

import os
import requests

BASE_URL = "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4"
access_token = os.environ.get("EPIC_FHIR_ACCESS_TOKEN", "")

headers = {
    "Authorization": f"Bearer {access_token}",
    "Accept": "application/fhir+json"
}

# Read a test patient from the Epic open sandbox
resp = requests.get(
    f"{BASE_URL}/Patient/erXuFYUfucBZaryVksYEcMg3",
    headers=headers
)
resp.raise_for_status()
patient = resp.json()
print(f"Patient: {patient['name'][0]['given'][0]} {patient['name'][0]['family']}")