Your scraper runs every night, throws no errors, the dashboard updates, and the numbers move in reasonable ways. At a glance, everything says the system is working correctly. There is new data, the metrics change, and nobody gets an alert.
The problem is that this data may be describing a market where you sell nothing.
This is one of the most expensive failures in cannabis menu scraping and also the hardest to detect, because it does not behave like a failure. There are no errors and no empty pages: the scraper returns data, the pipeline processes it, and the dashboard updates.
It behaves like a success.
But that data may be incomplete, shaped by the location the scraper runs from, or representing a different menu than the one your customers see. And by the time you catch it, you may already have spent weeks making decisions on information that looked reliable.
What is actually happening
US dispensary menus are served based on the location they are requested from. This is not simple visual personalization: location is part of the product's own architecture.
A menu platform needs to know where you are before it shows you anything, because cannabis is a legally state by state market. The catalog, the prices, the potency limits, the permitted formats, and even whether you see medical or adult use pricing all depend on where the person looking is standing.
So the platform resolves location however it can: by IP, by a URL parameter, by a session cookie, by a saved preference. And when it cannot resolve it confidently, it almost never returns an error. It returns something: a default market, a generic catalog, a store list from another region, or the menu for whatever location it resolved incorrectly.
Your extractor receives a 200 response, with the expected structure, containing the fields it knows how to parse. And it saves it.
Why this is worse in cannabis than in any other industry
In traditional e-commerce, a price captured from the wrong region is still the price of a real product, one that probably also sells where you sell. It is a precision error.
Not in cannabis. Products from one state do not exist in another, because interstate commerce remains prohibited at the federal level and each market operates as a closed seed-to-sale silo, with its own licensed brands and its own packaging and potency rules.
A Colorado menu captured in the belief that it was Michigan is not an imprecise approximation of the Michigan market. Those are SKUs that cannot exist in Michigan, from brands not licensed in Michigan, at prices shaped by a different tax structure.
You are not measuring badly. You are measuring something else.
And there is a subtler variant of the same problem that shows up inside a single state: in markets running medical and adult use programs in parallel, many stores publish two menus with different prices for the same product. In Ohio, for example, an adult use purchase carries a 10% recreational tax that medical patients do not pay, on top of discounts and loyalty programs exclusive to the medical channel. An extractor that does not pin down which of the two menus it is looking at produces a series that alternates between them without warning, with price differences that look like market movement and are not.
How to detect it
This is the useful part, and it works even if you are only now getting suspicious about a series you have been accumulating for months.
Manual comparison against ground truth. Open a store's menu from the real market, in a browser, and compare it against what your extractor saved that same night. It is a simple check, and also the most reliable one there is. Do it with three stores per market.
A canary product. Pick a SKU that only sells in that market, ideally from a brand licensed only there, and validate on every run that it appears. If the canary vanishes from every store at once, the problem is not assortment. It is location.
SKU counts out of range. A menu that had been returning between 300 and 500 products and suddenly returns 40, or returns 2,000, almost never changed its assortment. It changed markets, or it landed on a generic catalog. Set thresholds and make them fail loudly.
Store address against expected state. If your extractor also captures the dispensary record, you have the cheapest validation available: check that the address state matches the market you thought you were looking at. It is one line of code and it catches most cases.
Stores that should not be there. If the listing includes locations you know are two hundred and fifty miles away, you already have your answer.
How it gets solved badly
One exit point for everything. A single capture location, chosen once, serving markets across twelve different states. It works on day one because somebody tested it against one market and it came back fine.
Hardcoding coordinates and never looking again. A latitude and longitude get written in, verified once, and sit there for two years. Platforms change how they resolve location, and that value stops having the effect it used to with nothing to signal the change.
Trusting the URL parameter. Many platforms accept a location parameter and respect it almost always. "Almost always" is exactly the kind of guarantee that produces series with contaminated stretches nobody can locate afterward.
And the underlying error: treating the absence of an error as a signal of success. A pipeline that only alerts when something breaks has no way to tell you when something is working incorrectly.
How it gets solved well
Capture from the location that matches each market. Not one location. One per market, verified.
Validate on every run, not once. The canary, the SKU range, and the address state get checked on every execution, and when they fail they stop the write instead of saving anyway. A declared gap in the series beats a contaminated stretch that looks fine.
Log the vantage point alongside the data. Store where each record was captured from. It is one more field, and it is what lets you audit backwards when somebody asks eight months from now whether that odd quarter was real.
And treat validation as part of the extractor, not an add-on. The code that fetches the data and the code that verifies it came from the right market are the same project.
The other two failures that also stay quiet
Geo-gating is the most expensive failure, but it does not stand alone: it belongs to a family of problems that share one trait. The other two worth mentioning are:
The extractor that breaks on a Friday. A platform changes its structure, the parser stops finding fields, and the process saves empty records without complaining. Nobody looks until Monday. The series ends up with a three day hole, and those are probably the exact days your competitor moved price. The fix is alerting on record volume, not on exceptions.
The series that overwrites instead of accumulating. The extractor neatly saves the current state of the menu and replaces it every night. The dashboard always shows the day's data and appears to work perfectly. The problem shows up months later: you do not have a historical series, you have a snapshot. And that history cannot be rebuilt, because a competitor's menu from six months ago no longer exists in any archive.
All three failures share the same trait: they produce a system that looks healthy. That is why they survive so long without anyone catching them.
Conclusion
In cannabis scraping, the question "is it running?" is useless. It is always running. The question is "am I confident this data is from the market I think it is?", and it can only be answered if somebody built the validation that answers it.
That weighs more in this industry than in any other, for one concrete reason: there is no second source here. State regulators publish a great deal of information, but it tends to be aggregated and to arrive late. Industry data providers offer point of sale information, but always with their own scope. The price the dispensary ten blocks away is selling at today exists in exactly one place: its public menu. And that data only reaches your database if somebody extracts it.
When web scraping is the only instrument, its quality is directly the quality of your data: there is no internal system that will contradict a badly captured number before it ends up in a pricing decision.
On top of that, the data is perishable. Today's menu disappears tonight and is kept in no archive. Web scraping is not just how the cannabis market gets measured: it is the act that creates its historical record. Which is why it stopped being a script and became a discipline, with capture verified per market, validation on every run, alerting on data volume, and accumulation with timestamps.
If you have a series running for months and you have never run the canary check, run it now. Half the time you will find nothing. The other half, better to find out today than in the meeting where you present the conclusions.
At AUTOScraping we keep cannabis menu pipelines running in production, and market validation is part of the design rather than an add-on. If you are interested in how we work, you can learn more at Data Factory
Sources
- Harris Beach Murtha, Does Rescheduling Cannabis Make it Eligible for Interstate Commerce?, on the interstate commerce prohibition and closed state by state production silos
- Uplift Ohio, Differences Between Purchasing Medical and Adult-Use Marijuana in Ohio, on the 10% recreational tax and price differences between channels
- Jane, Cannabis Dispensary Software Platform, on how menu platforms work
- Jane, Cannabis Dispensaries Near Me, dispensary listings sorted by the user's location
- Massachusetts Cannabis Control Commission, Open Data Catalog, public data on licenses, sales and prices
- California Department of Cannabis Control, Data Dashboards, reports on licenses, harvests and sales




