Skip to content

Retail ID Labels

Metrc's Retail ID program puts a QR code on each retail unit. You download a set of codes for a package, then print them as labels. This page covers doing that through the API.

Metrc never produces a printable PDF on its server. Its own page draws the PDF in your browser. The API gives you the codes as CSV rows, which you can turn into labels with the Label Studio.

Endpoint What it does
GET /v2/packages/product-labels/download/source-packages Packages you can download labels for
POST /v2/packages/product-labels/download Issue labels for packages
GET /v2/packages/product-labels/print/csv Read one issued set as CSV rows
GET /v2/packages/product-labels/print/worker Read Metrc's instructions for drawing one set as a PDF
GET /v2/packages/product-labels Packages that have labels
GET /v2/packages/product-labels/download-history?packageId= The sets issued for one package
POST /v2/packages/product-labels/redownload Fetch a set issued earlier
GET /v2/packages/product-labels/download/compliance-templates?packageId= California's label templates for one package

Downloading labels is a write: it is a dry run by default, and with submit=true Metrc issues the labels. Metrc offers no way to withdraw them. Metrc's own form warns against labelling bulk material, whose child packages then cannot get labels of their own.

Downloading and printing

A download answers with Metrc's response under metrcResponse. Each entry in its result names one issued set. Pass its issuanceId and labelSet to the print endpoint for its format.

import requests

API = "https://api.trackandtrace.tools"
HEADERS = {"Authorization": f"Bearer {ACCESS_TOKEN}"}
PARAMS = {"licenseNumber": "RET00001"}

answer = requests.post(
    f"{API}/v2/packages/product-labels/download",
    params={**PARAMS, "submit": "true"},
    json=[{"packageId": 12345, "labelQuantity": 100, "format": "csv"}],
    headers=HEADERS,
).json()

for issued in answer["metrcResponse"]["result"]:
    if not issued.get("issuanceId"):
        print("Metrc refused this entry:", issued)
        continue

    rows = requests.get(
        f"{API}/v2/packages/product-labels/print/csv",
        params={**PARAMS, "issuanceId": issued["issuanceId"], "labelSet": issued.get("labelSet")},
        headers=HEADERS,
    ).json()["csv"]

    header, labels = rows[0], rows[1:]
    print(header, len(labels), "labels")

A "message": "Success" means Metrc accepted the request. Metrc can still refuse one entry inside it, so check each entry for an issuanceId as above.

For "format": "pdf", read the set from /print/worker instead. Metrc's own page reads an entry whose format is newPdf the same way.

Fetching labels again

Each package on GET /v2/packages/product-labels keeps a download history, one row per set. Redownload fetches a set issued earlier, and answers the same way as a download.

history = requests.get(
    f"{API}/v2/packages/product-labels/download-history",
    params={**PARAMS, "packageId": 12345},
    headers=HEADERS,
).json()["data"]

answer = requests.post(
    f"{API}/v2/packages/product-labels/redownload",
    params={**PARAMS, "submit": "true"},
    json={"id": 12345, "format": "csv", "labelSet": history[0]["labelSet"]},
    headers=HEADERS,
).json()

Leave out labelSet to send none, as the buttons on Metrc's grid rows do.

Staged labels

In California and Michigan, Metrc can issue labels for a tag before any package carries it. Every endpoint above has a staged twin under /v2/packages/staged-product-labels, taking a tagId instead of a packageId: download, redownload, download-history?tagId=, download/source-tags, download/inputs and download/compliance-templates?tagId=. The print endpoints serve both.

California compliance templates

California's form offers label templates. Read a package's with GET /v2/packages/product-labels/download/compliance-templates?packageId=12345, then send the chosen template's template as complianceLabelTemplate and its templateId with the download. Both are optional.