← All articles
TechnicalJune 16, 2026

LOINC Code Lookup API: Search Lab Tests and Build FHIR Observations

LOINC identifies every lab test and clinical observation in healthcare. Here's how to look up codes, search by component and specimen, read the six-axis model, and drop the result into a FHIR Observation — with working TypeScript.

LOINC code lookup API tutorial

If your application touches lab results, it speaks LOINC. Every glucose level, every CBC, every culture result that moves between systems carries a LOINC code to say what was measured. It's the Observation.code half of the FHIR lab story — the other half being the result value and its units.

Working with LOINC directly is its own project. Regenstrief ships it as a set of CSV tables you have to download, accept a license for, parse, and reload a few times a year. This tutorial shows how to look up, search, and batch-process LOINC codes with FHIRfly's API and TypeScript SDK — and how to turn a lookup into a FHIR Observation.

What Makes LOINC Different

Unlike ICD-10 or NDC, a LOINC code isn't a flat label — it's a coordinate in a six-axis model. Each code is defined by its parts:

PartMeaningExample (2345-7)
ComponentWhat is measuredGlucose
PropertyKind of quantityMCnc (mass concentration)
TimePoint vs intervalPt (point in time)
SystemSpecimen / sample typeSer/Plas (serum or plasma)
ScaleQuantitative, ordinal, nominalQn
MethodHow it was measured (optional)null

This is why "glucose" alone isn't enough — glucose in blood, in serum/plasma, in urine, and after a glucose challenge are all distinct codes. The parts are what let you disambiguate, and FHIRfly returns them as structured fields you can filter and read directly.

Setup

npm install @fhirfly-io/terminology
import { Fhirfly } from "@fhirfly-io/terminology";

const fhirfly = new Fhirfly({ apiKey: process.env.FHIRFLY_API_KEY! });

Free tier includes 10,000 requests/month — get your key here.

Look Up a Single Code

The most common operation: you have a LOINC code and need the name, parts, and units.

const result = await fhirfly.loinc.lookup("2345-7", { shape: "standard" });

console.log(result.data.code);
// "2345-7"

console.log(result.data.display_name);
// "Glucose, Blood"

console.log(result.data.long_name);
// "Glucose [Mass/volume] in Serum or Plasma"

console.log(result.data.parts.system);
// "Ser/Plas"

console.log(result.data.units.example_ucum_units);
// "mg/dL"

console.log(result.data.order_obs);
// "Both"  — orderable AND reportable

The order_obs field is worth noting: it tells you whether a code is meant to be ordered (Order), reported as a result (Observation), or Both. Use it to keep order-entry pick-lists separate from result-display logic.

Response Shapes

ShapeFieldsBest for
compactCode, display name, shortname, class, componentAutocomplete, search results
standard+ Parts, units, status, order/obs, map-to, FHIR codingResult processing, FHIR mapping
full+ Consumer name, version history, common-test ranks, source/copyrightAI agents, auditing, provenance

FHIR-Ready Coding

Every response includes a pre-built FHIR coding:

const result = await fhirfly.loinc.lookup("718-7", { shape: "standard" });

console.log(result.data.fhir_coding);
// {
//   system: "http://loinc.org",
//   code: "718-7",
//   display: "Hemoglobin, Blood"
// }

Drop this straight into an Observation.code — more on that below.

Search by Term, Component, and Specimen

Most LOINC work starts from a description, not a code. Search accepts a free-text q plus structured filters that map to the six axes.

const results = await fhirfly.loinc.search({ q: "glucose" }, { limit: 5 });

console.log(`${results.total} codes match "glucose"`);
// 976 codes match "glucose"

for (const code of results.items) {
  console.log(`${code.code} — ${code.display_name} (${code.class})`);
}
// "51595-7 — Glucose, Stool (CHEM)"
// "2349-9 — Glucose, Urine (CHEM)"
// "18296-4 — Glucose after dose glucose, Blood (CHAL)"
// "47622-6 — Glucose before dose glucose, Blood (CHAL)"
// "2339-0 — Glucose, Blood (CHEM)"

Nearly a thousand hits for one analyte — exactly why the axis filters matter. Narrow by specimen system, LOINC class, or scale:

// Hemoglobin measured in blood
await fhirfly.loinc.search({ q: "hemoglobin", system: "Bld" });
// 55235-6 (gene panel), 717-9, 718-7, 59260-0, ...
// Note the top hit is a panel, not a plain measurement — add scale: "Qn" to drop it.

// Only quantitative chemistry tests on serum
await fhirfly.loinc.search({
  class: "CHEM",
  system: "Ser/Plas",
  scale: "Qn",
});

// Only orderable codes (for an order-entry picker)
await fhirfly.loinc.search({ q: "lipid panel", order_obs: "Order" });

// Sort alphabetically instead of by relevance
await fhirfly.loinc.search({ q: "creatinine" }, { sort: "name" });

Pagination

let page = 1;
let hasMore = true;

while (hasMore) {
  const results = await fhirfly.loinc.search(
    { class: "CHEM", scale: "Qn" },
    { limit: 50, page }
  );

  for (const code of results.items) {
    await indexCode(code);
  }

  hasMore = results.has_more;
  page++;
}

Build a FHIR Observation

This is where LOINC earns its keep. A lab result in FHIR is an Observation whose code is a LOINC coding and whose valueQuantity carries the number and UCUM unit. FHIRfly hands you both pieces — the coding and an example UCUM unit — from a single lookup:

async function buildGlucoseObservation(value: number, patientId: string) {
  const loinc = await fhirfly.loinc.lookup("2345-7", { shape: "standard" });

  return {
    resourceType: "Observation",
    status: "final",
    code: {
      coding: [loinc.data.fhir_coding], // { system: "http://loinc.org", code, display }
      text: loinc.data.display_name,
    },
    subject: { reference: `Patient/${patientId}` },
    valueQuantity: {
      value,
      unit: loinc.data.units.example_units ?? undefined,       // "mg/dL"
      system: "http://unitsofmeasure.org",
      code: loinc.data.units.example_ucum_units ?? undefined,  // "mg/dL"
    },
  };
}

const obs = await buildGlucoseObservation(99, "123");

The example_ucum_units field is the bridge to a valid UCUM-coded valueQuantity. It reflects how the test is typically reported — confirm against your own result units before relying on it for conversion, since some analytes are reported in more than one unit.

Batch Lookups

Enriching a result feed or a lab interface with hundreds of codes? Use the batch endpoint:

const response = await fhirfly.loinc.lookupMany(
  ["2345-7", "718-7", "2160-0", "0000-0"],
  { shape: "standard" }
);

for (const result of response.results) {
  if (result.status === "ok") {
    console.log(`${result.input}: ${result.data.display_name}`);
  } else {
    console.log(`${result.input}: ${result.status}`);
  }
}
// "2345-7: Glucose, Blood"
// "718-7: Hemoglobin, Blood"
// "2160-0: Creatinine, Blood"
// "0000-0: not_found"

Batch accepts up to 100 codes per request. Each result carries its own status, so an invalid or retired code returns not_found without failing the whole batch — handy for validating an incoming feed.

Provenance and Ranking (Full Shape)

The full shape adds fields built for auditing and for prioritizing the tests that actually matter:

const result = await fhirfly.loinc.lookup("2345-7", { shape: "full" });

console.log(result.data.consumer_name);
// "Glucose, Blood"  — patient-friendly label

console.log(result.data.ranks);
// { common_test_rank: 6, common_order_rank: 120 }

console.log(result.data.version);
// "2.82"  — the LOINC release this term came from

common_test_rank reflects how frequently a code appears in real lab data (lower = more common). Use it to push the everyday tests to the top of an autocomplete and bury the obscure ones.

A Licensing Note

LOINC is free to use, but it is not public domain — it ships under the Regenstrief LOINC license, which requires attribution. FHIRfly surfaces this in every response so you don't have to track it separately:

const result = await fhirfly.loinc.lookup("2345-7", { shape: "standard" });

console.log(result.meta.legal.license);
// "regenstrief_license"

console.log(result.meta.legal.attribution_required);
// true

If you display LOINC names to end users, include the standard attribution. The full shape returns the exact citation string and terms-of-use link to make that straightforward.

REST API (Without the SDK)

# Single lookup
curl -H "x-api-key: $FHIRFLY_API_KEY" \
  "https://api.fhirfly.io/v1/loinc/2345-7?shape=standard"

# Search
curl -H "x-api-key: $FHIRFLY_API_KEY" \
  "https://api.fhirfly.io/v1/loinc/search?q=glucose&system=Bld&limit=10"

# Batch
curl -X POST -H "x-api-key: $FHIRFLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"codes": ["2345-7", "718-7", "2160-0"]}' \
  "https://api.fhirfly.io/v1/loinc/_batch?shape=standard"

Common Use Cases

Use CaseApproach
Lab result enrichmentBatch lookup with standard shape
Test search in an LIS/EHRSearch with axis filters, compact shape
Order-entry pick-listsSearch with order_obs: "Order"
FHIR Observation resourcesLookup, use fhir_coding + units
Code validationBatch lookup, check for not_found status
Patient-facing labelsLookup with full shape, read consumer_name
Prioritized autocompleteRead ranks.common_test_rank

Key Takeaways

  • Six-axis model — component, property, time, system, scale, method are returned as structured, filterable fields
  • Axis filters — narrow a thousand "glucose" hits down to the right specimen and scale
  • FHIR + UCUM readyfhir_coding and example_ucum_units build a valid Observation in one call
  • order_obs — separates orderable codes from reportable results
  • Common-test ranks — surface the tests clinicians actually use
  • Batch endpoint — up to 100 codes, per-code status
  • Attribution built in — Regenstrief license info travels with every response

Next Steps

  1. Get a free API key from the FHIRfly dashboard
  2. Install the SDK: npm install @fhirfly-io/terminology
  3. Search for the tests in your lab catalog and check their parts
  4. Read the LOINC API docs for the complete parameter reference
Tagsloinctutoriallab-datafhirapi
Written by The FHIRfly Team — healthcare data, AI, and interoperability folks building better clinical coding APIs.

Build it on real terminology

Try any endpoint live — no sign-up required.

© 2026 FHIRfly.io LLC. All rights reserved. · Terminology data sourced from official registries, updated daily.