> For the complete documentation index, see [llms.txt](https://docs.bloodgpt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bloodgpt.com/interpretation-and-results/download.md).

# Download API

Retrieve a completed test's full analysis as JSON — parameters, overview, follow-ups, trends and product recommendations

{% hint style="info" %}
`GET /api/v1/download/{test_id}` returns the **complete analysis of one test as JSON** — the same content that goes into the PDF report, in structured form. Use it when you submitted through [`/api/v1/process`](/submitting-tests/process.md) but want the data rather than (or as well as) the generated PDF.
{% endhint %}

## When to use Download

There are three ways to read a test's results, and they answer different questions:

| Endpoint                                                              | Returns                                                                                                                                       | Use when                                                          |
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `GET /api/v1/download/{test_id}`                                      | Full analysis as JSON: parameters **plus** overview, follow-ups, panels, trends, product recommendations                                      | You want the interpreted report as data                           |
| `GET /api/v1/upload/{test_id}`                                        | Recognized document data as flat JSON or a FHIR R4 Bundle — see [Upload API](/submitting-tests/upload.md#step-2-poll-for-recognition-results) | You want what was extracted from the document, or FHIR for an EHR |
| `download_url` from [`/api/v1/process`](/submitting-tests/process.md) | The rendered PDF, from the CDN                                                                                                                | You want the patient-facing report file                           |

{% hint style="warning" %}
**This endpoint only serves JSON.** Sending `Accept: application/pdf` answers `501 pdf_not_implemented` — the PDF is delivered through the `download_url` that [`/api/v1/process`](/submitting-tests/process.md) returns, not from here. `Accept` is otherwise ignored; JSON is the default and the only implemented format.
{% endhint %}

***

## Get the Analysis

**Endpoint:** `GET /api/v1/download/{test_id}`

```bash
curl https://sandbox.bloodgpt.com/api/v1/download/clxyz789012 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
```

### Response (200 OK)

```json
{
  "success": true,
  "message": "Blood test results retrieved successfully",
  "data": {
    "id": "clxyz789012",
    "status": "completed",
    "external_id": "lab-report-2025-001",
    "patient_id": "patient-123",
    "patient_info": {
      "name": "Jane Doe",
      "birth_date": "1985-04-12",
      "gender": "female",
      "weight": 68,
      "height": 170
    },
    "processing": {
      "created_at": "2025-09-09T10:00:00.000Z",
      "updated_at": "2025-09-09T10:07:31.000Z",
      "completed_at": "2025-09-09T10:07:31.000Z"
    },
    "results": [
      {
        "name": "Hemoglobin",
        "value": 14.5,
        "unit": "g/dL",
        "reference_min": 12.0,
        "reference_max": 16.0,
        "normal_min": 12.0,
        "normal_max": 16.0,
        "is_healthy": true,
        "parameter_details": "Value is within the normal range.",
        "panel": "Hematology",
        "loinc_code": "718-7",
        "allergen": false
      }
    ],
    "scan_results": {
      "total_parameters": 42,
      "healthy_count": 38,
      "attention_needed_count": 4
    }
  }
}
```

The payload is always wrapped: `success`, `message`, and the test itself under `data`.

### `data` Fields

| Field                       | Type   | Always present | Description                                                                   |
| --------------------------- | ------ | -------------- | ----------------------------------------------------------------------------- |
| `id`                        | string | Yes            | The test id                                                                   |
| `status`                    | string | Yes            | Stored test status — always `completed` for a successful response             |
| `external_id`               | string | No             | Your identifier from the submission                                           |
| `patient_id`                | string | No             | Your patient identifier                                                       |
| `patient_info`              | object | No             | `name`, `birth_date`, `weight`, `height`, `gender`                            |
| `processing`                | object | Yes            | `created_at`, `updated_at`, `completed_at`, `error_message`                   |
| `results`                   | array  | Yes            | One entry per parameter — see below                                           |
| `scan_results`              | object | Yes            | `total_parameters`, `healthy_count`, `attention_needed_count`                 |
| `test_overview`             | object | No             | Narrative summary, patterns, considerations, next steps                       |
| `follow_up_recommendations` | object | No             | Retest schedules and urgent tests                                             |
| `panel_overviews`           | array  | No             | Per-panel summary and healthy score                                           |
| `parameter_trends`          | object | No             | Direction and history per parameter — returning patients only                 |
| `product_recommendations`   | object | No             | Matches from your [product catalog](/patients-and-catalog/product-catalog.md) |

The interpreted blocks — `test_overview`, `follow_up_recommendations`, `panel_overviews`, `parameter_trends` — are present only once interpretation has produced them. `parameter_trends` additionally needs prior tests for the same patient to compare against.

### `results[]`

| Field                             | Type                     | Description                                                                                                   |
| --------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `name`                            | string                   | Parameter name                                                                                                |
| `value`                           | number \| string \| null | Number for quantitative results; string for qualitative (`"Negative"`); `null` when the measurement is absent |
| `unit`                            | string                   | Unit of measurement (empty for unitless observations)                                                         |
| `reference_min` / `reference_max` | number \| null           | Reference range from the source                                                                               |
| `normal_min` / `normal_max`       | number \| null           | Normal range used for the healthy assessment                                                                  |
| `is_healthy`                      | boolean                  | Whether the value falls in the normal range                                                                   |
| `parameter_details`               | string                   | Per-parameter interpretation text                                                                             |
| `panel`                           | string                   | Panel the parameter belongs to, when grouped                                                                  |
| `loinc_code`                      | string                   | LOINC code                                                                                                    |
| `code`                            | string                   | Passed through from your submission, for mapping back to your own ids                                         |
| `allergen`                        | boolean                  | Whether the parameter is an allergen result                                                                   |
| `range_type` / `range_source`     | string                   | How the range was determined and where it came from                                                           |

***

## Errors

| Status | `error`               | Meaning                                                           |
| ------ | --------------------- | ----------------------------------------------------------------- |
| `400`  | `missing_test_id`     | No test id in the path                                            |
| `404`  | `test_not_found`      | No test with that id in your organization                         |
| `422`  | `test_not_ready`      | The test has not finished — the message names the current status  |
| `501`  | `pdf_not_implemented` | `Accept: application/pdf` was requested; only JSON is served here |
| `500`  | `database_error`      | Results could not be read — safe to retry                         |

A `422` is returned in three situations, distinguished by the message: processing failed (the message carries the failure reason), the test is still running (`Test is still <status>. Please check back later.`), or the analysis produced no parameters yet (`Analysis not yet available.`). Treat the second and third as "poll again"; see [Errors & Validation](/reference/errors.md) for the shared contract.

{% hint style="info" %}
This endpoint requires a **finished** test. It is not a progress endpoint — poll [`GET /api/v1/upload/{test_id}`](/submitting-tests/upload.md#step-2-poll-for-recognition-results) for status, or take a [webhook](/interpretation-and-results/webhooks.md), then call this once the test is complete.
{% endhint %}

***

## Getting JSON out of the Processing API

[`/api/v1/process`](/submitting-tests/process.md) always answers with a `download_url` for the PDF; there is no request option that swaps the output to JSON. To get the data instead, keep the `test_id` from the `202` response and read it here once processing is done:

```bash
API=https://sandbox.bloodgpt.com
KEY=YOUR_API_KEY

# 1. Submit — keep test_id, not just download_url
TEST_ID=$(curl -s -X POST $API/api/v1/process \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "external_id": "lab-report-2025-001",
        "patient_id": "patient-123",
        "payload": {"parameters": [{"name": "Hemoglobin", "value": 14.5, "unit": "g/dL"}]}
      }' | jq -r .test_id)

# 2. Read the analysis as JSON once it is ready (422 until then)
curl -s $API/api/v1/download/$TEST_ID \
  -H "Authorization: Bearer $KEY" \
  -H "Accept: application/json" | jq .
```

A [webhook](/interpretation-and-results/webhooks.md) removes the waiting: `processing_completed` carries `results[]` in its payload alongside `pdf_url`, so for many integrations no call to this endpoint is needed at all.
