Healthcare/NHS, UK
National program

NHS FHIR integration in the UK

The NHS runs a mix of FHIR R4 under UK Core and older STU3 programmes like GP Connect. This guide explains which version each programme uses and shows how to call both from Python.

R4UK Core FHIR version
STU3GP Connect version
4UK nations covered by UK Core
nhs_patient_lookup.py
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class NHSPatientLookup(Service):
    name = 'demo.nhs.patient-lookup'

    def handle(self) -> 'None':

        # A UK Core R4 endpoint, OAuth handled for you
        client = self.fhir['FHIR.NHS']

        # Look up a patient by their NHS number
        patient = client.resources('Patient').search(
            identifier = 'https://fhir.nhs.uk/Id/nhs-number|9000000009',
        ).first()

        self.logger.info('Found %s', patient['id'])

What FHIR standards does the NHS use?

The strategic standard is FHIR UK Core - a FHIR R4 localisation endorsed by NHS England, with profiles spanning England, Scotland, Wales and Northern Ireland. New APIs in the NHS API catalogue are built against UK Core, with OAuth-based application access and CIS2 for user-facing authentication.

Several established programmes still run on FHIR STU3 - GP Connect is the prominent one, as is ITK3 Messaging Distribution over MESH. This split is a common source of confusion in NHS integrations - version answers that are true for one API are wrong for another, so check each API's entry in the catalogue rather than assuming.

ProgrammeFHIR versionWhat it provides
UK Core APIsR4The strategic profiles new NHS APIs are built on
Personal Demographics Service (PDS)R4Patient demographics on the Spine
GP ConnectSTU3GP record access, appointments, structured data
ITK3 / MESH messagingSTU3 documentsStore-and-forward document exchange

What is GP Connect and how do I use it?

GP Connect exposes GP practice records to other care settings - HTML views and structured data for medications and allergies, plus appointment management. It runs on FHIR STU3 through the Spine Secure Proxy, and callers authenticate system-to-system with signed JWTs describing the calling system and user context.

From an integration layer, an STU3 API is consumed the same way as R4 - the resource shapes differ, the HTTP and OAuth mechanics do not. Keeping GP Connect calls in their own services, separate from UK Core R4 services, keeps the version difference from leaking across the codebase.

How do I call an NHS FHIR API from Python?

One outgoing FHIR connection per NHS environment - sandbox, integration, production - each with its OAuth definition, and the services stay identical across environments. The service below reads a patient from a UK Core endpoint and lists their medication requests.

uk_core_medications.py
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class ReadMedications(Service):
    name = 'demo.nhs.read-medications'

    def handle(self) -> 'None':

        # The connection points at a UK Core R4 endpoint
        client = self.fhir['FHIR.NHS']

        # The NHS number is the national identifier
        patient = client.resources('Patient').search(
            identifier = 'https://fhir.nhs.uk/Id/nhs-number|9000000009',
        ).first()

        # Active medication requests for that patient
        medications = client.resources('MedicationRequest').search(
            subject = f'Patient/{patient.id}',
            status = 'active',
        )

        for medication in medications.fetch():
            name = medication.get_by_path('medicationCodeableConcept.text')
            self.logger.info('Medication: %s', name)

How do I handle HL7 v2 feeds alongside FHIR?

UK hospital systems - PAS, LIMS, RIS - still emit HL7 v2 over MLLP, and integrating them with FHIR-facing services is the standard bridge pattern - an MLLP channel receives the v2 message, a Python service transforms it and the FHIR side is one save.

pas_to_fhir.py
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class PASToFHIR(Service):
    name = 'demo.nhs.pas-to-fhir'

    def handle(self) -> 'None':

        # An ADT from the PAS, parsed by the MLLP channel ..
        msg = self.request.input

        # .. the NHS number arrives in PID-3 ..
        nhs_number = msg.pid.patient_identifier_list.id_number
        family_name = msg.pid.patient_name.family_name

        # .. and becomes a UK Core identifier on the FHIR side.
        client = self.fhir['FHIR.NHS']

        patient = client.resource('Patient',
            identifier = [{
                'system': 'https://fhir.nhs.uk/Id/nhs-number',
                'value': nhs_number,
            }],
            name = [{'family': family_name}],
        )

        patient.save()

Frequently asked questions

Check the API's page in the NHS API catalogue - it names the FHIR version, the auth pattern and the environments. UK Core APIs are R4, GP Connect and ITK3 are STU3, and nothing NHS-facing is R5.

Clinical risk management standards - DCB0129 for manufacturers of health IT and DCB0160 for the organizations deploying it. They are organizational obligations about clinical safety cases, not product features - no platform makes you compliant by itself, Zato included.

The national infrastructure that hosts services like PDS and the Electronic Prescription Service. Modern access is through the NHS API platform with OAuth, and the older SSP fronts GP Connect traffic.

Yes - the client works with the resources a server returns regardless of version, since resources are plain data with path access. Version differences live in the profiles, which is why R4 and STU3 calls are best kept in separate services.

Ready to integrate with NHS APIs?

Get started with Zato and call your first UK Core endpoint in minutes.

Open source In Python