Extract Lab Results From Medical PDFs Into Structured JSON
Extract lab results into JSON while retaining dates, units, reference ranges, and source citations.

Lab reports mix numbers, inequalities, text results, dates, units, and reference ranges in the same table. Flattening the page into a string can detach a result from the context that gives it meaning.
This guide extracts a small report into JSON using the Unsiloed Extract API. Each field retains its printed value, score, and optional source citation. The application flags missing or inconsistent evidence for review; it does not interpret results or make clinical decisions.
Download the Example
Download the Python project and unpack it into a folder. The included fixture contains sample data and no patient information. Python 3.11 or newer is required; dependencies were tested with Python 3.14.
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
python make_fixture.py
python main.py --response fixtures/extract_response.json --output output.json
python -m pytest -q
The offline command reads a saved extraction result for the sample and requires no API key. The JSON output contains a fields object for each row and a review list. The included tests cover missing values, citation geometry, repeated tests, and an application-selected score threshold.
Preserve Values as Printed
The fixture includes hemoglobin 13.2, CRP <0.5, a text result Negative, and glucose 104. Its schema asks for the test name, result, unit, reference range, and report date as strings. Keeping strings preserves qualifiers and textual values; it avoids silently converting <0.5 into 0.5.
The schema uses NOT_PRINTED as a missing-value sentinel because every field is required in the extraction schema. The normalizer converts that sentinel to null and retains the other fields. Missing test names, result values, or report dates are flagged for review. Units and reference ranges remain visible as null when absent; whether that requires review depends on the receiving workflow.

The report's individual values are nested in scored leaves. The normalizer reads each leaf's value, score, and citation separately; it never substitutes an array-level score for a cell. Current Unsiloed extraction results describe citations using page, bbox, page_width, and page_height in the result reference.
Check Source Evidence and Route Uncertainty
The project validates that a citation has a page and an ordered box inside the supplied page dimensions. Missing or invalid citations for populated fields generate review reasons. Bounds checks establish that a region can be inspected; they do not prove the text was read correctly.
Scores can be checked against a threshold chosen for your own labeled documents. The example leaves the threshold unset by default. Calling validate_lab(result, threshold=0.9) applies an illustrative floor to both score components; it is an application setting, not an accuracy guarantee.

Rows with the same test name and report date are kept and flagged for review. A second result for the same test on a different date remains a separate row. This simple key can flag legitimate repeat tests; review the source rather than deleting rows automatically.
Run Extraction on the Fixture
Set the key, then run the live path:
export UNSILOED_API_KEY="your-api-key"
python main.py --live --output output.json
The extraction.py adapter sends multipart pdf_file, a serialized schema_data, and enable_citations=true to /v2/extract, then polls /extract/{job_id}. It uses bounded request timeouts and a polling deadline and does not automatically resubmit a lost submission response.
The sample fixture returned four rows during the local check, including <0.5 and the text result. Extraction is probabilistic, so compare the output with the PDF rather than assuming another run returns identical wording or coordinates. The healthcare API comparison covers vendor evaluation; review your own privacy and deployment requirements before processing patient documents.
Move the Output to a Review Workflow
Retain the original file, the returned citations, and the review reasons together. A mapper into a clinical standard needs its own checks for codes, units, dates, and optional fields; this intermediate JSON is not itself a clinical record.
Unsiloed provides schema-driven extraction and source evidence for the document-reading step. Your application owns validation, access control, and downstream handling. Sign up for Unsiloed to evaluate it on your documents.
