DEV Community

Cover image for How to Use EHR APIs ?
Preecha
Preecha

Posted on

How to Use EHR APIs ?

TL;DR

EHR APIs provide access to patient records in platforms such as Epic, Cerner, and Athenahealth. Most modern EHRs expose FHIR (Fast Healthcare Interoperability Resources) APIs and use OAuth 2.0 with SMART on FHIR extensions for authentication and patient context. Before connecting to a production EHR, validate FHIR resources, authentication flows, search behavior, and error handling against sandbox environments.

Try Apidog today

Introduction

Electronic Health Records (EHRs) store patient-care data, including diagnoses, medications, lab results, allergies, encounters, and treatment plans. Healthcare organizations manage this information in EHR platforms such as Epic, Cerner, and Athenahealth.

When building a healthcare application, you typically integrate with EHR APIs instead of importing CSV exports. These APIs handle protected health information (PHI), so your integration must account for security, access controls, and regulations such as HIPAA in the United States.

Most EHR vendors now support FHIR, a standard API format for healthcare data exchange. The API format is standardized, but authentication, authorization, data mapping, and vendor-specific behavior still require implementation work.

Use Apidog to test FHIR resources, validate response structures, and run requests against public sandboxes before connecting to a hospital production system.

Test FHIR APIs with Apidog - free

By the end of this guide, you will be able to:

  • Identify common FHIR resource types
  • Authenticate with SMART on FHIR
  • Query patient demographics and clinical data
  • Create and update FHIR resources
  • Test against EHR sandbox environments

Understand FHIR

FHIR stands for Fast Healthcare Interoperability Resources. It is a healthcare API standard that defines:

  • Resources: Data models for healthcare concepts such as Patient, Observation, and MedicationRequest
  • REST operations: Standard HTTP methods for reading, searching, creating, and updating resources
  • Representations: JSON and XML formats

Base URL structure

A typical FHIR endpoint uses this pattern:

https://ehr.example.com/fhir/r4/{resource-type}/{id}
Enter fullscreen mode Exit fullscreen mode

For example, retrieve a patient with ID 123:

GET https://ehr.example.com/fhir/r4/Patient/123
Enter fullscreen mode Exit fullscreen mode

Choose the FHIR version

Most current EHR integrations use FHIR R4. Some older integrations still use DSTU2.

Before building your client:

  1. Confirm the vendor's supported FHIR version.
  2. Use the matching resource definitions and search parameters.
  3. Test against the vendor's sandbox for that version.

This guide uses FHIR R4 examples.

Common FHIR resource types

Resource Purpose
Patient Demographics and administrative data
Practitioner Healthcare providers
Organization Hospitals and clinics
Observation Lab results and vital signs
MedicationRequest Prescriptions
Condition Diagnoses and problems
Encounter Visits and admissions
DocumentReference Clinical documents
AllergyIntolerance Allergies and adverse reactions

Work with FHIR resource structures

FHIR resources are JSON documents with a required resourceType field. Read the resource definition before sending writes: different resources have different required fields, terminology requirements, and validation rules.

Patient resource example

{
  "resourceType": "Patient",
  "id": "123",
  "active": true,
  "name": [
    {
      "use": "official",
      "family": "Smith",
      "given": ["John", "Michael"]
    }
  ],
  "gender": "male",
  "birthDate": "1985-03-15",
  "address": [
    {
      "use": "home",
      "line": ["123 Main St"],
      "city": "Boston",
      "state": "MA",
      "postalCode": "02101",
      "country": "USA"
    }
  ],
  "telecom": [
    {
      "system": "phone",
      "value": "555-123-4567",
      "use": "home"
    },
    {
      "system": "email",
      "value": "john.smith@example.com"
    }
  ],
  "identifier": [
    {
      "system": "http://hospital.example.org/mrn",
      "value": "MRN-123456"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Observation resource example

Use Observation for measurements, lab results, and vital signs. This example represents a systolic blood-pressure reading.

{
  "resourceType": "Observation",
  "id": "obs-123",
  "status": "final",
  "category": [
    {
      "coding": [
        {
          "system": "http://terminology.hl7.org/CodeSystem/observation-category",
          "code": "vital-signs",
          "display": "Vital Signs"
        }
      ]
    }
  ],
  "code": {
    "coding": [
      {
        "system": "http://loinc.org",
        "code": "8480-6",
        "display": "Systolic blood pressure"
      }
    ]
  },
  "subject": {
    "reference": "Patient/123"
  },
  "effectiveDateTime": "2026-03-24T09:30:00Z",
  "valueQuantity": {
    "value": 120,
    "unit": "mmHg",
    "system": "http://unitsofmeasure.org",
    "code": "mm[Hg]"
  }
}
Enter fullscreen mode Exit fullscreen mode

Authenticate with SMART on FHIR

SMART on FHIR extends OAuth 2.0 for healthcare applications. It adds conventions for EHR discovery, scopes, and patient context.

A typical user-facing application follows this sequence:

  1. Discover the EHR authorization and token endpoints.
  2. Redirect the user to the EHR authorization page.
  3. Exchange the returned authorization code for an access token.
  4. Use the token for FHIR requests.
  5. Use patient context from the token response when available.

Step 1: Discover SMART configuration

Request the SMART configuration document:

GET https://ehr.example.com/fhir/r4/.well-known/smart-configuration
Enter fullscreen mode Exit fullscreen mode

Example response:

{
  "authorization_endpoint": "https://ehr.example.com/oauth2/authorize",
  "token_endpoint": "https://ehr.example.com/oauth2/token",
  "scopes_supported": [
    "patient/*.read",
    "patient/*.write",
    "user/*.read",
    "launch/patient"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Store these endpoints in environment variables instead of hardcoding them across requests.

Step 2: Redirect the user for authorization

Build an authorization URL with your registered client ID, redirect URI, requested scopes, and a random state value.

https://ehr.example.com/oauth2/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://yourapp.com/callback&
  scope=patient/*.read&
  state=random_state_value
Enter fullscreen mode Exit fullscreen mode

Use a unique state value per authorization request and validate it when the user returns to your callback.

Step 3: Exchange the authorization code for a token

After the authorization server redirects to your callback, exchange the returned code for an access token.

curl -X POST "https://ehr.example.com/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=AUTHORIZATION_CODE" \
  -d "redirect_uri=https://yourapp.com/callback" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"
Enter fullscreen mode Exit fullscreen mode

Example token response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "patient/*.read",
  "patient": "123"
}
Enter fullscreen mode Exit fullscreen mode

The patient field supplies the patient ID context when the authorization flow grants patient-level access.

Request the minimum SMART scopes

Request only the scope required for the operation you are implementing.

Scope Access
patient/*.read Read all patient data
patient/Patient.read Read patient demographics only
patient/Observation.read Read observations only
user/*.read Read all data for the authorized user
launch/patient EHR launches your app with patient context

For example, prefer this scope when an application only reads vital signs:

patient/Observation.read
Enter fullscreen mode Exit fullscreen mode

Avoid requesting patient/*.read unless your application genuinely needs access to multiple patient resource types.

Query patient data

Set the access token on every protected request:

Authorization: Bearer ACCESS_TOKEN
Enter fullscreen mode Exit fullscreen mode

Get patient demographics

curl -X GET "https://ehr.example.com/fhir/r4/Patient/123" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Search for observations

Use FHIR search parameters rather than downloading all resources.

curl -X GET "https://ehr.example.com/fhir/r4/Observation?patient=123&category=vital-signs" \
  -H "Authorization: Bearer ACCESS_TOKEN"
Enter fullscreen mode Exit fullscreen mode

FHIR search results are returned as a Bundle:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 5,
  "entry": [
    {
      "resource": {
        "...": "Observation resource"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

In your client code, read resources from entry[].resource and handle the case where entry is absent or empty.

Use common search parameters

# Lab results by date range
GET /Observation?patient=123&category=laboratory&date=gt2026-01-01

# Specific LOINC code
GET /Observation?patient=123&code=http://loinc.org|8480-6

# Active medications
GET /MedicationRequest?patient=123&status=active

# Active conditions
GET /Condition?patient=123&clinical-status=active

# Ambulatory encounters
GET /Encounter?patient=123&type=AMB
Enter fullscreen mode Exit fullscreen mode

Handle pagination

FHIR servers paginate large result sets. Request a page size with _count, then follow the URL where relation is next.

GET /Observation?patient=123&_count=20
Enter fullscreen mode Exit fullscreen mode

Example pagination link:

{
  "link": [
    {
      "relation": "next",
      "url": "https://ehr.example.com/fhir/r4/Observation?patient=123&_count=20&page=2"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Do not construct the next page URL manually. Use the server-provided next URL.

Create and update resources

Before enabling writes in production:

  1. Confirm that the EHR supports writes for the target resource.
  2. Request a scope that allows the operation.
  3. Validate required fields and terminology codes.
  4. Test the same request in a sandbox first.

Create an observation

Send FHIR JSON with Content-Type: application/fhir+json.

curl -X POST "https://ehr.example.com/fhir/r4/Observation" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "Observation",
    "status": "final",
    "code": {
      "coding": [{
        "system": "http://loinc.org",
        "code": "8480-6",
        "display": "Systolic blood pressure"
      }]
    },
    "subject": {
      "reference": "Patient/123"
    },
    "effectiveDateTime": "2026-03-24T09:30:00Z",
    "valueQuantity": {
      "value": 118,
      "unit": "mmHg",
      "system": "http://unitsofmeasure.org",
      "code": "mm[Hg]"
    }
  }'
Enter fullscreen mode Exit fullscreen mode

Update a patient

A PUT request updates the resource at a known ID.

curl -X PUT "https://ehr.example.com/fhir/r4/Patient/123" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/fhir+json" \
  -d '{
    "resourceType": "Patient",
    "id": "123",
    "name": [{
      "family": "Smith",
      "given": ["John", "Michael"]
    }],
    "telecom": [{
      "system": "phone",
      "value": "555-999-8888",
      "use": "home"
    }]
  }'
Enter fullscreen mode Exit fullscreen mode

Check vendor-specific endpoints

FHIR provides a common model, but each vendor can have different registration processes, extensions, available resources, and sandbox behavior.

Epic

Epic's FHIR API is available at many large hospitals.

Epic requires app registration in its app marketplace for production access.

Cerner

Cerner (Oracle Health) uses standard FHIR with some extensions.

Athenahealth

Athenahealth provides FHIR and legacy APIs.

Test FHIR APIs with Apidog

Healthcare APIs require careful testing because patient data is sensitive. Use mock or sandbox patient data during development.

Image

1. Start with public sandboxes

Use public FHIR sandboxes to verify request construction, resource validation, searches, and response parsing.

# HAPI FHIR (open source)
https://hapi.fhir.org/baseR4

# SMART Health IT sandbox
https://launch.smarthealthit.org
Enter fullscreen mode Exit fullscreen mode

2. Add resource validation tests

Validate that returned resources contain the fields your application depends on.

pm.test("Resource is valid Patient", () => {
  const response = pm.response.json();

  pm.expect(response.resourceType).to.eql("Patient");
  pm.expect(response.id).to.exist;
  pm.expect(response.name).to.be.an("array");
});

pm.test("Observation has required fields", () => {
  const resource = pm.response.json();

  pm.expect(resource.status).to.exist;
  pm.expect(resource.code).to.exist;
  pm.expect(resource.subject).to.exist;
});
Enter fullscreen mode Exit fullscreen mode

3. Configure the SMART on FHIR flow

Store configuration in an environment rather than embedding secrets in requests.

AUTHORIZATION_ENDPOINT: https://ehr.example.com/oauth2/authorize
TOKEN_ENDPOINT: https://ehr.example.com/oauth2/token
CLIENT_ID: your_client_id
CLIENT_SECRET: your_client_secret
SCOPE: patient/*.read
Enter fullscreen mode Exit fullscreen mode

Test FHIR APIs with Apidog - free

Apply compliance requirements

HIPAA

In the United States, applications handling PHI must comply with HIPAA. Implementation concerns include:

  • Data transmission: Use TLS 1.2 or later.
  • Data storage: Encrypt stored data at rest.
  • Access control: Maintain audit logging and enforce minimum-necessary access.
  • Business Associate Agreements: Required with EHR vendors when applicable.

SMART on FHIR security practices

Account for the lifecycle of OAuth tokens:

  • Access tokens expire, typically after one hour.
  • Refresh tokens may be available for extended sessions.
  • Patient context is bound to the token scope.
  • Logout requires token revocation.

Minimize data access

Design each API request and scope around the minimum data necessary for the feature.

  • Good: patient/Observation.read
  • Avoid unless necessary: patient/*.read

Troubleshoot common errors

401 Unauthorized

Cause: The token is expired or invalid.

Fix: Refresh the token using the refresh token received during initial authorization.

403 Forbidden

Cause: The current scope does not permit access to the requested resource.

Fix: Check the scopes granted to the token. Request additional scopes during authorization only if the application requires them.

404 Not Found

Cause: The patient or resource does not exist, or it is not available in the current patient context.

Fix: Verify the resource ID and confirm that the current token can access the patient.

422 Unprocessable Entity

Cause: FHIR resource validation failed.

Fix: Inspect the OperationOutcome response, then correct required fields or terminology codes.

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "required",
      "details": {
        "text": "Observation.status is required"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Compare EHR API options

Feature Epic Cerner Athenahealth OpenEMR
FHIR R4 Partial
SMART on FHIR No
Sandbox access Limited Self-host
API documentation Excellent Good Good Basic
Market share Large hospitals Health systems Small practices Open source

Epic and Cerner are common in large healthcare systems. Athenahealth primarily serves smaller practices. OpenEMR is open source but has limited API support.

Apply FHIR APIs to real-world use cases

Patient portal

A health system builds a portal that pulls data from Epic. Patients can view lab results, medications, and upcoming appointments. The portal uses FHIR APIs with SMART on FHIR authentication.

Clinical research

A pharmaceutical company identifies patients eligible for clinical trials by querying FHIR APIs across hospital systems, with proper consent management.

Remote monitoring

A telehealth company sends patient-reported vital signs to an EHR. It creates Observation resources through the FHIR API so clinicians can view the data in Epic.

Next steps

You now have the core workflow for building an EHR integration:

  • Use FHIR resources to model healthcare data.
  • Use SMART on FHIR for OAuth 2.0 authentication and patient context.
  • Query resources with FHIR search parameters.
  • Follow server-provided pagination links.
  • Validate writes and error responses in a sandbox.
  • Apply HIPAA-compliant handling for PHI.

Start by exploring the HAPI FHIR public sandbox, identify the resource types required by your application, register for the relevant EHR developer programs, and test your requests with mock patient data before production.

Test FHIR APIs with Apidog - free

FAQ

What is the difference between FHIR and HL7 v2?

HL7 v2 is an older messaging standard commonly used in hospital interfaces. FHIR is a modern REST API standard. Most new integrations use FHIR, though HL7 v2 remains common in legacy systems.

Do I need a BAA to use EHR APIs?

Yes, if you handle PHI. Business Associate Agreements are required between covered entities and business associates. Confirm requirements with the EHR vendor's compliance team.

How do I get access to Epic's FHIR API?

Register through Epic's App Orchard marketplace. Use the public sandbox for testing. Production access requires hospital approval.

What is patient context?

SMART on FHIR tokens can include a patient ID. API calls are limited to that patient's data, ensuring that applications access only data authorized by the patient.

Can I write data to EHRs?

Yes, with limitations. Many EHRs allow creating observations and updating patient demographics. Writing diagnoses or medications commonly requires clinical decision support approval.

How do I handle terminology codes?

FHIR uses standard terminology systems:

  • LOINC for lab tests and observations
  • SNOMED CT for clinical concepts
  • ICD-10 for diagnoses
  • RxNorm for medications

Use the appropriate terminology system when creating resources.

What about international healthcare?

FHIR is a global standard. Countries and regions can publish their own implementation guides. The United States uses US Core profiles. Check the specifications for your region.

Top comments (0)