Search¶
The /v2/search endpoint provides a single text-search entry point across the most commonly searched Metrc collections. Hand it a tag, a manifest number, an item name, or a partial label, and it returns a ranked list of every matching object across the relevant Metrc collections, without you having to know which collection to look in.
A T3+ subscription is not required to use this endpoint.
flowchart LR
Q["One request<br/>query=1A4FF01..."] --> D["Format detection<br/>HEX_STRING"]
D --> G["Format gating"]
G -.-> SK(["skippedMetrcEndpointIds:<br/>endpoints that only search<br/>names or manifest numbers"])
G --> E1["ACTIVE_PACKAGES"]
G --> E2["INTRANSIT_PACKAGES"]
G --> E3["FLOWERING_PLANTS"]
G --> E4["USED_TAGS"]
G --> E5["...up to 17 endpoints by default,<br/>queried in parallel"]
E1 --> R["Scored, merged, and sorted<br/>by score descending"]
E2 --> R
E3 --> R
E4 --> R
E5 --> R What /search Does¶
Each request goes through five steps:
- Format detection. The query string is classified into one or more formats:
DATETIME,HEX_STRING,INTEGER, orBASIC_STRING. A query can match multiple formats; for example,"12345"is bothINTEGERandHEX_STRING. - Endpoint selection. If
endpointIdsis omitted, the 17 default-enabled endpoints are used. IfendpointIdsis provided, exactly that set is used (including any endpoints that are off by default). - Format gating. Endpoints whose searchable fields don't accept any of the detected query formats are removed from the request and reported in
skippedMetrcEndpointIds. For example,"Blue Dream"(BASIC_STRING) skips transfer endpoints, which only searchmanifestNumber. - Parallel fan-out. The remaining endpoints are queried in parallel against Metrc. Each endpoint is asked for at most
pageSizematches (default 10, max 100). - Score and sort. Each matched object is scored using the query length, the matched field's length, and the position of the match within that field. Results from all endpoints are merged into a single list and sorted by
scoredescending.
Query Formats¶
Format detection determines which endpoints are eligible. The detection is greedy; a single query can satisfy multiple formats simultaneously, in which case it unlocks the union of their endpoints.
| Format | Detection rule | Example |
|---|---|---|
DATETIME | ISO 8601 date or datetime: ^\d{4}-\d{2}-\d{2}(T...)?$ | 2024-07-17 |
HEX_STRING | Starts with 1A (Metrc tag prefix) or is all hex characters (0-9a-fA-F) | 1A4FF0100000022000000123 |
INTEGER | All digits | 12345 |
BASIC_STRING | Anything that doesn't match a more specific format above | Blue Dream |
Note
No endpoint currently searches date fields. A pure DATETIME query (e.g. 2024-07-17) returns an empty data array with every targeted endpoint reported in skippedMetrcEndpointIds. Use a tag, label, manifest number, or name instead.
Endpoint Catalog¶
The 26 endpoints /search can query, what each one searches, the formats it accepts, and whether it's queried by default.
| Endpoint ID | Searches | Accepted formats | Default |
|---|---|---|---|
AVAILABLE_TAGS | label | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
USED_TAGS | label | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
VOIDED_TAGS | label | HEX_STRING, INTEGER, BASIC_STRING | ❌ |
ACTIVE_ITEMS | name (also scores strainName) | BASIC_STRING | ✅ |
ACTIVE_LOCATIONS | name | BASIC_STRING | ✅ |
ACTIVE_STRAINS | name | BASIC_STRING | ✅ |
ACTIVE_HARVESTS | name | BASIC_STRING | ✅ |
INACTIVE_HARVESTS | name | BASIC_STRING | ❌ |
ACTIVE_PLANTBATCHES | name | BASIC_STRING | ✅ |
INACTIVE_PLANTBATCHES | name | BASIC_STRING | ❌ |
ACTIVE_PACKAGES | label | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
INTRANSIT_PACKAGES | label | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
INACTIVE_PACKAGES | label | HEX_STRING, INTEGER, BASIC_STRING | ❌ |
TRANSFERRED_PACKAGES | packageLabel | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
VEGETATIVE_PLANTS | label | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
FLOWERING_PLANTS | label | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
INACTIVE_PLANTS | label | HEX_STRING, INTEGER, BASIC_STRING | ❌ |
ACTIVE_MOTHER_PLANTS | label | HEX_STRING, INTEGER, BASIC_STRING | ✅ |
INACTIVE_MOTHER_PLANTS | label | HEX_STRING, INTEGER, BASIC_STRING | ❌ |
INCOMING_ACTIVE_TRANSFERS | manifestNumber | INTEGER, BASIC_STRING | ✅ |
INCOMING_INACTIVE_TRANSFERS | manifestNumber | INTEGER, BASIC_STRING | ❌ |
OUTGOING_ACTIVE_TRANSFERS | manifestNumber | INTEGER, BASIC_STRING | ✅ |
OUTGOING_INACTIVE_TRANSFERS | manifestNumber | INTEGER, BASIC_STRING | ❌ |
OUTGOING_REJECTED_TRANSFERS | manifestNumber | INTEGER, BASIC_STRING | ✅ |
ACTIVE_SALES | receiptNumber | INTEGER, BASIC_STRING | ✅ |
INACTIVE_SALES | receiptNumber | INTEGER, BASIC_STRING | ❌ |
To opt into off-by-default endpoints (INACTIVE_*, VOIDED_TAGS), pass them explicitly via endpointIds. Providing endpointIds overrides the default set entirely, if you want to add off-by-default endpoints to the defaults, you have to list every endpoint you want.
Validation Rules¶
/search rejects requests that can't produce useful results, with a 400 Bad Request. The most common rejections:
- Empty
query(or whitespace-only); there's no fan-out target. Provide a real query string. - Pure ISO 8601 date/datetime
query(e.g.2024-07-17,2024-07-17T20:26:07), no endpoint searches date fields, so the request would silently return empty. Use a tag, label, manifest number, or name instead. - Unknown
endpointIds: the error message names the specific unrecognized IDs (not the whole list you sent). - Out-of-range
pageSize: must be an integer in[1, 100]. - Out-of-range
minScore: must be a number in[0.0, 1.0].
Response Shape¶
Every successful response has the same seven top-level keys.
{
"data": [
{
"endpointId": "ACTIVE_PACKAGES",
"score": 0.7142857142857143,
"matchedEntry": { "...": "..." }
},
{
"endpointId": "TRANSFERRED_PACKAGES",
"score": 0.0833333333333333,
"matchedEntry": { "...": "..." }
}
],
"queriedMetrcEndpointIds": [
"ACTIVE_PACKAGES",
"INTRANSIT_PACKAGES",
"TRANSFERRED_PACKAGES",
"AVAILABLE_TAGS"
],
"failedMetrcEndpointIds": [],
"skippedMetrcEndpointIds": [
"ACTIVE_ITEMS",
"ACTIVE_LOCATIONS"
],
"detectedQueryFormats": ["HEX_STRING", "INTEGER"],
"endpointMetadata": [
{ "endpointId": "ACTIVE_PACKAGES", "truncated": true, "totalAvailable": 47 },
{ "endpointId": "INTRANSIT_PACKAGES", "truncated": false, "totalAvailable": 2 },
{ "endpointId": "TRANSFERRED_PACKAGES", "truncated": false, "totalAvailable": 1 },
{ "endpointId": "AVAILABLE_TAGS", "truncated": false, "totalAvailable": 0 }
],
"request": {
"query": "1A4FF...",
"licenseNumber": "LIC-00001",
"pageSize": 10,
"minScore": 0.0
}
}
data is sorted by score descending. The shape of each matchedEntry depends on its endpointId, a match from ACTIVE_PACKAGES looks like a Metrc package, a match from INCOMING_ACTIVE_TRANSFERS looks like a Metrc transfer. Always check endpointId before using a result.
queriedMetrcEndpointIds vs failedMetrcEndpointIds vs skippedMetrcEndpointIds¶
| Field | Meaning |
|---|---|
queriedMetrcEndpointIds | Endpoints that were called against Metrc and returned successfully. Their results are in data. |
failedMetrcEndpointIds | Endpoints that were called but errored, Metrc API error, timeout, or insufficient license permissions. Their results are missing. |
skippedMetrcEndpointIds | Endpoints that were not called because the detected query format isn't compatible with any of their fields. No Metrc traffic was generated. |
Together, the three arrays account for every endpoint the request would have touched. Use detectedQueryFormats if you need to understand why an endpoint was skipped.
endpointMetadata and the truncated Flag¶
Each successful endpoint contributes one entry to endpointMetadata, naming itself, whether its response was truncated, and Metrc's reported total count.
| Field | Meaning |
|---|---|
endpointId | The Metrc endpoint this entry describes, one of the IDs in queriedMetrcEndpointIds. |
truncated | true when Metrc had more matches than pageSize returned. Raise pageSize to retrieve them. |
totalAvailable | Metrc's reported total count for the underlying query, the size of the unbounded result set. |
Failed and skipped endpoints are omitted (no Metrc response to derive from).
A typical flow: receive the response, find any endpointMetadata entry with truncated: true, and decide whether to re-issue the search with a larger pageSize or scope it down with endpointIds.
request Echo¶
The request object echoes the resolved request parameters, values after server-side defaults and trimming. Useful for:
- Debugging proxied or cached responses where the original URL is no longer obvious.
- Clients that fan out multiple concurrent searches and need to match responses to requests.
- Confirming that a default value (e.g.
pageSize: 10,minScore: 0.0) was actually applied as expected.
endpointIds isn't echoed; the resolved set is queriedMetrcEndpointIds plus skippedMetrcEndpointIds.
Examples¶
Substitute your API token (Authorization: Bearer ...) and license number into each example.
Tag scan, find every collection a tag appears in:
curl 'https://api.trackandtrace.tools/v2/search?licenseNumber=LIC-00001&query=1A4FF0100000022000000123' \
-H 'Authorization: Bearer <TOKEN>'
Manifest number lookup, find a transfer by its manifest number:
curl 'https://api.trackandtrace.tools/v2/search?licenseNumber=LIC-00001&query=1234566' \
-H 'Authorization: Bearer <TOKEN>'
This query is detected as HEX_STRING and INTEGER, so it searches every endpoint that accepts either, packages, plants, tags, transfers, and so on.
Item or strain name search, narrowed to items and strains for speed:
curl 'https://api.trackandtrace.tools/v2/search?licenseNumber=LIC-00001&query=Blue%20Dream&endpointIds=ACTIVE_ITEMS,ACTIVE_STRAINS' \
-H 'Authorization: Bearer <TOKEN>'
Include inactive packages in a label search:
curl 'https://api.trackandtrace.tools/v2/search?licenseNumber=LIC-00001&query=0000123&endpointIds=ACTIVE_PACKAGES,INTRANSIT_PACKAGES,INACTIVE_PACKAGES' \
-H 'Authorization: Bearer <TOKEN>'
Next Steps¶
- Control result volume and stream results with Tuning Search Results.
- Load a full record once you have found it, by the id
/searchreturned: Loading One Record by ID. - Load full records in bulk: Supercollections.
- Check what your account may query with Permissions.