A Python toolkit for the Particle Health API — the nationwide health data network that pulls clinical records from thousands of sources with a single query. Register patients, submit queries, and retrieve data in Flat JSON, CCDA, or FHIR format.
- Python 3.11+
- Particle Health credentials — you need a
client_id,client_secret, andscope_idfrom your Particle Health dashboard
-
Clone and set up a virtual environment
git clone <repo-url> && cd particle-health-starters python3 -m venv .venv source .venv/bin/activate
-
Install dependencies
pip install -e ".[dev]" -
Configure credentials
cp .env.example .env # Edit .env with your Particle Health credentialsYour
.envshould look like:PARTICLE_CLIENT_ID=your-client-id PARTICLE_CLIENT_SECRET=your-client-secret PARTICLE_SCOPE_ID=projects/your-scope-id -
Validate setup
python workflows/check_setup.py
Expected output:
Checking Particle Health API setup... 1. Checking environment variables... OK: All required variables found 2. Testing authentication... OK: Authentication successful --- Setup OK --- Environment: sandbox -
Run the demo
python workflows/hello_particle.py
This registers a demo patient, submits a query, polls for completion (2-5 minutes), and prints a summary of the clinical data returned. Expected output:
=== Hello Particle === 1. Registering demo patient... Patient ID: ... 2. Submitting clinical data query... 3. Waiting for query to complete (this may take 2-5 minutes)... Status: COMPLETE 4. Retrieving flat data... === Data Summary === Resource types returned: allergies: 4 records encounters: 12 records medications: 8 records ...
If you want to see what Particle flat data looks like before setting up credentials, open the notebook:
notebooks/explore_flat_data.ipynb
It loads sample-data/flat_data.json (included in this repo) and walks through resource types, medications, problems, vital signs, and common transformations. No API calls, no credentials — just a JSON file and Python.
For users who want to run each step individually instead of hello_particle.py:
Register a patient
python workflows/register_patient.pyReturns a particle_patient_id you'll need for the next steps. Example output:
Patient registered successfully!
Particle Patient ID: 12345678-1234-1234-1234-123456789012
Your Patient ID: test-elvira-valadez
You can also pass a JSON file or inline JSON:
python workflows/register_patient.py patient.json
python workflows/register_patient.py '{"given_name": "John", ...}'Submit a query
python workflows/submit_query.py <particle_patient_id>Submits the query and polls until completion (up to 5 minutes). Example output:
Query completed!
Status: COMPLETE
Files available: ['flat', 'ccda']
Retrieve data
python workflows/retrieve_data.py <particle_patient_id> flat
python workflows/retrieve_data.py <particle_patient_id> ccdaFlat and CCDA are the two formats available in the sandbox. FHIR is production-only (see Data Formats).
Document submission is a separate flow from query/retrieval. It uploads a clinical document for a patient:
# Submit a CCDA (XML) document
python workflows/submit_document.py <patient_id>
# Submit a PDF document
python workflows/submit_document.py <patient_id> pdfThe <patient_id> here is your external patient ID (assigned during registration), not the Particle patient UUID.
These scripts call the Particle API directly with curl or httpx — no SDK required. Useful for debugging, testing endpoints, or as copy-paste references for your own integration.
source quick-starts/curl/auth.sh # sets $TOKEN
bash quick-starts/curl/register_patient.sh
bash quick-starts/curl/submit_query.sh <particle_patient_id>
bash quick-starts/curl/retrieve_data.sh <particle_patient_id> flatpython quick-starts/python/auth.py
python quick-starts/python/register_patient.py
python quick-starts/python/submit_query.py <particle_patient_id>
python quick-starts/python/retrieve_data.py <particle_patient_id> flat| Step | cURL | Python |
|---|---|---|
| Auth | quick-starts/curl/auth.sh |
quick-starts/python/auth.py |
| Register | quick-starts/curl/register_patient.sh |
quick-starts/python/register_patient.py |
| Query | quick-starts/curl/submit_query.sh |
quick-starts/python/submit_query.py |
| Retrieve | quick-starts/curl/retrieve_data.sh |
quick-starts/python/retrieve_data.py |
| Submit Document | quick-starts/curl/submit_document.sh |
quick-starts/python/submit_document.py |
| Manage Documents | quick-starts/curl/manage_documents.sh |
quick-starts/python/manage_documents.py |
| Format | Sandbox | Production | Description |
|---|---|---|---|
| Flat | Yes | Yes | Particle's denormalized JSON — each resource type is a flat list of records with string values. Easiest to parse and load into a database. |
| CCDA | Yes | Yes | C-CDA XML documents in a ZIP file. Standard clinical document format used across EHRs. |
| FHIR | No | Yes | FHIR R4 Bundle (JSON). Richest clinical semantics, but not available in sandbox. |
Recommendation: Start with flat for data analysis and pipeline work. Use CCDA when you need the original clinical documents. Use FHIR in production when you need standard interoperability.
| Endpoint | Method | Description |
|---|---|---|
/auth |
GET | Get JWT token (custom headers) |
/api/v2/patients |
POST | Register a patient |
/api/v2/patients/{id}/query |
POST | Submit a query |
/api/v2/patients/{id}/query |
GET | Check query status |
/api/v2/patients/{id}/flat |
GET | Get flat JSON data |
/api/v2/patients/{id}/fhir |
GET | Get FHIR Bundle (production only) |
/api/v2/patients/{id}/ccda |
GET | Get CCDA ZIP |
/api/v1/documents |
POST | Submit a clinical document (multipart) |
/api/v1/documents/{id} |
GET | Retrieve document metadata |
/api/v1/documents/{id} |
DELETE | Delete a document |
/api/v1/documents/patient/{id} |
GET | List all documents for a patient |
- Sandbox:
https://sandbox.particlehealth.com - Production:
https://api.particlehealth.com
| Field | Required | Format |
|---|---|---|
given_name |
Yes | String |
family_name |
Yes | String |
date_of_birth |
Yes | YYYY-MM-DD |
gender |
Yes | MALE or FEMALE |
postal_code |
Yes | 5 or 9 digit ZIP |
address_city |
Yes | String |
address_state |
Yes | Two-letter abbreviation (e.g., "MA", not "Massachusetts") |
address_lines |
No | Array of strings |
ssn |
No | XXX-XX-XXXX |
telephone |
No | XXX-XXX-XXXX |
patient_id |
Yes | Your external ID |
email |
No | String |
address_state must be a two-letter abbreviation. The API rejects full state names — use "MA", not "Massachusetts".
FHIR endpoint returns 404 in sandbox. The /fhir endpoint is production-only. Use flat or ccda in sandbox.
Query takes 2-5 minutes. This is normal — Particle queries a nationwide network. The SDK polls with exponential backoff automatically.
Flat data returns empty in sandbox. Sandbox only returns data for seeded test patients (e.g., Elvira Valadez-Nucleus). Arbitrary demographics will query successfully but return {}.
For more issues (overlay errors, 404 after query submission, timeout tuning), see docs/troubleshooting.md.
pytest -vThe .env file (or environment variables) supports these settings:
| Variable | Required | Default | Description |
|---|---|---|---|
PARTICLE_CLIENT_ID |
Yes | — | Your Particle client ID |
PARTICLE_CLIENT_SECRET |
Yes | — | Your Particle client secret |
PARTICLE_SCOPE_ID |
Yes | — | Your Particle scope ID |
PARTICLE_BASE_URL |
No | https://sandbox.particlehealth.com |
API base URL |
PARTICLE_TIMEOUT |
No | 30 |
Request timeout in seconds |
To switch to production:
export PARTICLE_BASE_URL="https://api.particlehealth.com"particle-health-starters/
workflows/ # SDK-based workflow scripts
hello_particle.py # End-to-end demo (start here)
check_setup.py # Validate credentials
register_patient.py # Register a patient
submit_query.py # Submit + poll a query
retrieve_data.py # Retrieve flat/ccda/fhir data
submit_document.py # Submit a clinical document
manage_documents.py # Get, list, or delete documents
quick-starts/ # No-SDK scripts (direct API calls)
curl/ # cURL scripts
python/ # httpx scripts
notebooks/
explore_flat_data.ipynb # Explore flat data (no credentials needed)
sample-data/
flat_data.json # Sample flat JSON response
ccda_data.zip # Sample CCDA ZIP
ccda_data/ # Extracted CCDA XML files
src/particle/ # SDK source code
core/ # Auth, HTTP client, settings, errors
patient/ # Patient registration
query/ # Query submission + retrieval
document/ # Document submission
tests/ # Test suite
docs/
troubleshooting.md # Full troubleshooting guide