Skip to content

Filterable Fields

Most collections that have their own table in Metrc have a /fields endpoint beside them. It lists the fields Metrc's table offers one of your licenses, with the operators each field takes, in the order Metrc's table shows its columns. Use it to build a filter form. A collection route accepts a filter only on its own fields; see Limits and Behavior.

GET /v2/plants/vegetative/fields?licenseNumber=LIC-00001
GET /v2/packages/active/fields?licenseNumber=LIC-00001

A T3+ subscription is not required to use this endpoint.

Listing Fields

Parameter Required Description
licenseNumber yes The license to list fields for. One license per request
import requests

response = requests.get(
    "https://api.trackandtrace.tools/v2/packages/active/fields",
    params={"licenseNumber": "LIC-00001"},
    headers={"Authorization": f"Bearer {access_token}"},
).json()

for field in response["fields"]:
    print(field.get("label", field["field"]), field["type"], field["operators"])
{
  "t3EndpointId": "METRC_ACTIVE_PACKAGES",
  "fields": [
    {
      "field": "label",
      "metrcField": "Label",
      "label": "Tag",
      "type": "string",
      "operators": ["endswith", "startswith", "eq", "neq"],
      "sortable": true,
      "metrc": {
        "title": "Tag",
        "field": "Label",
        "filterable": {
          "operators": {
            "string": {
              "endswith": "Ends with",
              "startswith": "Starts with",
              "eq": "Equal to",
              "neq": "Not Equal to"
            }
          }
        },
        "metrcHtmlField": true,
        "filterOperators": {
          "endswith": "Ends with",
          "startswith": "Starts with",
          "eq": "Equal to",
          "neq": "Not Equal to"
        }
      }
    },
    {
      "field": "item.unitCbdPercent",
      "metrcField": "Item.UnitCbdPercent",
      "label": "Unit CBD Percent",
      "type": "number",
      "operators": ["eq", "neq", "gte", "gt", "lte", "lt"],
      "sortable": true,
      "metrc": {
        "field": "Item.UnitCbdPercent",
        "hidden": true,
        "title": "Unit CBD Percent",
        "abbreviation": "Unit CBD%",
        "schemaType": "number",
        "filterOperators": {
          "eq": "Equal to",
          "neq": "Not Equal to",
          "gte": "Greater than or Equal to",
          "gt": "Greater than",
          "lte": "Less than or Equal to",
          "lt": "Less than"
        }
      }
    }
  ]
}

Field Properties

Property Description
field The name a filter takes, as in filter=item.strainName__eq:Blue Dream. Nested fields use dots
metrcField Metrc's name for the field
label The column's title in Metrc's table. Absent when Metrc gives the column none
type string, number, date, datetime or boolean. Filter values are always text; write dates as ISO 8601
operators The operators Metrc's table offers on the field, its default first. Empty when Metrc does not let the column be filtered
sortable false when Metrc does not let the column be sorted
metrc Everything Metrc declares about the column, under Metrc's own names, such as hidden for a column Metrc hides until you show it, and abbreviation for its short header. filterOperators is Metrc's label for each operator

A column's own filterable.operators, as on Tag above, replaces the operators its type would otherwise get. The operators themselves are listed in Shaping a Report.

Building a Filter

Look a field up by its label, then send its field and an operator from its operators:

import requests

BASE = "https://api.trackandtrace.tools"
HEADERS = {"Authorization": f"Bearer {access_token}"}
LICENSE = "LIC-00001"

fields = requests.get(
    f"{BASE}/v2/plants/vegetative/fields",
    params={"licenseNumber": LICENSE},
    headers=HEADERS,
).json()["fields"]

by_label = {field["label"]: field for field in fields if "label" in field}
location = by_label["Location"]
planted = by_label["Plant Batch Date"]

plants = requests.get(
    f"{BASE}/v2/plants/vegetative",
    params=[
        ("licenseNumber", LICENSE),
        ("filter", f"{location['field']}__contains:Room A"),
        ("filter", f"{planted['field']}__gte:2026-01-01T00:00:00.000Z"),
    ],
    headers=HEADERS,
).json()["data"]

That sends filter=locationName__contains:Room A and filter=plantedDate__gte:2026-01-01T00:00:00.000Z. Repeated filters must all match unless you add filterLogic=or.

Limits and Behavior

  • One license per request. licenseNumber must be one of your session's licenses, or the response is 403 with the code FORBIDDEN. Sending it twice is a 400.
  • Fields differ between licenses. Metrc shows each license the columns that apply to it, so two licenses in the same state can get different lists for the same collection. Ask once per license.
  • A license without the table gets a 403 with the code METRC_RESOURCE_UNAUTHORIZED. California has no vegetative plants table, for example.
  • Only collections with their own table. A collection Metrc shows only inside another record, such as a package's history, has no /fields. Processing jobs, harvest schedules and data import templates have no /fields either.
  • A collection accepts only its own fields. A filter must name one of the collection's fields; packages also accept item.name, item.strainName, item.productCategoryName and item.unitThcContent. Any other field is a 400 that lists the accepted ones, even a column /fields lists, such as item.unitCbdPercent on packages.
  • Free. No T3+ subscription required. /fields has its own limit per Metrc user, separate from the limit your other requests share; a 429 with the code RATE_LIMIT_EXCEEDED means wait and retry.

Next Steps

  • Shaping a Report: filter, sort and choose columns for a report
  • Metrc Pages: link from your tool to the Metrc table a collection comes from