> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anivahealth.com/llms.txt
> Use this file to discover all available pages before exploring further.

# List Biomarkers

> Browse the biomarker catalog

Returns a paginated catalog of biomarkers your API key can attach to appointments via [Add Individual Biomarkers](/api/appointments/add-individual-biomarkers). Archived biomarkers are excluded.

Only **blood-collectable** biomarkers are listed. Appointments are blood draws, so biomarkers whose sample is a non-blood material (urine, saliva, stool, swabs, and the like) are excluded — you can't collect them at a draw. Markers with no specific material (ratios, genetic markers) are kept.

## Request

### Query parameters

<ParamField query="test_location_id" type="string">
  Restrict the catalog to biomarkers testable at this location, and populate per-biomarker `pricing`
  for its routing pipeline. The filter is the **union** across every lab the location's logistic
  template supports: a biomarker is included if it is testable at at least one of those labs.
</ParamField>

<ParamField query="rate_card" type="string">
  Override which rate card the `yours` price uses: `retail`, `subscription`, or a billing-account
  slug your key may use. Defaults to the card on your API key's billing account, else retail. Only
  affects pricing (which requires `test_location_id`).
</ParamField>

<ParamField query="limit" type="number">
  Maximum results to return (1–1000). Omit to return the entire matching catalog — it's bounded (a
  few thousand rows at most). When omitted, `pagination.limit` echoes back `total`.
</ParamField>

<ParamField query="offset" type="number" default="0">
  Pagination offset. Must be ≥ 0.
</ParamField>

## Response

<ResponseField name="data" type="Biomarker[]">
  Array of biomarkers matching the query.

  <Expandable title="Biomarker fields">
    <ResponseField name="id" type="number">
      Stable biomarker identifier — pass this to [Add Custom
      Biomarkers](/api/appointments/add-individual-biomarkers).
    </ResponseField>

    <ResponseField name="slug" type="string">
      URL-safe identifier.
    </ResponseField>

    <ResponseField name="name" type="string">
      Display name.
    </ResponseField>

    <ResponseField name="description" type="string | null">
      Long-form description, when available.
    </ResponseField>

    <ResponseField name="primary_category" type="string | null">
      Primary category (e.g. `Hormones`, `Vitamins`).
    </ResponseField>

    <ResponseField name="secondary_category" type="string | null">
      Secondary category.
    </ResponseField>

    <ResponseField name="type" type="&#x22;clinical&#x22; | &#x22;genetic&#x22;">
      Whether this biomarker is a clinical measurement or a genetic marker.
    </ResponseField>

    <ResponseField name="material" type="string | null">
      Sample material required (e.g. `serum`, `whole_blood`).
    </ResponseField>

    <ResponseField name="unit" type="object | null">
      Default measurement unit. Use `id` to dereference against [List Units](/api/units/list-units)
      if you need stable identity across renames.

      <Expandable title="Unit fields">
        <ResponseField name="id" type="number">
          Stable measurement unit identifier.
        </ResponseField>

        <ResponseField name="title" type="string">
          Canonical unit title (e.g. `nmol/L`).
        </ResponseField>

        <ResponseField name="display_title" type="string | null">
          Humanised display title.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="pricing" type="object | null">
      Per-biomarker pricing — `null` unless `test_location_id` was supplied **and** your key has the
      `prices_view` scope, or when no lab can price the biomarker standalone. Carries `retail`
      (retail rate card) and `yours` (your API key's card), each a [price breakdown](#price-objects)
      or `null`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="pagination" type="object">
  <Expandable title="Pagination fields">
    <ResponseField name="total" type="number">
      Total rows matching the query across all pages.
    </ResponseField>

    <ResponseField name="limit" type="number">
      Limit applied to this page. Echoes `total` when the `limit` query param was omitted (the whole
      catalog was returned).
    </ResponseField>

    <ResponseField name="offset" type="number">
      Offset applied to this page.
    </ResponseField>

    <ResponseField name="has_more" type="boolean">
      `true` when more pages are available.
    </ResponseField>
  </Expandable>
</ResponseField>

## Price objects

Pricing has one shape everywhere it appears — biomarkers, panels, and the appointment
[preview](/api/appointments/preview-containers). It is always a pair of breakdowns:

<ResponseField name="retail" type="object | null">
  Recommended retail price (retail rate card), or `null` when not priceable.
</ResponseField>

<ResponseField name="yours" type="object | null">
  Your price, at the rate card on your API key's billing account (equals `retail` when your key has
  no billing-account card).
</ResponseField>

Each breakdown is a list of **billing groups** — each billed once — plus a total:

<ResponseField name="total_cents" type="integer">
  Sum of all groups (each billed once), in cents.
</ResponseField>

<ResponseField name="groups" type="object[]">
  The billing groups (order groups — a ZOTZ|KLIMAS order panel, an LC price group, a flat bundle, a
  Stripe panel). Every group carries `price_cents` (the group's cost at this card, billed once) and
  `biomarker_ids` (every biomarker it delivers; empty for panel-level groups). A ZK GOÄ
  group also carries `goa_lines` — how the price was built. Absent for flat-priced bundles, every
  other lab, rate-card overrides, and Stripe-priced panels.

  <ResponseField name="price_cents" type="integer">
    What this group costs at this card, in cents (billed once). This is what you pay — when
    `goa_lines` is present it equals the sum of their `amount_cents`.
  </ResponseField>

  <ResponseField name="biomarker_ids" type="number[]">
    Every biomarker this group delivers — not just the one you queried. The order group is billed
    once, so this is what comes with it: prerequisite results (a blood-count differential pulls the
    full count) and co-delivered markers (one fatty-acid run yields the whole panel), plus, for a
    ratio, its components (the ratio rides along). Empty for panel-level groups. Because order groups
    are shared, summing per-biomarker prices across a basket over-counts — use the appointment
    [preview](/api/appointments/preview-containers) for an exact cart total.
  </ResponseField>

  <ResponseField name="goa_lines" type="object[] | undefined">
    The GOÄ positions making up this charge (ZK GOÄ analyses) — usually one, but a panel can bill
    several codes. Each line is `{ goa_code, goa_base_cents (GOÄ 1.0× baseline / Einfachsatz), quantity
            (may be fractional, e.g. 3.5), factor (the multiplier applied to the 1.0× price on this card —
            e.g. 1.15 retail / 0.8 partner / 0 when ZK invoices you directly; NOT a GOÄ Steigerungsfaktor),
            amount_cents (what the line costs = goa_base_cents × quantity × factor, server-rounded — read
            it directly) }`. Omitted for flat-priced bundles, other labs, rate-card overrides, and
    Stripe-priced panels.
  </ResponseField>
</ResponseField>

## Error responses

| Status | Description                                                                        |
| ------ | ---------------------------------------------------------------------------------- |
| `400`  | Validation error — e.g. `limit` out of range, or unknown/unauthorised `rate_card`. |
| `403`  | Forbidden — missing `panels_access` scope.                                         |
| `500`  | Internal server error.                                                             |

<RequestExample>
  ```bash cURL theme={null}
  curl --url 'https://anivahealth.com/api/v1/biomarkers?limit=5' \
    --header 'x-api-key: YOUR_API_KEY'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "data": [
      {
        "id": 42,
        "slug": "vitamin-d-25-oh",
        "name": "Vitamin D (25-OH)",
        "description": "Serum 25-hydroxyvitamin D …",
        "primary_category": "Vitamins",
        "secondary_category": null,
        "type": "clinical",
        "material": "serum",
        "unit": { "id": 1, "title": "nmol/L", "display_title": null }
      }
    ],
    "pagination": { "total": 17, "limit": 5, "offset": 0, "has_more": true }
  }
  ```
</ResponseExample>
