{"title":"The Rhode Island METRC API returns an empty HTTP 200 when the required date filters are missing, and several other traps produce the same empty answer","url":"https://www.seedlink.net/guides/metrc/rhode-island-api-traps","json":"https://www.seedlink.net/guides/metrc/rhode-island-api-traps/data.json","question":"Why does METRC return 0 packages when I query the Rhode Island API?","answer":"Because lastModifiedStart and lastModifiedEnd are required on /packages/v2/active and /items/v2/active and Rhode Island production answers HTTP 200 with an empty list when they are missing; if the filters are present, check the pageSize cap of 20, the route timeout and the credential before believing the empty list.","environment":"Rhode Island production and sandbox","observed":"2026-08-20","revised":"2026-09-20","published":"2026-09-20","facts":[{"id":"sweep_endpoints","value":"A read-only sweep of 49 METRC endpoints at one production cultivator facility; SeedLink calls about 25 of the 268 documented paths","observed":"2026-07-25","source":"Rhode Island production, one cultivator facility, read-only probe script"},{"id":"plural_tags_path_404","value":"GET /tags/v2/packages/available (plural) answers HTTP 404; the real path is the singular /tags/v2/package/available","observed":"2026-07-25","source":"Rhode Island production, the same read-only sweep"},{"id":"package_history_404","value":"GET /packages/v2/{id}/history answers HTTP 404: no per-package audit trail endpoint exists","observed":"2026-07-25","source":"Rhode Island production, the same read-only sweep"},{"id":"sales_patients_401","value":"Every /sales/* and /patients/* path answers HTTP 401 for a cultivator credential","observed":"2026-07-25","source":"Rhode Island production, the same read-only sweep, one cultivator license"},{"id":"rejected_transfers_empty","value":"GET /transfers/v2/rejected answers HTTP 200 with 0 rows for one cultivator","observed":"2026-07-25","source":"Rhode Island production, the same read-only sweep"},{"id":"readonly_vendor_key","value":"With a vendor key issued as read-only, every GET answered HTTP 200 and every POST and PUT answered HTTP 401; two different write endpoints both answering 401 marked the key rather than the payload, and the sync log stayed green throughout","observed":"2026-06-04","source":"Rhode Island production, one cultivator facility, the integration’s own request log"},{"id":"sandbox_adjust_echo","value":"After PUT /packages/v2/adjust, GET /packages/v2/{label} reported the last adjustment amount as Quantity: an adjustment of -9 on a 9 g package read back as -9, and a later +4.5 read back as +4.5; repeated reads returned the same value","observed":"2026-07-23","source":"Rhode Island sandbox, one test facility"},{"id":"sandbox_finish_blocked","value":"PUT /packages/v2/finish validated against that echoed Quantity, so a package could not be finished as empty; negative package quantities are real on the sandbox","observed":"2026-07-23","source":"Rhode Island sandbox, one test facility"},{"id":"sandbox_retry_after","value":"A 23-second Retry-After after two full syncs and a packaging scan inside a few minutes","observed":"2026-07-01","source":"Rhode Island sandbox, July 2026; the record gives the month only, so the first day of the month stands in for the day"},{"id":"sandbox_sync_duration","value":"A full sandbox sync of 186 packages took about 36 to 38 seconds","observed":"2026-07-23","source":"Rhode Island sandbox, one test facility, timed by the sync route"},{"id":"sync_past_timeout","value":"A sync of 258 packages finished in about 105 seconds; the route that started it had returned at a 30-second timeout reporting 0 synced","observed":"2026-08-20","source":"Rhode Island sandbox, one facility, the sync route and its log"},{"id":"pagesize_cap","value":"HTTP 400 with the body \"pageSize must be a positive number between 1 and 20.\" for any pageSize above 20, on GET /transfers/v2/deliveries/{id}/packages; the cap applies to /packages/v2/* and /transfers/v2/* alike","observed":"2026-06-01","source":"Rhode Island production, a read-only probe during a transfer reconciliation in June 2026; the record gives the month only, so the first day of the month stands in for the day"},{"id":"wrong_facility_400","value":"HTTP 400 with the body \"Item N does not exist at the current Facility.\" from PUT /items/v2/ at a facility that does not own the item, beside HTTP 401 and HTTP 404 for the same condition","observed":"2026-07-09","source":"Rhode Island sandbox, the item-update walk across /facilities/v2/"}],"constants":[{"value":64,"why":"base64, the name of the encoding in the Authorization header"},{"value":365,"why":"the width in days of the lastModified window SeedLink’s sync sends by default; a design choice, not an observation"},{"value":24,"why":"hours in a day, in the worked example of a one-year window written in seconds"},{"value":60,"why":"minutes in an hour and seconds in a minute in that worked example; also the spacing in milliseconds SeedLink’s client keeps between requests on one credential"},{"value":6,"why":"a window of 365 seconds is about 6 minutes: the width the seconds-not-days mistake produced"},{"value":5,"why":"the buffer in minutes that METRC’s own integration guidance puts behind a LastModified poll to absorb clock drift; a stated rule, not an observation"},{"value":42,"why":"worked example of the PageSize field: a 42-row result paged at 20 reports PageSize 20, 20 and then 2"},{"value":2,"why":"the last page of that worked example"},{"value":4,"why":"the number of requests SeedLink’s client allows in flight against one credential; a design choice"},{"value":3.5,"why":"the least total delay in seconds that SeedLink’s exponential backoff puts across its three sleeps; a property of the client, not an observation of METRC"},{"value":1320.46,"why":"the example in SeedLink’s field guide of a weight-based package Quantity: a decimal, not an integer"}],"faq":[{"question":"Why does GET /packages/v2/active return an empty list on Rhode Island?","answer":"Because lastModifiedStart and lastModifiedEnd are required and Rhode Island production answers HTTP 200 with an empty list when they are missing, with no error. Send a window; SeedLink’s default is 365 days, and production accepted a year."},{"question":"How wide can the lastModified window be?","answer":"Rhode Island production accepted a window a year wide when SeedLink’s 365-day default was tested. A California sandbox enforces at most 24 hours and answers HTTP 400 for anything wider."},{"question":"My paginator says a manifest has zero packages, but METRC Connect shows some. Why?","answer":"Check the page size first. Any pageSize above 20 is refused with HTTP 400 and pageSize must be a positive number between 1 and 20., and a loop that treats a status other than 200 as the end of the data reads that refusal as an empty last page. Pin pageSize to 20, throw on any other status, and follow TotalPages."},{"question":"Should I honour Retry-After: 0?","answer":"Not literally. On Rhode Island most rate-limited responses from GET /labtests/v2/results carry Retry-After: 0, and sleeping for zero is an immediate retry into the same burst. Floor the delay at your own exponential backoff, at least 3.5 seconds across three sleeps."},{"question":"Every write returns 401 but every read works. What is wrong?","answer":"The key, not the payload. On 2026-06-04 a vendor key issued as read-only did exactly that on Rhode Island production, and the sync log stayed green because reads were healthy. Two different write endpoints both answering 401 is scope; a payload problem answers 400."},{"question":"PUT /items/v2/ says the item does not exist, but GET finds it. Why?","answer":"The item belongs to another facility. GET /items/v2/{id} reads across facilities, while PUT /items/v2/ is accepted only by the owner, and a non-owning license answers 401, 404, or 400 with Item N does not exist at the current Facility. (Rhode Island sandbox, 2026-07-09). Try the owning license."},{"question":"After an adjustment, the sandbox reads the adjustment amount as the package quantity. Is production the same?","answer":"Unknown, and this page does not answer it. On 2026-07-23 the Rhode Island sandbox reported the last adjustment amount as Quantity (an adjustment of -9 on a 9 g package read -9) and finish validated against it. SeedLink has not read a production package back after an adjustment, so it has no observation of production either way. Treat the echo as a sandbox behaviour and verify on the facility you are actually calling."},{"question":"The sync said 0 synced and the catalog filled in a minute later. Which is right?","answer":"The catalog. On 2026-08-20 a sync of 258 packages finished in about 105 seconds after its route had returned at a 30-second timeout reporting 0 synced. A count from a caller that stopped waiting is not a count."}],"sources":["SeedLink METRC field guide (docs/METRC.md), sections Auth & Credentials, Sync (with the pageSize cap and the Retry-After: 0 notes) and Items Are Facility-Scoped, read 2026-09-20. The empty answer without date filters and the Retry-After counts are recorded there without a date.","Read-only sweep of 49 METRC endpoints at one Rhode Island production cultivator facility, 2026-07-25.","A vendor key issued as read-only: every GET 200, every POST and PUT 401, Rhode Island production, 2026-06-04.","Rhode Island sandbox: adjustment echo reads and a full sync of 186 packages, 2026-07-23; a 23-second Retry-After after two syncs and a packaging scan, July 2026 (month only).","Sync of 258 packages timed against its route’s 30-second timeout, Rhode Island sandbox, 2026-08-20."],"citation":"SeedLink, \"The Rhode Island METRC API returns an empty HTTP 200 when the required date filters are missing, and several other traps produce the same empty answer\", observed 2026-08-20, revised 2026-09-20, https://www.seedlink.net/guides/metrc/rhode-island-api-traps","methodology":"https://www.seedlink.net/methodology","publisher":"SeedLink"}