{"id":137,"library":"epic-fhir","title":"Epic FHIR R4 REST API","description":"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.","status":"active","version":"N/A","language":"python","source_language":"en","source_url":"https://fhir.epic.com/","tags":["epic","fhir","r4","ehr","healthcare","smart-on-fhir","oauth2","hl7","rest-api"],"install":[{"cmd":"pip install requests","lang":"bash","label":"HTTP client (no official PyPI package)"}],"dependencies":[{"reason":"HTTP client for REST API calls.","package":"requests","optional":false},{"reason":"Required for creating signed JWTs for backend OAuth 2.0 (system-to-system) authentication flows.","package":"PyJWT","optional":true},{"reason":"Required for RS384 signing of JWT assertions in backend service authorization.","package":"cryptography","optional":true}],"imports":[{"note":"No official Python SDK exists for Epic FHIR. Use requests or any HTTP client to call the REST API directly.","symbol":"requests","correct":"import requests"}],"quickstart":{"code":"import os\nimport requests\n\nBASE_URL = \"https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4\"\naccess_token = os.environ.get(\"EPIC_FHIR_ACCESS_TOKEN\", \"\")\n\nheaders = {\n    \"Authorization\": f\"Bearer {access_token}\",\n    \"Accept\": \"application/fhir+json\"\n}\n\n# Read a test patient from the Epic open sandbox\nresp = requests.get(\n    f\"{BASE_URL}/Patient/erXuFYUfucBZaryVksYEcMg3\",\n    headers=headers\n)\nresp.raise_for_status()\npatient = resp.json()\nprint(f\"Patient: {patient['name'][0]['given'][0]} {patient['name'][0]['family']}\")","lang":"python","description":"Fetch a test patient resource from the Epic FHIR R4 open sandbox using a Bearer token."},"warnings":[{"fix":"Implement a SMART on FHIR authorization flow or backend service JWT-based token exchange before making API calls.","message":"Epic FHIR endpoints require a valid OAuth 2.0 Bearer token for every request. There is no API key fallback. Unauthenticated requests return 401.","severity":"breaking","affected_versions":"all"},{"fix":"Register your app at appmarket.epic.com and obtain endpoint URLs from each organization's FHIR metadata endpoint.","message":"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.","severity":"breaking","affected_versions":"all"},{"fix":"Always set headers={'Accept': 'application/fhir+json'} on every request.","message":"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.","severity":"gotcha","affected_versions":"all"},{"fix":"Implement token refresh using the refresh_token grant or re-authenticate via the backend JWT flow before expiry.","message":"Access tokens are short-lived (typically 5 minutes). Caching tokens without refresh logic causes sudden 401 errors.","severity":"gotcha","affected_versions":"all"},{"fix":"Check for bundle.get('link') entries with relation 'next' and follow them until no next link is returned.","message":"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.","severity":"gotcha","affected_versions":"all"},{"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.","message":"Epic scopes are strictly enforced. Requesting a resource outside your granted SMART scopes returns 403 Forbidden, not an empty result set.","severity":"gotcha","affected_versions":"all"},{"fix":"Avoid running pip as the 'root' user by utilizing a Python virtual environment. To resolve the update notification, run: `pip install --upgrade pip`.","message":"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.","severity":"gotcha","affected_versions":"all"}],"env_vars":[{"name":"EPIC_FHIR_ACCESS_TOKEN","required":true,"description":"OAuth 2.0 Bearer token obtained via SMART on FHIR authorization flow or backend JWT exchange. Required for all API calls."}],"last_verified":"2026-05-12T08:14:31.283Z","next_check":"2026-06-17T00:00:00.000Z","problems":[{"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.","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.","error":"401 Unauthorized"},{"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.","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.","error":"403 Forbidden"},{"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.","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).","error":"404 Not Found"},{"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).","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.","error":"{ \"error\": \"invalid_client\", \"error_description\": null }"},{"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.","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.","error":"User authentication is blocked for your account. Contact your system administrator to unblock your account."}],"ecosystem":"rest","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.53,"mem_mb":9.6,"disk_size":"70.4M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.37,"mem_mb":9.6,"disk_size":"142M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.74,"mem_mb":10.7,"disk_size":"77.1M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.58,"mem_mb":10.8,"disk_size":"148M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.63,"mem_mb":11.2,"disk_size":"67.6M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.65,"mem_mb":11.2,"disk_size":"139M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.64,"mem_mb":11.6,"disk_size":"64.1M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.62,"mem_mb":11.6,"disk_size":"137M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.51,"mem_mb":9.4,"disk_size":"69.3M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.41,"mem_mb":9.4,"disk_size":"141M"}]},"quickstart_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}