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.
licenseNumbermust be one of your session's licenses, or the response is403with the codeFORBIDDEN. Sending it twice is a400. - 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
403with the codeMETRC_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/fieldseither. - A collection accepts only its own fields. A
filtermust name one of the collection's fields; packages also acceptitem.name,item.strainName,item.productCategoryNameanditem.unitThcContent. Any other field is a400that lists the accepted ones, even a column/fieldslists, such asitem.unitCbdPercenton packages. - Free. No T3+ subscription required.
/fieldshas its own limit per Metrc user, separate from the limit your other requests share; a429with the codeRATE_LIMIT_EXCEEDEDmeans 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