*** September 2026 Major Release ***Read and watch
Document type

Extracting data from lab reports

Clinical laboratories print results from their laboratory information systems, each with its own letterhead, panel layout and units. Insurers, clinical research teams and care coordinators receive reports from many labs every day, as PDFs from lab portals, faxed pages or patient uploads.

Clinical review needs each test's name, result, unit and reference range, with the patient's age and sex that set the range. Those values feed an electronic health record or a claims review system, which compares each result with its range.

Page 1 of a sample Lab Report, with extracted values outlined
3,850Markdown characters
103parse blocks
112fields extracted
The problem

What makes lab reports hard

Flags in their own column

An H or L between the test name and the value marks an abnormal result, often with the whole row set in bold.

Ranges written as text

Reference ranges print as text such as 13 - 17 or < 2, and each one holds a low and a high bound.

Units that vary by region

Platelet counts print in lakhs per cubic millimeter at labs in India and in thousands per microliter at labs in the US.

How it works

Parse, extract, verify

1

Parse

Parse turns the document into reading-order Markdown and a hierarchy of blocks (text, tables, figures), and grounds each block to its exact location on the page.

2

Extract

You define the fields in a JSON schema, and Extract returns their values in a consistent, structured format, each linked back to where it appears in the Markdown.

3

Verify

Each value links back to where it appears, down to the character range in the Markdown, and the page and bounding box in the source document, so every result can be verified.

Worked example

One lab report, end to end

About this document: A complete blood count report from LabSmart, a laboratory information system vendor, printed on its sample letterhead to show the software's output. Two portrait pages: a patient and sample header, a 13-row hematology results table, a clinical notes panel and signature blocks. Page 1 is featured. Every patient detail is the vendor's placeholder.

Processing objective: The objective is to read one result that falls outside its range. The lymphocyte count is a percentage, flagged low on the report, and sits below the lower bound of its reference range. Each of those values grounds to its own cell in that row.

Demonstration fields: Test, Result, Unit, Reference range low

The document

2 of 2 pages
  • Page 1 of the sample Lab ReportPage 1shown below
  • Page 2 of the sample Lab ReportPage 2

Where each value came from

Page 1 of the sample Lab Report, with every featured field outlined
Testtest_results[3].test_name
LYMPHOCYTE
The region of the sample Lab Report containing Test, with the extracted value outlined
extraction_metadata["test_results[3].test_name"]
{
  "value": "LYMPHOCYTE",
  "page": 1,
  "range": {
    "end": 818,
    "start": 808
  },
  "box": {
    "xmin": 0.03042,
    "ymin": 0.3805,
    "xmax": 0.36666,
    "ymax": 0.39854
  }
}
Resulttest_results[3].result
18
The region of the sample Lab Report containing Result, with the extracted value outlined
extraction_metadata["test_results[3].result"]
{
  "value": "18",
  "page": 1,
  "range": {
    "end": 831,
    "start": 827
  },
  "box": {
    "xmin": 0.36654,
    "ymin": 0.38058,
    "xmax": 0.46241,
    "ymax": 0.39856
  }
}
Unittest_results[3].unit
%
The region of the sample Lab Report containing Unit, with the extracted value outlined
extraction_metadata["test_results[3].unit"]
{
  "value": "%",
  "page": 1,
  "range": {
    "end": 841,
    "start": 840
  },
  "box": {
    "xmin": 0.46236,
    "ymin": 0.3806,
    "xmax": 0.69816,
    "ymax": 0.39862
  }
}
Reference range lowtest_results[3].reference_values[0].reference_low
20
The region of the sample Lab Report containing Reference range low, with the extracted value outlined
extraction_metadata["test_results[3].reference_values[0].reference_low"]
{
  "value": 20,
  "page": 1,
  "range": {
    "end": 857,
    "start": 850
  },
  "box": {
    "xmin": 0.69812,
    "ymin": 0.38066,
    "xmax": 0.96879,
    "ymax": 0.39868
  }
}

How to try it yourself

Everything on this page is reproducible. The sample, its schema, and the Parse and Extract API outputs are published. You can repeat the same two calls from whichever surface you already build on.

  • Claude Skilldescribe the job; the agent writes it
  • CLIno code at all
  • MCPexpose ADE docs as an agent tool
  • REST APIany language
  • Pythonlandingai-ade
  • TypeScriptlandingai-ade
Why LandingAI?

Documents into data

Accurate

The best accuracy on the difficult documents other tools get wrong, such as messy scans, complicated tables, checkboxes and handwriting.

Auditable

Every answer cites its sources. Values come back with an audit trail (page, bounding box, character range) so a human can review them.

Agentic

Applies multi-step reasoning, and plans before it acts. Handles documents it has never seen before and adapts to variations.

API-first

Composable APIs that drop into any tech stack and can be called from anywhere. Easy to build on and embed in your pipelines and products.

Affordable

Cost-competitive rates with complete price transparency. Spend controls before you run, and every job reports its exact cost.

Enterprise-grade

Built for production and scale from day one. Fully observable and monitored, running securely in our cloud or yours.

Built for high-volume, high-throughput production

  • Zero data retention
  • Priority rate limits
  • Enhanced support
  • VPC and on-prem deployment

Available on Team and Enterprise plans.

Compare plans
Capabilities

Lab Report processing highlights

Test name and result
Unit for every result
Reference low and high
Open-ended ranges
Regional units such as lakhs
Test category
Report date and time
Patient age and sex
Order and patient IDs
Lab phone and website
Results in printed order
Bounding box for every value
FAQs

Questions, answered

Yes. Define the bounds as numbers in your schema. A range printed as two figures comes back as a low and a high, and an open-ended range such as under 2 comes back with its upper bound.

Yes. Describe the unit as its own field. Each result comes back with the unit printed beside it, including regional units, so values from different labs can be converted before they are compared.

Yes. A schema names the fields you want and describes them in plain language. Extraction reads the parsed report for meaning, so one schema covers each lab's own letterhead and labels.

Every extracted value carries the character range it was quoted from in the parsed Markdown. The Ground API maps that range to a page and a bounding box, so a reviewer sees each result in the row that printed it.

Start extracting lab report data

Reliable, structured outputs with full traceability in minutes