DEV Community

Cover image for How to Use EHR APIs ?
Wanda
Wanda

Posted on • Originally published at apidog.com

How to Use EHR APIs ?

TL;DR

EHR APIs allow you to access patient health records stored in systems like Epic, Cerner, and Athenahealth. Most modern EHRs support FHIR (Fast Healthcare Interoperability Resources), a standard format for healthcare data. Authentication uses OAuth 2.0 with SMART on FHIR extensions. For testing, use Apidog to validate FHIR resources, test against sandboxes, and ensure your integration handles patient data correctly before connecting to production systems.

Try Apidog today

Introduction

Electronic Health Records (EHRs) store all aspects of patient care: diagnoses, medications, lab results, allergies, and treatment plans. Hospitals, clinics, and health systems use EHR platforms like Epic, Cerner, and Athenahealth.

To build healthcare applications, you need to connect directly to these systems via APIs. EHR APIs are not like typical REST APIs—they handle protected health information (PHI) and must adhere to regulations like HIPAA in the US.

Most EHR vendors now support FHIR, a standard API format. However, authentication, authorization, and data mapping can still be challenging.

If you're building healthcare integrations, Apidog helps you test FHIR resources, validate data structures, and ensure your app handles patient data correctly. Test against public sandboxes before connecting to real hospital systems.

By the end of this guide, you’ll know how to:

  • Understand FHIR resource types and structure
  • Authenticate with SMART on FHIR
  • Query patient demographics and clinical data
  • Create and update patient resources
  • Test with EHR sandboxes

Understanding FHIR

FHIR (Fast Healthcare Interoperability Resources) is the current standard for healthcare APIs. It defines:

  • Resources: Data types for healthcare concepts (e.g., Patient, Observation, Medication)
  • REST API: Standard CRUD operations on resources
  • Formats: JSON and XML representations

Base URL Structure

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

Example:

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

FHIR Version

Most EHRs use FHIR R4 (Release 4). Some older systems may use DSTU2, but this guide focuses on R4.

Resource Types

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

FHIR Resource Structure

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

{
  "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

Authentication with SMART on FHIR

SMART on FHIR extends OAuth 2.0 for healthcare, handling patient context and EHR-specific scopes.

OAuth Flow for Patient Access

Step 1: Get Authorization URL

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

Sample 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

Step 2: Redirect User to Authorize

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

Step 3: Exchange Code for 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

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 gives you the patient ID context.

SMART Scopes

Scope Access Description
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 authorized user
launch/patient EHR launches your app with patient context

Querying Patient Data

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

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

Sample Bundle Response:

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

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

# Medications
GET /MedicationRequest?patient=123&status=active

# Conditions (diagnoses)
GET /Condition?patient=123&clinical-status=active

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

Pagination

For large result sets, use pagination:

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

Paginated Response:

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

Creating and Updating Resources

Create an Observation

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

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

EHR Vendor Specifics

Epic

  • Base URL: https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/
  • Sandbox: https://fhir.epic.com/test/api/FHIR/R4/
  • Documentation: https://fhir.epic.com

App registration in their marketplace is required for production access.

Cerner

Athenahealth

  • Base URL: https://api.platform.athenahealth.com/fhir/r4/{practice-id}
  • Sandbox: Available through developer program
  • Documentation: https://docs.athenahealth.com

Testing with Apidog

Healthcare APIs require thorough testing due to the sensitivity of patient data.

FHIR API Testing with Apidog

1. Use Public Sandboxes

Test integrations against public FHIR sandboxes before production:

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

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

2. Validate FHIR Resources

Use automated scripts to ensure required fields are present:

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. Test Authentication Flow

Save your SMART configuration in your environment:

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

Compliance Considerations

HIPAA

In the US, applications handling healthcare data must comply with HIPAA:

  • Data transmission: Use TLS 1.2+
  • Data storage: Encrypt at rest
  • Access control: Implement audit logging and least-privilege access
  • Business Associate Agreement: Required with EHR vendors

SMART on FHIR Security

  • Tokens expire (typically 1 hour)
  • Refresh tokens are available for longer sessions
  • Patient context is bound to the token scope
  • Logout requires token revocation

Data Minimization

Only request the scopes you need:

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

Common Errors and Fixes

401 Unauthorized

Cause: Token expired or invalid.

Fix: Refresh your access token using the refresh token from initial authorization.

403 Forbidden

Cause: Scope does not cover the requested resource.

Fix: Ensure your requested scopes are sufficient. Request additional scopes if needed.

404 Not Found

Cause: Patient or resource does not exist.

Fix: Verify resource IDs and confirm the patient is accessible with your current token context.

422 Unprocessable Entity

Cause: FHIR resource validation failed.

Fix: Check required fields and code systems. Example error:

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

Alternatives and Comparisons

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 hosp. Health systems Small pract. Open src.

Epic and Cerner are most common in large healthcare systems. Athenahealth is used by smaller practices. OpenEMR is open source but offers limited API support.


Real-World Use Cases

  • Patient portal: Health systems pull data from Epic to display lab results, meds, and appointments to patients via FHIR APIs and SMART on FHIR authentication.
  • Clinical research: Pharma companies identify eligible trial patients by querying FHIR APIs across multiple systems, with consent management.
  • Remote monitoring: Telehealth companies integrate patient-reported vitals with EHRs by creating Observations via FHIR API, making them available to clinicians in Epic.

Wrapping Up

Key takeaways:

  • FHIR is the standard for healthcare APIs
  • Resources represent concepts like patients and observations
  • SMART on FHIR handles OAuth with patient context
  • Each EHR vendor has specific implementation details
  • Always test with sandboxes before moving to production

Next steps:

  1. Explore the HAPI FHIR public sandbox
  2. Understand necessary FHIR resource types for your app
  3. Register for EHR developer programs
  4. Test with Apidog using mock patient data
  5. Implement HIPAA-compliant data handling

Try Apidog today


FAQ

What’s the difference between FHIR and HL7 v2?

HL7 v2 is an older messaging standard for hospital interfaces. FHIR is a modern REST API standard. Most new integrations use FHIR, but HL7 v2 is still common in legacy systems.

Do I need a BAA to use EHR APIs?

Yes, if handling PHI. A Business Associate Agreement is 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 in Epic’s App Orchard marketplace. For sandbox testing, use their public sandbox. Production access requires hospital approval.

What’s a patient context?

SMART on FHIR tokens include a patient ID. API calls are limited to that patient’s data, restricting access to only what’s authorized.

Can I write data to EHRs?

Yes, with limitations. Most EHRs allow creating Observations and updating patient demographics. Writing diagnoses or medications often requires additional clinical approval.

How do I handle terminology codes?

FHIR uses standard terminologies:

  • LOINC for labs/observations
  • SNOMED CT for clinical concepts
  • ICD-10 for diagnoses
  • RxNorm for medications Use these when creating resources.

What about international healthcare?

FHIR is global, but each country may have its own implementation guide. The US uses US Core profiles. Check your region’s specifications.

Top comments (0)