CPT Codes (HCPCS Level I)
Current Procedural Terminology (CPT), maintained by the American Medical Association and adopted under HIPAA as HCPCS Level I, is the standard code set for identifying professional services and outpatient procedures on US claims; it appears on every physician (CMS-1500/837P) and outpatient facility (UB-04) claim but NOT on inpatient facility claims, where ICD-10-PCS governs procedure coding — the single most consequential setting rule for procedure ascertainment in RWE.
On this page
CPT codes are five-digit numbers printed on every US doctor's bill and outpatient hospital charge that tell the insurer exactly which service was performed — an office visit, a blood test, a surgery, or a chemotherapy infusion. When researchers study what care patients received using insurance claims, they look up these codes to find who had a procedure or visit and when. The important catch is that CPT codes do not appear on regular hospital admission bills — hospitals use a completely different system called ICD-10-PCS for inpatient procedures — so researchers who rely only on CPT will miss every procedure done during a hospital stay.
CPT (Current Procedural Terminology)
is the procedure and service coding system maintained by the American Medical Association (AMA) CPT Editorial Panel and adopted by the Department of Health and Human Services under HIPAA as HCPCS Level I.
It is the universal language of professional billing in the United States: every evaluation and management (E/M) encounter, surgical service, diagnostic test, imaging study, and therapeutic infusion performed outside an inpatient hospital setting is identified on the claim by a CPT code. Because reimbursement is tied to the code, the claim record reflects what was billed (and paid), making CPT the most comprehensive, consistently captured procedure signal in US administrative data.
Copyright and licensing constraint — read before using in any catalog, tool, or publication. CPT is a copyrighted work of the American Medical Association. The AMA licenses CPT for commercial use, redistribution, and derivative products; a valid AMA CPT license is required before reproducing code descriptors in software, databases, publications, or data products. This is the reason that the OMOP Common Data Model's Athena vocabulary download portal requires a separate license-acceptance step for the CPT4 vocabulary: even though OMOP maps CPT codes to standard concepts, the underlying descriptor text remains AMA-copyrighted and cannot be redistributed without that license.
Analysts building phenotype libraries, open-source code lists, or research tools that display CPT descriptors must obtain or operate under an AMA CPT license. Researchers who work within a licensed institutional environment (hospital system, payer, major analytics vendor) are typically covered by an enterprise license, but that coverage does not extend to public GitHub repositories or open publications that reproduce the descriptor text. This entry therefore describes the format and structure of CPT without reproducing licensed descriptor text; worked examples use numeric range logic rather than official code labels.
Code format and category structure
CPT codes are organized into four categories, each with a distinct format that can be detected by regular expression:
- Category I (the main procedural set): exactly five numeric digits (pattern `\d{5}`). These are the codes that drive the overwhelming majority of professional and outpatient facility claims. They are organized into six sections: Evaluation and Management, Anesthesia, Surgery, Radiology, Pathology and Laboratory, and Medicine. Within each section codes are grouped in ascending numeric ranges that correspond roughly to anatomic system or service type.
The range structure — not the descriptors — is usable in open code without a license, and it is what enables range-based filtering (e.g., identifying office/outpatient E/M visit lines from the 99202–99215 family by numeric range alone).
- Category II (performance measurement tracking): four digits followed by the letter "F" (pattern `\d{4}F`). These codes are supplemental and non-billable for payment; they document quality measures, preventive care actions, and patient safety activities.
Because they are not required for reimbursement, their capture in administrative claims is uneven and they should not be used as the primary source for procedure ascertainment. Their RWE value is primarily for quality-of-care or pay-for-performance studies that use claims from payers who mandate their reporting.
- Category III (emerging technology, temporary): four digits followed by the letter "T" (pattern `\d{4}T`). These are time-limited codes for new procedures, technologies, and services that lack sufficient evidence or utilization for Category I status. They are updated semiannually (January and July) rather than annually.
When a Category III code accumulates sufficient evidence and volume, the AMA may promote it to a permanent Category I code; when that happens, the Category III code is retired and both the old and new codes may appear in historical claims during the transition window.
RWE studies on novel devices or procedures must anticipate this code-evolution pattern: a procedure identified only by its Category III code will be systematically missed in claims before the code was created and after it was replaced by a Category I code.
- PLA codes (Proprietary Laboratory Analyses): four digits followed by the letter "U" (pattern `\d{4}U`). Introduced in 2018, these codes identify laboratory tests marketed under a proprietary name. They are relevant to RWE studies on genomic testing, liquid biopsy, and other advanced laboratory diagnostics.
Modifiers
CPT codes accept two-character alphanumeric modifiers appended after the code (in claims data, typically stored in a separate modifier field). Modifiers convey clinically important information without altering the core code: they can indicate that only the professional component of a service was provided (as opposed to the technical component), that a service was distinct and separate from another service on the same date, that laterality applies (left side vs. right side), or that a bilateral procedure was performed.
In RWE, failing to parse modifiers leads to two common errors: (1) double-counting a global service when both the professional and technical components are billed separately with component modifiers, and (2) missing laterality when the study requires distinguishing which limb, eye, or ear was treated.
Where CPT appears on claims — the central setting rule for RWE
This is the most operationally consequential fact about CPT for real-world evidence:
- Professional (physician/supplier) claims — CMS-1500 paper form / 837P electronic transaction, field 24D: CPT or HCPCS codes appear on every service line. This is the primary source for all physician office visits, specialist consultations, outpatient surgical procedures billed by the surgeon, diagnostic services, infusion administration codes, and labs billed under the physician fee schedule.
- Outpatient facility claims — UB-04 paper form / 837I electronic transaction, field FL44 (revenue code line): CPT/HCPCS codes appear alongside revenue center codes on outpatient hospital claims. A hospital outpatient department (HOPD) procedure generates both a revenue code and a CPT code; the CPT code identifies the specific procedure while the revenue code identifies the cost center.
- Inpatient facility claims — UB-04 / 837I: CPT codes do NOT appear on inpatient facility claims. Inpatient procedures are coded using ICD-10-PCS (International Classification of Diseases, 10th Revision, Procedure Coding System), a wholly separate, public-domain system maintained by CMS. Any RWE study that relies on CPT alone for procedure ascertainment will systematically miss every inpatient case.
For procedures that can be performed in either inpatient or outpatient settings (cardiac catheterization, joint replacement, certain cancer resections), a CPT-only code list captures the lower-acuity outpatient population while dropping the higher-acuity inpatient population — a form of case-mix ascertainment bias that can severely distort comparative effectiveness estimates.
Key RWE use cases
- E/M visit ascertainment and utilization counting. Office/outpatient E/M visits fall in a well-known numeric range; place-of-service codes narrow to office, telehealth, or outpatient settings. Counting unique dates rather than service lines avoids double-counting split-billing (where E/M and a procedure on the same day both generate a line).
- Drug administration coding (infusion and injection). For oncology and specialty pharmacy products, the CPT administration code (identifying the infusion service) and the HCPCS Level II J-code (identifying the drug itself) appear as separate lines on the same claim. In RWE, treating the J-code alone as evidence of drug exposure without the administration code may miss routes of administration, and treating the administration code alone identifies the service but not the specific agent. The correct approach for chemotherapy/biologic administration studies is to require both the drug J-code and an administration CPT code on the same or proximate claim.
- Procedure ascertainment (outpatient and ambulatory surgery). CPT identifies outpatient surgical procedures in ambulatory surgery centers (ASC) and HOPDs precisely. Because the CPT code is the payment trigger, capture is high for reimbursable procedures. Failure modes include: (a) bilateral procedures coded with a modifier on a single code line rather than two separate lines, so volume-based utilization counts undercount; (b) unlisted procedure codes (Category I "unlisted" codes ending in specific suffixes) that aggregate heterogeneous procedures and cannot be distinguished without chart review.
- Laboratory test identification. CPT lab codes identify the test that was billed (e.g., a specific panel or analyte), while LOINC codes identify the observable result returned by the laboratory. In EHR-linked data, CPT appears on the order/billing side and LOINC on the result side; claims data contains CPT but not LOINC for most tests. This means claims can confirm a test was ordered and billed but cannot directly provide the result value — that requires EHR linkage.
Relationship to HCPCS Level II
CPT constitutes HCPCS Level I. HCPCS Level II is a parallel code set maintained by CMS that fills gaps not addressed by CPT: drugs and biologics administered in clinical settings (J-codes), durable medical equipment (E-codes), ambulance services (A-codes), and supplies.
The two levels are complementary: professional claims use both CPT and HCPCS Level II codes on the same service line when appropriate. For drug administration studies in claims, J-codes (Level II) identify the drug and CPT codes (Level I) identify the administration service; analysts must work with both levels simultaneously.
Relationship to SNOMED CT and LOINC
CPT is a billing-oriented system optimized for reimbursement, not clinical representation. SNOMED CT provides a clinically rich, ontologically structured representation of procedures with formal hierarchical relationships and laterality that CPT lacks. LOINC provides standardized identifiers for laboratory observations and results. In OMOP, CPT4 procedure codes are mapped to SNOMED standard concepts via the Athena vocabulary server, enabling cross-database queries that are not possible on raw CPT alone.
However, because CPT4 is AMA-copyrighted, downloading the CPT4 vocabulary from Athena requires an explicit license-acceptance step that is separate from the standard OMOP vocabulary download — the only major OMOP vocabulary that requires this step.
Annual update cycle and version management
The AMA CPT Editorial Panel issues a new CPT code set effective January 1 of each year (Category III codes also receive a July 1 semiannual update). Codes can be added, revised, or deleted in each update.
In longitudinal RWE studies spanning multiple calendar years, a code that was not yet created in year one of the study window cannot appear in year-one claims, and a code deleted mid-study creates a truncated capture window. Code-list documentation for regulatory-grade RWE must specify which CPT version(s) were in effect during the study window and how code changes were handled — particularly for Category III codes transitioning to Category I.
Pros, cons, and trade-offs — specific and comparative
- vs ICD-10-PCS (inpatient procedure coding): CPT covers all professional and outpatient facility services with high payment-driven capture; ICD-10-PCS covers inpatient facility procedures with clinical detail (approach, device, qualifier) that CPT lacks but is coded by facility coders rather than the performing clinician.
For a procedure performed in either setting, a complete ascertainment requires both systems in a union code set. Never use CPT alone when the study population includes hospitalizations for the procedure of interest.
- vs HCPCS Level II J-codes (drug identification): CPT identifies the administration service; J-codes identify the specific drug or biologic. The two are complements on infusion claims, not substitutes. Choosing one and ignoring the other produces a systematically incomplete study design for any infused specialty product.
- vs revenue center codes (outpatient facility billing): Revenue codes are present on all UB-04 facility claims and identify the cost center (e.g., operating room, observation, emergency department). They are broader than CPT — a single revenue code covers many procedures — and should be used to confirm care setting (outpatient vs. inpatient) or to detect services not billed with a CPT code, not as a substitute for CPT in procedure identification.
- vs SNOMED CT (clinical concept representation): SNOMED provides hierarchical procedure semantics (a hip replacement IS-A joint replacement IS-A musculoskeletal procedure) enabling queries that traverse the hierarchy without enumerating every leaf code. CPT is flat with no formal hierarchy, so CPT-based phenotyping requires explicit enumeration of all relevant codes. In OMOP-mapped data, SNOMED ancestors provide a shortcut; in raw claims, every CPT code of interest must be listed explicitly.
- AMA copyright vs ICD-10-CM/PCS public domain: ICD-10-CM and ICD-10-PCS are public-domain code sets — their descriptor text can be reproduced freely in research, software, and publications. CPT descriptors require an AMA license for reproduction.
This licensing asymmetry has practical consequences for open-source phenotype libraries, GitHub repositories, and journal publications: CPT-based code lists can share the numeric codes but not the official descriptor text without a license. Prefer public-domain alternatives where clinically equivalent (e.g., SNOMED in OMOP); when CPT is required, work within a licensed institutional environment.
When NOT to use — and when it is actively misleading or dangerous
- As the sole source for inpatient procedure ascertainment. A CPT-only code list will return zero results for inpatient facility claims (ICD-10-PCS governs there). Applying CPT to inpatient data silently drops the entire hospitalized population, systematically excluding the sicker, more complex patients. This produces a healthy-worker-style selection bias that can make a procedure look safer or more effective than it is.
- When the care setting is Medicare Advantage (managed care). MA enrollees' claims are encounter-based and historically less complete than FFS claims; procedure capture may be absent or under-coded. Do not pool MA-only and FFS person-time for procedure rates.
- When reproducing CPT descriptor text without an AMA license. Publishing, sharing, or embedding official CPT descriptor text in a database, software tool, or public repository without a valid AMA CPT license is a copyright violation. Use numeric ranges, SNOMED mappings, or plain English descriptions of procedure families instead.
- When Category III codes govern the procedure of interest without awareness of the transition timeline. If a Category III code was promoted to Category I mid-study, using only one code family will produce a time-truncated ascertainment that looks like a sudden change in utilization when it is actually a coding-system change.
- As a substitute for LOINC in laboratory result analysis. CPT identifies the billed lab test; it cannot provide the result value or the measured analyte in a LOINC-structured way. Using CPT to identify a lab order is correct; treating CPT as equivalent to a structured lab result is an analytical error.
Worked example
Scenario
A health outcomes researcher wants to count how many adult patients in a commercial insurance database had an office or outpatient visit for diabetes management in a single calendar year, using professional claims (CMS-1500 / 837P). The goal is a simple utilization rate: unique patients with at least one qualifying visit, and total visits per 100 enrolled members. The researcher must identify the right CPT codes without using licensed descriptor text, and must decide how to count — by service line or by date — to avoid inflating the number.
Dataset
Professional claim lines for three patients in the calendar year. Each row is one service line on a CMS-1500 claim. A single visit can generate more than one line (e.g., the E/M visit code plus a separate procedure code on the same date).
| person_id | service_date | cpt_code | place_of_service | paid_amount_usd |
|---|---|---|---|---|
| 1001 | 2023-03-14 | 99213 | 11 | 95.0 |
| 1001 | 2023-03-14 | 82947 | 11 | 12.0 |
| 1001 | 2023-09-05 | 99214 | 11 | 130.0 |
| 1002 | 2023-06-20 | 99212 | 11 | 72.0 |
| 1003 | 2023-01-10 | 99203 | 11 | 88.0 |
| 1003 | 2023-01-10 | 99203 | 11 | 88.0 |
Steps
Result
3 unique patients had at least one office E/M visit (CPT 99202-99215, place-of-service 11) in the calendar year. Deduplicating by (person_id, service_date) yields 4 total visit-dates across the 3 patients (person 1001 contributed 2, persons 1002 and 1003 each contributed 1). The glucose test line (CPT 82947) is excluded because its code falls outside the 99202-99215 E/M range; the duplicate row for person 1003 collapses to 1 after deduplication: 4 qualifying visit-dates / 3 unique patients = 1.33 visits per patient in the year.
Trade-offs
Runnable example
Format validation and range-based identification of CPT code categories and the E/M office visit family from professional claims. Uses only the numeric code format — no licensed descriptor text is embedded. All pattern matching is done against code format and numeric range, consistent with open research use.
import re
import pandas as pd
from typing import Optional
# ── CPT format patterns ───────────────────────────────────────────────────────
# Category I: exactly 5 numeric digits (leading zeros preserved as strings)
# Category II: 4 numeric digits + "F"
# Category III: 4 numeric digits + "T"
# PLA: 4 numeric digits + "U"
_CAT_I = re.compile(r"^\d{5}$")
_CAT_II = re.compile(r"^\d{4}F$")
_CAT_III = re.compile(r"^\d{4}T$")
_PLA = re.compile(r"^\d{4}U$")
# Modifier: 2-character alphanumeric suffix, stored separately in most datasets
_MODIFIER = re.compile(r"^[A-Z0-9]{2}$")
def classify_cpt(code: str) -> Optional[str]:
"""Return CPT category string or None if the code does not match any known format."""
if not isinstance(code, str):
return None
code = code.strip().upper()
if _CAT_I.match(code):
return "Category_I"
if _CAT_II.match(code):
return "Category_II"
if _CAT_III.match(code):
return "Category_III"
if _PLA.match(code):
return "PLA"
return None
def is_valid_cpt(code: str) -> bool:
"""True if the code string matches any valid CPT format."""
return classify_cpt(code) is not None
# ── E/M office visit range filter ────────────────────────────────────────────
# Office and outpatient E/M visits: CPT 99202–99215 (post-2021 restructuring).
# 99201 was retired effective 2021-01-01 (no longer a valid code after that date).
# Range-based filtering avoids reproducing licensed descriptor text.
_EM_OFFICE_MIN = 99202
_EM_OFFICE_MAX = 99215
# Place of service codes for office/outpatient E/M visits (CMS POS codes)
OFFICE_POS = {"11"} # physician office
TELEHEALTH_POS = {"02", "10"} # telehealth (distant site / patient home)
OUTPATIENT_POS = {"22", "19", "49"} # outpatient hospital, off-campus HOPD, independent clinic
def flag_em_office_visit(cpt_code: str, place_of_service: str) -> bool:
"""
Return True if the claim line represents an office or outpatient E/M visit.
Requires BOTH a code in the 99202-99215 range AND an office/outpatient
place-of-service code. This avoids counting the same E/M range when billed
from an ED (23), inpatient (21), or SNF (31).
"""
if classify_cpt(cpt_code) != "Category_I":
return False
try:
n = int(cpt_code)
except ValueError:
return False
in_em_range = _EM_OFFICE_MIN <= n <= _EM_OFFICE_MAX
in_office_setting = str(place_of_service).strip() in OFFICE_POS
return in_em_range and in_office_setting
# ── Deduplication: count unique visit dates, not service lines ─────────────
def count_unique_em_visits(
claims_df: pd.DataFrame,
person_col: str = "person_id",
date_col: str = "service_date",
cpt_col: str = "cpt_code",
pos_col: str = "place_of_service",
) -> pd.DataFrame:
"""
From a professional claims DataFrame, return a summary of unique E/M office
visit dates per person.
Deduplication logic:
1. Keep only lines where flag_em_office_visit() is True.
2. Deduplicate to unique (person_id, service_date) pairs — a single date with
multiple qualifying CPT lines (e.g., E/M code plus a modifier variant) counts
as one visit, not multiple.
Returns a DataFrame with columns [person_id, em_visit_count].
"""
claims_df = claims_df.copy()
claims_df[cpt_col] = claims_df[cpt_col].astype(str).str.strip().str.upper()
claims_df["_em_flag"] = claims_df.apply(
lambda r: flag_em_office_visit(str(r[cpt_col]), str(r[pos_col])), axis=1
)
em_lines = claims_df[claims_df["_em_flag"]].copy()
# Unique visit dates: deduplicate on (person_id, service_date)
unique_visits = (
em_lines[[person_col, date_col]]
.drop_duplicates()
.groupby(person_col)
.size()
.reset_index(name="em_visit_count")
)
return unique_visits
# ── Category III transition guard ─────────────────────────────────────────
def warn_category_iii_codes(cpt_list: list[str]) -> list[str]:
"""
Given a list of CPT codes, identify any Category III (####T) codes and
return them with a warning. The caller should verify whether a Category I
successor code exists for the study window.
"""
cat_iii = [c for c in cpt_list if classify_cpt(c) == "Category_III"]
if cat_iii:
print(
f"WARNING: {len(cat_iii)} Category III (emerging technology) code(s) detected: "
f"{cat_iii}. Verify whether a Category I successor code exists for any part "
"of the study window. Using only the T-code may produce time-truncated "
"ascertainment if a promotion occurred mid-study."
)
return cat_iii
# ── Example usage ─────────────────────────────────────────────────────────
if __name__ == "__main__":
# Synthetic professional claim lines (no licensed descriptor text)
sample = pd.DataFrame({
"person_id": [1001, 1001, 1001, 1002, 1003, 1003],
"service_date": ["2023-03-14", "2023-03-14", "2023-09-05",
"2023-06-20", "2023-01-10", "2023-01-10"],
"cpt_code": ["99213", "82947", "99214", "99212", "99203", "99203"],
"place_of_service": ["11", "11", "11", "11", "11", "11"],
})
# Validate all codes
sample["cpt_category"] = sample["cpt_code"].apply(classify_cpt)
print("Code classification:")
print(sample[["cpt_code", "cpt_category"]].to_string(index=False))
# Count unique E/M office visits per person
visit_summary = count_unique_em_visits(sample)
print("\nUnique E/M office visit dates per person:")
print(visit_summary.to_string(index=False))
# Expected: person 1001=2, 1002=1, 1003=1 (duplicate row deduped)CPT format validation and E/M office visit ascertainment in R, using only numeric range logic against the code string — no licensed descriptor text. Compatible with Medicare and commercial claims data where CPT codes are stored as character strings.
library(dplyr)
library(stringr)
# ── CPT format classification ─────────────────────────────────────────────
# Category I: exactly 5 digits
# Category II: 4 digits + F
# Category III: 4 digits + T
# PLA: 4 digits + U
classify_cpt <- function(code) {
code <- trimws(toupper(as.character(code)))
dplyr::case_when(
stringr::str_detect(code, "^\\d{5}$") ~ "Category_I",
stringr::str_detect(code, "^\\d{4}F$") ~ "Category_II",
stringr::str_detect(code, "^\\d{4}T$") ~ "Category_III",
stringr::str_detect(code, "^\\d{4}U$") ~ "PLA",
TRUE ~ NA_character_
)
}
is_valid_cpt <- function(code) !is.na(classify_cpt(code))
# ── E/M office visit flag ─────────────────────────────────────────────────
# Office/outpatient E/M visits: CPT 99202-99215 at place_of_service = "11"
# 99201 retired 2021-01-01; range starts at 99202 for post-2021 data.
EM_OFFICE_MIN <- 99202L
EM_OFFICE_MAX <- 99215L
OFFICE_POS <- c("11") # physician office; add "02","10" for telehealth if needed
flag_em_office_visit <- function(cpt_code, place_of_service) {
cat <- classify_cpt(cpt_code)
n <- suppressWarnings(as.integer(cpt_code))
is_em_range <- !is.na(n) & n >= EM_OFFICE_MIN & n <= EM_OFFICE_MAX
is_office <- trimws(as.character(place_of_service)) %in% OFFICE_POS
cat == "Category_I" & is_em_range & is_office
}
# ── Unique visit date count ───────────────────────────────────────────────
count_unique_em_visits <- function(df,
person_col = "person_id",
date_col = "service_date",
cpt_col = "cpt_code",
pos_col = "place_of_service") {
df |>
dplyr::mutate(
.em_flag = flag_em_office_visit(.data[[cpt_col]], .data[[pos_col]])
) |>
dplyr::filter(.em_flag) |>
dplyr::distinct(.data[[person_col]], .data[[date_col]]) |>
dplyr::count(.data[[person_col]], name = "em_visit_count")
}
# ── Category III transition warning ──────────────────────────────────────
warn_category_iii <- function(cpt_vec) {
cat3 <- unique(cpt_vec[classify_cpt(cpt_vec) == "Category_III"])
if (length(cat3) > 0) {
warning(
sprintf(
"%d Category III (emerging technology) code(s) in code list: %s. ",
length(cat3), paste(cat3, collapse = ", ")
),
"Verify whether a Category I successor exists for any part of the study window.",
call. = FALSE
)
}
invisible(cat3)
}
# ── Example ───────────────────────────────────────────────────────────────
sample_claims <- tibble::tibble(
person_id = c(1001L, 1001L, 1001L, 1002L, 1003L, 1003L),
service_date = as.Date(c("2023-03-14","2023-03-14","2023-09-05",
"2023-06-20","2023-01-10","2023-01-10")),
cpt_code = c("99213","82947","99214","99212","99203","99203"),
place_of_service = c("11","11","11","11","11","11")
)
# Classify codes
sample_claims <- sample_claims |>
dplyr::mutate(cpt_category = classify_cpt(cpt_code))
print(sample_claims |> dplyr::select(cpt_code, cpt_category))
# Count unique E/M office visits per person
visit_summary <- count_unique_em_visits(sample_claims)
print(visit_summary)
# Expected: person 1001=2, person 1002=1, person 1003=1Citations
- [1]Hirsch JA, Leslie-Mazwi TM, Nicola GN, Bhatt DL, Jovin TG, Baxter BW, et al. Current procedural terminology; a primer. Journal of NeuroInterventional Surgery. 2015;7(4):309-312.
- [2]Berenson RA, Lazaroff AE. Time Is of the Essence: Solving Office Visit Coding Problems. Journal of the American Geriatrics Society. 2019;67(8):1568-1573.
- [4]Lin KJ, Schneeweiss S. Considerations for the analysis of longitudinal electronic health records linked to claims data to study the effectiveness and safety of drugs. Clinical Pharmacology and Therapeutics. 2016;100(2):147-159.
- [5]American Medical Association. CPT (Current Procedural Terminology) Overview. AMA; 2024. Available at: https://www.ama-assn.org/practice-management/cpt
- [6]Centers for Medicare and Medicaid Services. Healthcare Common Procedure Coding System (HCPCS). CMS; 2024. Available at: https://www.cms.gov/medicare/coding-billing/healthcare-common-procedure-system