← Methods repository
concept

Infused Biologic Administration Capture

An exposure-definition method that ascertains infused (physician-administered, buy-and-bill) biologic use from medical-claim HCPCS J-codes/Q-codes and administration CPT codes on the administration date, then derives exposure intervals from the label dosing schedule rather than from a pharmacy days_supply.

Exposure_Definitionexposure-definitionphysician-administered-drugsbuy-and-billj-codeshcpcsinfused-biologicsbiosimilarspharmacoepidemiology
Methods reference only. Use primary source citations and local policy before applying this in a study protocol, regulatory submission, payer dossier, or clinical decision.

In plain language

Some medications are injected or infused by a nurse or doctor in a clinic rather than picked up at a pharmacy. To track when a patient received one of these infusions in insurance records, researchers look for a billing code on the medical claim called a J-code, which appears on the date the infusion was given. Because there is no days_supply field on a medical claim the way there is on a pharmacy fill, analysts instead use the drug's official dosing schedule to estimate how long each infusion should cover the patient until the next one is due. A gap in coverage is declared only when the patient goes too long without returning for their next scheduled infusion.

Infused biologic administration capture

is the operational problem of measuring exposure to physician-administered biologics — infliximab, rituximab, vedolizumab, abatacept IV, tocilizumab IV, ocrelizumab, natalizumab — in real-world data. These drugs are almost never dispensed through the pharmacy benefit. Under the U.S. "buy-and-bill" model they are acquired by the provider, administered in an infusion suite, hospital outpatient department (HOPD), or home, and billed to the medical benefit (Medicare Part B, or the commercial medical claim) as a HCPCS Level II J-code for the drug (e.g., J1745 infliximab, J3262 tocilizumab IV, J0129 abatacept IV, J3380 vedolizumab) plus an administration CPT code (96365/96366 IV infusion, 96413/96415 chemotherapy-style infusion, 96401 SC). The unit of capture is therefore a medical-claim line on the administration date, not a pharmacy fill — which makes most oral-drug exposure machinery (days_supply stitching, refill-gap rules, drug-era logic keyed on dispensing) the wrong default.

Core conceptual distinction

An infused biologic exposure has no `days_supply`; the duration of one administration's coverage is fixed by the product label's dosing interval, not by a pharmacist-entered field. Infliximab loads at weeks 0, 2, 6 then maintains q8w; rituximab gives 1000 mg ×2 separated by 14 days then re-treats q24w; vedolizumab loads 0/2/6 then q8w. The correct exposure interval is built as `next_expected_date = administration_date + label_interval_days`, with a grace period for real-world timing variability, and a gap is declared when the observed inter-administration interval exceeds roughly 1.5× the label interval — that is the operational definition of discontinuation for an infused agent. This is the conceptual opposite of `persistence-time-to-discontinuation` for oral drugs, which keys off the end of `days_supply`. The dose actually delivered comes from the units billed on the J-code (each J-code defines a unit of milligrams, e.g., J1745 = 10 mg infliximab), which must be read together with the JW/JZ discarded-drug modifiers and weight-based dosing, because naively summing units across lines double-counts wastage and mis-scales weight-based regimens.

Pros, cons, and trade-offs

- vs a pharmacy-fill / Part D-only exposure definition: Capturing J-codes on the medical claim is the only way to see infused biologics at all — a Part D / NDC-only algorithm has ~0% sensitivity for buy-and-bill agents and will silently drop the entire infused arm, producing a cohort that looks like a self-injectable-only population. Cost: medical claims lack `days_supply`, require label-driven interval logic, and force you to reconcile drug J-codes with separate administration CPT codes. Prefer J-code capture for any analysis of IV/infused biologics; reserve NDC/Part D logic for self-injected or white-bagged product that flows through the pharmacy benefit. - vs an "ever exposed" J-code flag (presence only): A binary flag is robust and easy, but it discards dose, schedule, persistence, and time-varying exposure. Building intervals from the label schedule supports `time-updated-exposures-cumulative-dose-rwe`, on-treatment risk windows, and per-protocol estimands. Cost: interval construction is sensitive to grace-period and gap-multiplier choices, which must be pre-specified and varied in sensitivity analyses. - vs treating each infusion as an independent point exposure: Point exposures are simplest but cannot represent continuous on-treatment time between scheduled infusions, so they understate exposed person-time and mishandle the loading phase. Prefer interval construction when the estimand is a rate or a hazard over on-treatment time.

When to use

Comparative effectiveness, safety, persistence, dose, or cost analyses of infused biologics in claims, EHR-medication-administration (MAR), or linked data; any study where the exposure or comparator is a buy-and-bill agent; building the exposure spine of a target-trial emulation whose strategies are infused regimens. It is mandatory whenever an infused agent appears on either side of a comparison — including originator-vs-biosimilar and IV-vs-SC formulation studies.

When NOT to use — and when it is actively misleading or dangerous

- Self-injectable-only comparisons (e.g., adalimumab pen vs etanercept SYRINGE) belong to NDC/Part D fill logic; forcing J-code logic there finds nothing and the cohort collapses. - Pure inpatient bundled administrations. Inpatient biologic doses are usually rolled into a DRG and not separately billed as a J-code, so an outpatient J-code algorithm misses inpatient bridging doses — see `inpatient-bridging-exposure-rwe`. Concluding "treatment gap" across a hospitalization when the drug was given inpatient is a false discontinuation and is actively misleading for persistence and immortal-time analyses. - Biosimilar-era exposure without code reconciliation. Infliximab biosimilars carry distinct Q-codes (Q5103 infliximab-dyyb, Q5104 infliximab-abda, Q5121 infliximab-axxq). A code list built before a biosimilar launch will register apparent discontinuation at the originator→biosimilar switch date. Decide by design whether to pool originator+biosimilars into one molecule or analyze them as distinct exposures, and document the switch handling. - Naive unit summation for dose. Summing J-code units without removing JW (discarded) lines and without weight scaling inflates cumulative-dose estimates; for a renal/oncology outcome modified by dose this can manufacture a spurious dose-response.

Data-source operational depth

- Claims (FFS Medicare or commercial): Exposure = medical-claim line with a drug J-code/Q-code on `admin_date`, ideally co-occurring with an administration CPT and a plausible place-of-service (POS 11 office, 19/22 HOPD, 12 home). Require continuous medical enrollment (Part B, not just Part D) across baseline and follow-up so absence of a J-code is a real no-treatment period, not unobserved benefit. Failure modes: (1) MA-only person-time lacks complete FFS encounter/claim submission — "no infusion" can be missingness; restrict to FFS Parts A/B (and D for any white-bagged product) or to commercial plans with complete medical capture. (2) JW/JZ discarded-drug modifiers create extra lines that double-count dose if not netted out. (3) Claims adjudication lag and reversals make the most recent quarters look like false discontinuation — impose a data-maturity buffer. (4) Differential competing risks (death before a scheduled infusion) in elderly claims can masquerade as discontinuation unless death/disenrollment are handled as censoring/competing events. - EHR (MAR / order data): The administration record (MAR `administered` event), not the order, is the exposure; orders without a matching administration over-capture intended-but-not-given doses. EHR adds weight, BSA, lab severity, and the actual milligrams hung — superior dose fidelity — but external-care leakage (an infusion given at an outside center) is invisible, biasing persistence downward for mobile patients. Link to claims to recover out-of-system infusions. - Registry: Strong for indication, disease activity, and adjudicated outcomes; typically weak/incomplete for every administration date. Link to claims for the full infusion history and to a death index for censoring. - Linked claims–EHR–registry: The ideal substrate (claims completeness + EHR dose fidelity + registry severity) but introduces linkage selection and date-discrepancy problems (order date vs MAR date vs claim service date vs claim paid date) that must be reconciled to a single `admin_date` before interval construction.

Worked claims example

Question: 12-month persistence on infliximab (originator + biosimilars) among adults with Crohn's disease initiating IV induction in a commercial + Medicare FFS database. (1) Build the drug code list: J1745 (originator) plus Q5103/Q5104/Q5121 (biosimilars), each defined in 10-mg units; pool them into a single `infliximab` molecule. (2) Keep medical claim lines with those codes co-occurring with an administration CPT (96365/96413) and drop lines carrying the JW modifier before counting units. (3) Require ≥365 days continuous medical enrollment (FFS Parts A/B or commercial medical) before and after the first administration; exclude MA-only person-time so an absent J-code means no infusion, not missing data. (4) Index = first qualifying administration date. (5) Reconstruct the schedule: loading at weeks 0, 2, 6, then maintenance q8w; for each administration set `next_expected_date = admin_date + (14 or 56 days)` per loading/maintenance phase, add a 28-day grace period, and declare discontinuation when the next administration is absent beyond `1.5 ×` the expected maintenance interval (i.e., > 56 + 28 = 84 days with no infusion). (6) Before calling a gap a discontinuation, check for an inpatient bridging dose (hospitalization spanning the expected window with no outpatient J-code) and for an originator↔biosimilar switch on the gap date. (7) Censor at disenrollment, death, end of data, and the data-maturity buffer; estimate persistence with a Kaplan–Meier curve and a Fine–Gray model treating death as a competing risk for the time-to-discontinuation outcome.

Worked example

Scenario

A 45-year-old with Crohn's disease starts infliximab on January 6, 2025. The drug label calls for a loading phase of three infusions at weeks 0, 2, and 6, then maintenance infusions every 8 weeks (56 days). An analyst has 360 days of continuous medical-benefit enrollment (January 6 through December 31, 2025) and wants to know how many days this patient was on-treatment and whether they discontinued. There is no pharmacy fill record anywhere because infliximab is infused in a clinic and billed to the medical benefit as J-code J1745.

Dataset

Five medical claim lines from the infusion suite. Each row is one J-code administration. There is no days_supply column because infused drugs have none.

person_idadmin_datehcpcscpt_adminunitsjw_modifier
10012025-01-06J174596365100
10012025-01-20J174596365100
10012025-02-17J174596365100
10012025-04-14J174596365100
10012025-06-09J174596365100

Steps

  • Each row is a single infusion administration. The jw_modifier column is false on every row, meaning no drug was discarded or wasted, so all five administrations count as real exposure events.

  • J-code J1745 = 10 mg of infliximab per billed unit. With 100 units each visit, each infusion delivers 1,000 mg. Dose is not needed to build the coverage timeline, but it confirms the claims data is plausible for a weight-based maintenance dose.

  • Classify infusions by sequence. The first three (Jan 6, Jan 20, Feb 17) are loading infusions. The loading dosing interval is 14 days (wk 0 to wk 2) and then 28 days (wk 2 to wk 6). Adding a 28-day grace period, each loading infusion keeps the patient covered for 42 days (14 + 28). Loading coverage ends 42 days after Feb 17, which is March 31.

  • The fourth and fifth infusions (Apr 14, Jun 9) are maintenance at 56-day intervals. Adding the 28-day grace period, each maintenance infusion covers 84 days. Maintenance coverage from Apr 14 runs through Jul 7; coverage from Jun 9 runs through Sep 1, which is later, so the combined maintenance span runs Apr 14 through Sep 1 (141 days).

  • There is a 13-day gap between the end of loading coverage (Mar 31) and the start of maintenance coverage (Apr 14), spanning April 1 through April 13. This gap is within the grace period for the transition from loading to maintenance, so it is expected scheduling flexibility rather than discontinuation.

  • After Sep 1 no sixth infusion appears. The discontinuation threshold is 1.5 times the 56-day maintenance interval = 84 days after the last infusion (Jun 9 + 84 days = Sep 1). Because no infusion arrives by Sep 1, discontinuation is declared on that date. The patient is off-treatment from Sep 2 through Dec 31 (121 days).

  • On-treatment days = loading coverage (85 days: Jan 6 to Mar 31) + maintenance coverage (141 days: Apr 14 to Sep 1) = 226 days. Uncovered days = 13-day mid-gap + 121 post-discontinuation days = 134 days. Check: 226 + 134 = 360, which matches the window length.

Result

5 infusions captured via J-code J1745; on-treatment (covered) for 226 of 360 window days (63%); discontinuation declared September 1, 2025 after no sixth infusion arrived within the 84-day grace threshold.

Timeline Spec

Title

Infliximab infusion exposure for one Crohn's patient (label-schedule intervals, 360-day window)

Caption

Five J-code infusions (loading at weeks 0, 2, 6 then maintenance q8w). Coverage intervals are inferred from the dosing schedule plus a 28-day grace period. No days_supply exists; the dosing interval replaces it. A 13-day gap appears between loading and maintenance; discontinuation is declared on September 1 after no sixth infusion arrives within 1.5 times the 56-day maintenance interval.

Alt Text

Horizontal timeline from January 6 to December 31, 2025. Five vertical tick marks represent J-code infusion administrations on January 6, January 20, February 17, April 14, and June 9. Three shaded loading-phase bars span January 6 through March 31. A narrow unlabeled gap runs April 1 through April 13. Two overlapping maintenance-phase bars span April 14 through September 1. A dashed discontinuation marker appears on September 1. The remainder of the window through December 31 is unshaded.

Window
Start

2025-01-06

End

2025-12-31

Label

Denominator: 360-day observation window (continuous medical enrollment)

Events
  • Label

    Infusion 1 (wk 0 loading)

    Start

    2025-01-06

    Length Days

    42

    Quantity

    J1745 infusion, 100 units (1,000 mg)

  • Label

    Infusion 2 (wk 2 loading)

    Start

    2025-01-20

    Length Days

    42

    Quantity

    J1745 infusion, 100 units (1,000 mg)

  • Label

    Infusion 3 (wk 6 loading)

    Start

    2025-02-17

    Length Days

    42

    Quantity

    J1745 infusion, 100 units (1,000 mg)

  • Label

    Infusion 4 (q8w maintenance)

    Start

    2025-04-14

    Length Days

    84

    Quantity

    J1745 infusion, 100 units (1,000 mg)

  • Label

    Infusion 5 (q8w maintenance)

    Start

    2025-06-09

    Length Days

    84

    Quantity

    J1745 infusion, 100 units (1,000 mg)

Spans
  • Kind

    exposed

    Start

    2025-01-06

    End

    2025-03-31

    Label

    85 covered days (loading phase, union of 3 intervals)

  • Kind

    gap

    Start

    2025-04-01

    End

    2025-04-13

    Label

    13-day scheduling gap (loading to maintenance transition)

  • Kind

    exposed

    Start

    2025-04-14

    End

    2025-09-01

    Label

    141 covered days (maintenance phase)

  • Kind

    unexposed

    Start

    2025-09-02

    End

    2025-12-31

    Label

    121 uncovered days after discontinuation (no 6th infusion)

Result
Label

226 on-treatment days / 360 window days = 63% covered; discontinuation declared 2025-09-01

Value

0.628

Runnable example

python implementation

Build label-schedule exposure intervals for an infused biologic from claims-style inputs. Required inputs (cleaned, de-duplicated, one row per administration line): med : medical drug-administration claims -> person_id, admin_date (datetime), hcpcs (J/Q...

import pandas as pd
import numpy as np

# Pool originator + biosimilars into one molecule (example: IV infliximab).
CODE_LIST = {"J1745", "Q5103", "Q5104", "Q5121"}
UNIT_MG   = {"J1745": 10, "Q5103": 10, "Q5104": 10, "Q5121": 10}  # mg per billed unit
ADMIN_CPT = {"96365", "96366", "96413", "96415"}                  # IV infusion administration
LOAD_GAP_DAYS = 14    # loading phase target interval (weeks 0,2,6)
MAINT_GAP_DAYS = 56   # maintenance target interval (q8w)
GRACE_DAYS = 28       # real-world timing slack
GAP_MULTIPLIER = 1.5  # discontinuation when next infusion absent beyond MULTIPLIER x maintenance interval

def build_infusion_intervals(med: pd.DataFrame, enroll: pd.DataFrame) -> pd.DataFrame:
    # 1) Keep on-molecule administrations with a real administration CPT; drop JW (discarded) lines for dose/event logic.
    m = med[med["hcpcs"].isin(CODE_LIST) & med["cpt_admin"].isin(ADMIN_CPT) & (~med["jw_modifier"])].copy()

    # 2) Collapse multiple same-day lines (e.g., two vials) into one administration; sum mg actually delivered.
    m["dose_mg"] = m["units"] * m["hcpcs"].map(UNIT_MG)
    adm = (m.groupby(["person_id", "admin_date"], as_index=False)
             .agg(dose_mg=("dose_mg", "sum")))

    # 3) Require continuous medical enrollment (no MA-only gaps) covering each administration date.
    e = enroll[~enroll["ma_only"]]
    adm = adm.merge(e, on="person_id")
    adm = adm[(adm["enroll_start"] <= adm["admin_date"]) & (adm["enroll_end"] >= adm["admin_date"])]
    adm = adm.drop(columns=["enroll_start", "enroll_end", "ma_only"]).drop_duplicates(["person_id", "admin_date"])

    # 4) Order administrations and classify loading vs maintenance by infusion sequence (first 3 = loading 0/2/6 wk).
    adm = adm.sort_values(["person_id", "admin_date"])
    adm["seq"] = adm.groupby("person_id").cumcount()
    adm["target_gap"] = np.where(adm["seq"] < 3, LOAD_GAP_DAYS, MAINT_GAP_DAYS)

    # 5) Exposure interval = [admin_date, admin_date + target_gap + grace]; gap to the next infusion drives discontinuation.
    adm["interval_end"] = adm["admin_date"] + pd.to_timedelta(adm["target_gap"] + GRACE_DAYS, unit="D")
    adm["next_admin"] = adm.groupby("person_id")["admin_date"].shift(-1)
    adm["gap_to_next"] = (adm["next_admin"] - adm["admin_date"]).dt.days
    thresh = MAINT_GAP_DAYS * GAP_MULTIPLIER
    adm["discontinued"] = adm["next_admin"].isna() | (adm["gap_to_next"] > thresh)
    return adm[["person_id", "admin_date", "seq", "dose_mg", "interval_end", "next_admin", "discontinued"]]
r implementation

Label-schedule exposure intervals with data.table. Inputs mirror the Python version: med : person_id, admin_date (Date), hcpcs, cpt_admin, units (int), jw_modifier (logical), place_of_service enroll : person_id, enroll_start, enroll_end, ma_only (logical) #...

library(data.table)

code_list  <- c("J1745", "Q5103", "Q5104", "Q5121")              # originator + biosimilars pooled
unit_mg    <- c(J1745 = 10, Q5103 = 10, Q5104 = 10, Q5121 = 10)  # mg per billed unit
admin_cpt  <- c("96365", "96366", "96413", "96415")
load_gap   <- 14L; maint_gap <- 56L; grace <- 28L; gap_mult <- 1.5

build_infusion_intervals <- function(med, enroll) {
  setDT(med); setDT(enroll)

  # 1) On-molecule administrations with a real administration CPT; drop JW (discarded) lines.
  m <- med[hcpcs %chin% code_list & cpt_admin %chin% admin_cpt & !jw_modifier]
  m[, dose_mg := units * unit_mg[hcpcs]]

  # 2) Collapse same-day lines into one administration; sum mg delivered.
  adm <- m[, .(dose_mg = sum(dose_mg)), by = .(person_id, admin_date)]

  # 3) Require continuous medical enrollment (no MA-only) covering each administration date.
  e <- enroll[ma_only == FALSE]
  adm <- e[adm, on = "person_id", allow.cartesian = TRUE
           ][enroll_start <= admin_date & enroll_end >= admin_date]
  adm <- unique(adm[, .(person_id, admin_date, dose_mg)])

  # 4) Sequence administrations; first 3 = loading (0/2/6 wk), rest = maintenance (q8w).
  setorder(adm, person_id, admin_date)
  adm[, seq := seq_len(.N) - 1L, by = person_id]
  adm[, target_gap := fifelse(seq < 3L, load_gap, maint_gap)]

  # 5) Exposure interval and discontinuation flag from the gap to the next infusion.
  adm[, interval_end := admin_date + target_gap + grace]
  adm[, next_admin := shift(admin_date, type = "lead"), by = person_id]
  adm[, gap_to_next := as.integer(next_admin - admin_date)]
  adm[, discontinued := is.na(next_admin) | gap_to_next > maint_gap * gap_mult]
  adm[, .(person_id, admin_date, seq, dose_mg, interval_end, next_admin, discontinued)]
}