FHIR (pronounced “fire”) stands for Fast Healthcare Interoperability Resources.
It was developed by the HL7 International organization as a standard for exchanging healthcare information electronically.
Let's bring a bit of history.
Historically, medical information was scattered around hospitals, ambulances and other healthcare points. If a patient needs to move to an alternative general practitioner he needs to take his/her personal health records (EHR) on paper and physically brings them to another healthcare provider. Again, if someone was brought to an emergency room, to get access to his previous health information was almost impossible.
Then electronic databases emerged. Typically they were developed by different software companies for different hospitals and again, moving information between them was a nightmare.
So, the need for a system that would make different medical information systems communicate with one another was obvious.
The first attempts to solve this problem was made in 1990s by HL7 organization.
HL7 stands for Health Level Seven, a set of international technical standards that allow different healthcare software systems to share, integrate, and exchange patient information.
First came the HL7 Version 2 messaging standard. It established a practical way for healthcare applications to exchange events such as patient admissions, discharges, transfers, and laboratory results.
In the 2000s the HL7 Version 3 was developed.
HL7 began developing FHIR in the 2011. Release 3 appeared in 2017, and FHIR Release 4 (R4) was published in the 2019.
but FHIR became a central building block for healthcare APIs in the 2020s
FHIR is built around several entities.
To exchange data, FHIR usually uses the JSON format but also natively supports XML, NDJSON and even HTML.
To select the format FHIR uses standard HTTP content negotiation (FHIR server supports all options internally)
GET /Patient/123
Accept: application/fhir+json
or
GET /Patient/123
Accept: application/fhir+xml
# When creating or updating a resource, the Content-Type header indicates the format of the request body:
Content-Type: application/fhir+json
Before requesting anything one needs to know the URL of clinic's end-point.
In FHIR, there are actually two different kinds of lookups:
For example, for EPIC the endpoint is https://open.epic.com/MyApps/Endpoints.
Usually, one will be provided with an endpoint when contacting EHR provider or you can easily google it.
In general, let's assume we have it like this, https://directory.example.org/fhir
Many large EHR providers work with multiple clinics and you can get User-access Brands and Endpoints, which is a Bundle with a list of resources available.
Having endpoint you can ask
GET /HealthcareService?name=cardiology
Accept: application/fhir+json
The directory returns a FHIR Bundle containing matching services.
{
"resourceType": "Bundle",
"type": "searchset",
"entry": [
{
"resource": {
"resourceType": "HealthcareService",
"id": "cardiology-01",
"name": "Cardiology",
"providedBy": {
"reference": "Organization/clinic-01"
},
"location": [
{
"reference": "Location/site-01"
}
],
"endpoint": [
{
"reference": "Endpoint/api-01"
}
]
}
}
]
}
The flow looks like this:
GET /Encounter/enc-001
Accept: application/fhir+json
Response: Encounter
{
"resourceType": "Encounter",
"id": "enc-001",
"status": "finished",
"class": {
"system": "https://terminology.hl7.org/CodeSystem/v3-ActCode",
"code": "AMB",
"display": "ambulatory"
},
"subject": {
"reference": "Patient/pat-001"
},
"period": {
"start": "2026-10-20T09:00:00Z",
"end": "2026-10-20T09:30:00Z"
},
"reasonReference": [
{
"reference": "Condition/cond-001"
}
],
"diagnosis": [
{
"condition": {
"reference": "Condition/cond-001"
}
}
]
}
{
"resourceType": "Observation",
"id": "obs-001",
"status": "final",
"category": [
{
"coding": [
{
"system": "https://terminology.hl7.org/CodeSystem/observation-category",
"code": "vital-signs"
}
]
}
],
"code": {
"text": "Systolic blood pressure"
},
"subject": {
"reference": "Patient/pat-001"
},
"encounter": {
"reference": "Encounter/enc-001"
},
"valueQuantity": {
"value": 120,
"unit": "mmHg",
"system": "https://unitsofmeasure.org",
"code": "mm[Hg]"
}
}
To test your software's FHIR integration you can use either sandboxes provided by EHR providers or a test FHIR server in docker
# Local FHIR server, so fhir/client.py and fhir/flatten.py can be written against real
# FHIR payloads before credentials for the production server arrive.
#
# docker compose -f docker/docker-compose-fhir.yaml up -d
# curl -s http://localhost:8080/fhir/metadata | jq '.rest[0].security'
#
# Then put FHIR_BASE_URL=http://localhost:8080/fhir in .env and leave FHIR_TOKEN empty.
# Uses the embedded H2 database: data is lost on `down`, so keep seeding repeatable.
services:
fhir:
image: hapiproject/hapi:latest
ports:
- "8080:8080"
environment:
HAPI_FHIR_FHIR_VERSION: R4
HAPI_FHIR_ALLOW_EXTERNAL_REFERENCES: "true"
HAPI_FHIR_BULK_EXPORT_ENABLED: "true"
# small page size locally, so client.py is forced to follow Bundle.link next
HAPI_FHIR_DEFAULT_PAGE_SIZE: "20"
HAPI_FHIR_MAX_PAGE_SIZE: "200"
On top of that you can import a larger dataset from Synthea.