Skip to main content

Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you've safely connected to the .gov website. Share sensitive information only on official, secure websites.

Handling pagination

A single patient can have hundreds of claims. Blue Button paginates search results: 10 per page by default, 50 max. Here’s how to work through them.

How pagination works

Search responses include a link array with URLs for navigating the result set:

{
"resourceType": "Bundle",
"total": 89,
"link": [
{
"relation": "self",
"url": "https://sandbox.bluebutton.cms.gov/v3/fhir/ExplanationOfBenefit?startIndex=0&_count=10&patient=..."
},
{
"relation": "first",
"url": "https://sandbox.bluebutton.cms.gov/v3/fhir/ExplanationOfBenefit?startIndex=0&_count=10&patient=..."
},
{
"relation": "next",
"url": "https://sandbox.bluebutton.cms.gov/v2/fhir/ExplanationOfBenefit?startIndex=10&_count=10&patient=..."
},
],
"entry": [ ... ]
}
Link relationDescription
selfThe current page
firstFirst page of results
nextNext page (absent on the last page)
previousPrevious page (absent on the first page)

To get the next page, fetch the URL from the next link:

Terminal
curl "https://sandbox.bluebutton.cms.gov/v3/fhir/ExplanationOfBenefit?_offset=10&_count=10&patient=..." \
-H "Accept: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

WARNING

Always use the full URL from Bundle.link. Don’t construct pagination URLs yourself. The server may include parameters you’re not aware of.

Control page size

Use count to control how many resources come back per page:

Terminal
# Get 50 claims per page (maximum)
curl "https://sandbox.bluebutton.cms.gov/v3/fhir/ExplanationOfBenefit?_count=50" \
-H "Accept: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
ParameterDefaultMaximum
_count1050

Larger pages mean fewer requests but bigger responses. If you’re pulling all claims for a patient, _count=50 reduces the number of round trips.

Build a complete dataset

To fetch all claims for a patient, follow the links below until there are no more. The following is some sample code that will help you get started:

import requests
def fetch_all_claims(base_url, access_token):
claims = []
url = f"{base_url}/ExplanationOfBenefit?_count=50"
headers = {
"Accept": "application/json",
"Authorization": f"Bearer {access_token}"
}
while url:
response = requests.get(url, headers=headers)
bundle = response.json()
for entry in bundle.get("entry", []):
claims.append(entry["resource"])
# Find the next link, if any
url = None
for link in bundle.get("link", []):
if link["relation"] == "next":
url = link["url"]
break
return claims

For actual production usage, we recommend adding more robust error handling for cases such as disconnections and mid-loop token expirations. Also note that as claims progress through adjudication, they will appear several times, so deduplication is necessary.

Handle data changes during pagination

Claims data can be updated while you’re paginating through results. Because pagination is offset-based, a claim added or removed mid-walk shifts every record after it so you can skip a claim entirely or read the same one twice.

Some strategies for handling this:

  • Bound the window before you start. Capture a timestamp, then cap every page with _lastUpdated=le{timestamp}. The result set stays stable no matter what changes upstream, and you resume the next sync with gt{timestamp}.
  • Use _lastUpdated to detect changes. After your initial full fetch, use _lastUpdated=gtYOUR_LAST_SYNC_DATE in subsequent requests to get only what changed. The operator prefix is required: lt, le, gt, or ge.
  • Accept eventual consistency. For apps that display claims rather than reconcile them, an occasional skipped or duplicated record is fine. The next full sync will catch up.
Note: _lastUpdated returns added and modified claims only: it can’t tell you a claim was removed, so schedule a periodic full resync. On v3, pass _source or _tag alongside it; otherwise Blue Button applies _source=NCH and your sync will miss every update to partially adjudicated claims.
Tip: For most apps, the simplest approach is: do a full fetch on first load bounded with le{timestamp}, then use _lastUpdated for incremental updates going forward, with a periodic full resync to catch removals.
Looking for U.S. government information and services?
Visit USA.gov