Understanding SKUs
Products in the CannMenus API are organized by SKU (Stock Keeping Unit) to consolidate listings from multiple sources. This structure makes it easy to compare prices and availability for the same product across platforms.
Data Structure
The API organizes product data in a three-level hierarchy:
Response
├── Retailer (dispensary)
│ ├── SKU Group (unique product variant)
│ │ ├── Product listing (from Dutchie)
│ │ ├── Product listing (from Leafly)
│ │ └── Product listing (from Weedmaps)
│ └── SKU Group
│ └── Product listing
└── Retailer
└── SKU Group
└── Product listing
| Level | Description |
|---|---|
| Retailer | A unique dispensary, identified by retailer_id |
| SKU Group | A unique product variant at that retailer, identified by sku |
| Product Listing | Individual listing from a menu provider |
What is a SKU?
A SKU uniquely identifies a specific product variant within a dispensary. For example, if a dispensary sells "Blue Dream" flower in 1g, 3.5g, and 7g sizes, each size is a separate SKU.
Same Product, Different Platforms
Many dispensaries list products on multiple platforms (Dutchie, Leafly, Weedmaps, etc.). The API groups these listings under a single SKU:
{
"retailer_id": "10600",
"sku": "07A0Sbb8OieeaR8rIW6MVHRw",
"products": [
{
"cann_sku_id": "07A0Sbb8OieeaR8rIW6MVHRw",
"product_name": "Blue Dream",
"display_weight": "3.5g",
"latest_price": 45.00,
"menu_provider": "Dutchie"
},
{
"cann_sku_id": "07A0Sbb8OieeaR8rIW6MVHRw",
"product_name": "Blue Dream",
"display_weight": "3.5g",
"latest_price": 48.00,
"menu_provider": "Leafly"
}
]
}
This lets you compare pricing across platforms for the exact same product at the exact same retailer.
Benefits of SKU Grouping
1. Price Comparison
Compare the same product across menu providers:
for sku_group in response["data"]:
prices = [p["latest_price"] for p in sku_group["products"]]
product_name = sku_group["products"][0]["product_name"]
print(f"{product_name}: ${min(prices)} - ${max(prices)}")
2. No Duplicates
Without SKU grouping, the same product would appear multiple times (once per platform). SKUs consolidate these into a single entry.
3. Accurate Inventory View
The products array shows every platform where the product is listed, giving you a complete picture of its availability.
4. Consistent Identification
Even when platforms use different naming (e.g., "Blue Dream 3.5g" vs "Blue Dream - Flower - 1/8 oz"), the SKU ensures you're looking at the same product.
Full Response Example
{
"data": [
{
"retailer_id": "10600",
"sku": "07A0Sbb8OieeaR8rIW6MVHRw",
"products": [
{
"cann_sku_id": "07A0Sbb8OieeaR8rIW6MVHRw",
"brand_name": "93 Boyz",
"brand_id": 25407,
"raw_product_name": "Aeriz - 93 Boyz Kept Secret 3.5g",
"product_name": "Aeriz Kept Secret",
"raw_weight_string": "3.5g",
"display_weight": "3.5g",
"raw_product_category": "Flower",
"category": "Flower",
"raw_subcategory": "Flower",
"subcategory": null,
"product_tags": null,
"percentage_thc": null,
"percentage_cbd": null,
"mg_thc": null,
"mg_cbd": null,
"quantity_per_package": 1,
"medical": false,
"recreational": true,
"latest_price": 59.04,
"menu_provider": "Leafly"
}
]
},
{
"retailer_id": "14583",
"sku": "07sDygynAUjkxftzxSnk8Cq8",
"products": [
{
"cann_sku_id": "07sDygynAUjkxftzxSnk8Cq8",
"brand_name": "RYTHM",
"brand_id": 5163,
"raw_product_name": "Rythm Jet Fuel OG 3.5g | Flower",
"product_name": "Jet Fuel Og",
"raw_weight_string": "3.5g",
"display_weight": "3.5g",
"raw_product_category": "Flower",
"category": "Flower",
"raw_subcategory": "small-buds",
"subcategory": "Popcorn",
"product_tags": null,
"percentage_thc": null,
"percentage_cbd": null,
"mg_thc": null,
"mg_cbd": null,
"quantity_per_package": null,
"medical": false,
"recreational": true,
"latest_price": 55.0,
"menu_provider": "Dutchie"
}
]
}
],
"pagination": {
"total_records": 2,
"current_page": 1,
"total_pages": 1,
"next_page": null,
"prev_page": null
}
}
Working with SKUs
Flatten Products for Analysis
# Convert nested structure to flat list
all_products = []
for sku_group in response["data"]:
for product in sku_group["products"]:
product["retailer_id"] = sku_group["retailer_id"]
product["sku"] = sku_group["sku"]
all_products.append(product)
# Now analyze as flat DataFrame
import pandas as pd
df = pd.DataFrame(all_products)
Find Best Price per SKU
for sku_group in response["data"]:
best = min(sku_group["products"], key=lambda p: p["latest_price"])
print(f"{best['product_name']}: ${best['latest_price']} on {best['menu_provider']}")
Current Limitations
SKUs are currently scoped to individual dispensaries. The same product at different dispensaries will have different SKUs. Cross-dispensary SKU matching is on our roadmap.
This means:
- Use SKUs to compare listings within a single retailer
- To find the same product across retailers, use
brand_name+product_namefilters
