If you work with FHIR APIs, you already know that _include and _revinclude are powerful but dangerous. A single search request can pull an entire patient record, plus every order, encounter, observation, and medication linked to it. In 2026, EHR vendors are enforcing tighter API gateways and lower latency budgets, which means learning how to optimize FHIR _include and _revinclude queries is no longer optional. The speed tricks below focus on one goal: keeping integrations responsive without pushing servers into timeout territory.
Why _include and _revinclude Queries Blow Up
At a REST level, _include tells the server to return related resources in the same bundle. _revinclude tells the server to find resources that reference the primary result. Both are graph operations hiding inside a search API. Servers must traverse relationships, serialize a bundle, and respect pagination. As the result set grows, the memory and CPU cost can balloon quickly.
For example, this request asks for a patient and every encounter and observation that reference the patient:
GET /Patient/123?_revinclude=Encounter:patient&_revinclude=Observation:patient
The server may return one patient, fifty encounters, and thousands of observations. If you already had those resources, the response is now slower and larger than necessary. Worse, many EHRs terminate the request before the bundle is even built.
Start with _count and _elements to Reduce Bundle Size
One of the fastest ways to tame a query is to shrink the data returned. The _count parameter limits the number of primary resources in the search response. The _elements parameter limits the fields on every returned resource. Together, they let you request only what the integration actually needs.
GET /Encounter?subject=Patient/123&_count=20&_elements=id,status,class,period&_include=Encounter:subject
This query returns 20 encounters plus the referenced patient(s), and each resource carries only the selected elements. In many FHIR servers, _elements also applies to included resources, which can dramatically reduce payload size.
Be careful: if you trim reference fields away, the include may not be useful. Keep id, meta, and the reference paths in your _elements list, and run a small test against the EHR to confirm the server handles this the way you expect.
Flip the Direction of _revinclude Wherever Possible
If you need all observations for a patient, the natural FHIR command is Patient?_revinclude=Observation:patient. But this makes the server start from one patient, then scan the observation table to find every matching row. If that patient has years of clinical data, the response quickly becomes enormous.
Instead, start from the observation side:
GET /Observation?subject=Patient/123&_count=100
This returns the observations directly, without forcing the server to attach the patient resource to every bundle entry. You can page through observations using the Bundle next link and fetch the patient resource separately. This pattern reduces duplicated patient data and can make paging much more predictable.
Likewise, reverse chaining with _has can narrow the primary resource set before you consider includes. For example, to find patients who actually have a systolic blood pressure reading, use:
GET /Patient?_has=Observation:patient:code=55284-4&_count=50
This returns only patients that have the target observation type, rather than every patient followed by a bulky _revinclude.
Break Multi-Include Queries into Parallel Requests
Adding several include parameters to one request is the fastest way to create a combinatorial explosion. This query is tempting but dangerous:
GET /Patient/123?_include=Patient:general-practitioner&_include=Patient:organization&_revinclude=Encounter:patient&_revinclude=Observation:patient
The server has to merge different relationship types into one bundle while keeping everything consistent. The response gets heavier with every added include, and the chance of a timeout climbs.
Instead, split the request into separate queries and run them concurrently from the client:
GET /Patient/123?_include=Patient:general-practitioner&_include=Patient:organization GET /Encounter?subject=Patient/123&_count=100&_sort=-date GET /Observation?subject=Patient/123&_count=100&_sort=-date
Each response is smaller, easier to page, and less likely to trip server limits. Parallel requests also let you stream results to the user as they arrive instead of waiting for one monolithic bundle.
Use Chained Search Parameters Instead of Includes
Not every related resource needs to be in the same response bundle. Chained search parameters follow references directly in the query string, which allows the server to use database indexes before assembling the output. Suppose you need encounters for a particular practitioner and the patient identifier:
GET /Encounter?practitioner=Practitioner/abc&patient.identifier=12345
This query uses a server-side join to filter encounters by the patient identifier. It is often faster than fetching all encounters and then trying to deduplicate patient data on the client.
Modern FHIR servers also support _filter, which can pre-narrow the dataset before includes are applied. While _filter syntax is not identical everywhere, a simple boolean expression such as _filter=status=active can reduce the number of resources that flow into _include and _revinclude. Always test the expression against the target EHR first.
Check Search Parameter Support and Indexes
The most optimized query in the world will still time out if the EHR does not index the search parameter behind it. Before relying on an include, check the server’s CapabilityStatement. Look for searchParam definitions under Rest.resource and verify that the parameter you need is supported. If it is missing, the server may be doing a full table scan to satisfy your query.
It is also worth monitoring response times for different search pairs. Some EHRs index subject on Observation but not patient on MedicationRequest. A query that uses _revinclude=MedicationRequest:patient can be much slower than MedicationRequest?subject=Patient/123 if the corresponding index is absent. Use the server’s logs, performance headers, and response time measurements to spot these differences.
Go Asynchronous or Use Bulk Data for Large Extractions
If you need a complete patient history, do not fight the search endpoint. Use asynchronous processing whenever the EHR supports it. The Prefer: respond-async header lets a server return a status URL immediately and fulfill the query in the background. You can poll the status endpoint and retrieve the result when it is ready.
For even larger jobs, FHIR Bulk Data Export is designed for efficient population-level or patient-level extraction. Instead of trying to load thousands of resources into a single search bundle, start an export and read NDJSON files. This approach is now well supported across many EHRs and puts far less pressure on the server’s query engine.
Conclusion
Optimize FHIR _include and _revinclude queries by thinking about what the server must do before it sends a response. Reduce the primary result size, reverse the query direction, split includes into parallel requests, use chained search and reverse chaining, and respect server index limitations. For large jobs, switch to asynchronous or bulk export methods. These techniques will not only prevent server timeouts but also make your EHR integrations feel faster and more reliable.
