---
title: "Get Independent Lab Testing Results for a Product"
method: GET
path: "/api/products/{product_id}/lab-results"
tags: ["products"]
---

# Get Independent Lab Testing Results for a Product

`GET /api/products/{product_id}/lab-results`

## Request

- Base URL: `https://api.supp.co`
- URL: `https://api.supp.co/api/products/{product_id}/lab-results`
- Auth: none required

Returns Certificate of Analysis (COA) results for a product, grouped by
canonical analyte. Each canonical analyte has a single `most_recent` round
plus a `history` array of older rounds. Both `published` and `archived`
COA→product links contribute, so disjoint-analyte COAs (e.g. one COA
for Vitamin D3 and a separate COA for Vitamin K1/K2 on the same
product) both surface their unique canonicals.

Picking is per canonical analyte. The selector prefers the newest
`active` round (ops nominates which rounds are live by leaving them
active), and falls back to the newest of all surfaceable rounds when
no active round exists for that canonical. Demoted/inactive rounds
still appear in `history` for the audit trail. Rows in `pending` /
`needs_manual_review` / `suppressed` states are excluded.

Entries in `lab_results` are sorted alphabetically by `display_name`
(case-insensitive) so FE rendering order is stable across requests.

The response is unauthenticated and ships with a `public, max-age=…`
`Cache-Control` header so it can be cached on a shared CDN per
product. Edits to the underlying COA/result rows are visible on the
next request after the CDN TTL elapses.

## Path parameters

- `product_id` string, required

## Response `200`

Successful operation

- object
  - `product_id` string, uuid
  - `has_lab_results` boolean: True iff at least one canonical-analyte entry survived all surface filters; FE can short-circuit the PDP "Lab Testing" panel on this flag.
  - `lab_results` object[]
    - `canonical_analyte_id` string, uuid: For sum_group rounds whose members span multiple canonicals, this is the primary canonical (lowest coa_result_index member).
    - `display_name` string: For sum_group rounds this is the group's optional ops-set display name (the "Summed Group Module" label). When no name is set it falls back to a comma-joined string of the members' canonical names (e.g. "Vitamin A (Retinol Palmitate), Vitamin A (Beta Carotene)"). For single-assay rounds it is the canonical analyte's display name.
    - `category` string, nullable: Pass-through of `Supplement#category` (`IngredientCategory` — e.g. `vitamin`, `mineral`, `amino acid`, `botanical`, `peptide`). Looked up by matching the canonical analyte's display name to `Supplement#name`. Nullable when no matching supplement record exists.
    - `optional_description` string, nullable
    - `test_date` string, date, nullable
    - `label_claim_pct` number, nullable: Percent of label claim, rounded to the nearest whole number. Uncapped — values >100 are normal (brands typically overdose to retain potency through expiration).
    - `pass_fail_status` 'pass' | 'fail': Surfaced rows are always pass or fail. Pending / needs_manual_review rows are filtered out before they reach the FE.
    - `lab_name` string, nullable: ISO 17025 accredited lab that ran the assay.
    - `measured_amount` number, nullable: The lab-measured amount in `measured_units`, in the same unit family as the label claim. For sum groups this is the sum across constituent assays.
    - `measured_units` string, nullable
    - `target_amount` number, nullable: The label claim the result is being measured against.
    - `target_units` string, nullable
    - `undisclosed_on_label` boolean: True when the canonical analyte being measured is not on the supplement-facts label (e.g. Withanolides from Ashwagandha, Silymarins from Milk Thistle). Lets the FE render an "undisclosed on label" hint. For sum_group rounds, true iff every constituent member is flagged.
    - `attempt_number` integer: 1-indexed chronological ordinal of this test for this canonical analyte (oldest = 1).
    - `total_attempts` integer: Total number of rounds for this canonical analyte (most_recent + history.length).
    - `most_recent` boolean
    - `history` object[]
      - `test_date` string, date, nullable
      - `label_claim_pct` number, nullable
      - `pass_fail_status` 'pass' | 'fail'
      - `lab_name` string, nullable
      - `measured_amount` number, nullable
      - `measured_units` string, nullable
      - `target_amount` number, nullable
      - `target_units` string, nullable
      - `undisclosed_on_label` boolean
      - `attempt_number` integer

## Other responses

- `404`: Product not found

---

[API](https://skmtc.dev/supp/apis/suppco-public-api.md) · [All operations](https://skmtc.dev/supp/apis/suppco-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/supp/apis/suppco-public-api/revisions/e088e6cdcb6b?raw)
