Products Endpoint

Search for cannabis products across thousands of dispensaries. Filter by location, category, brand, potency, price, and more.


Request

GET https://api.cannmenus.com/v2/products

V1 is also available at /v1/products — see V1 Legacy Format below.


Core Parameters

  • Name
    states
    Type
    string | string[]
    Description

    US state(s) or Canadian province(s) to search. Use full names (e.g., "California", "Ontario"). Required in V1. Optional in V2 (but recommended for performance).

    states=California
    states=California&states=Oregon
    

Location Parameters

Narrow results by geographic area:

  • Name
    lat
    Type
    number
    Description

    Latitude coordinate for location-based search. Must be used with lng.

  • Name
    lng
    Type
    number
    Description

    Longitude coordinate for location-based search. Must be used with lat.

  • Name
    distance
    Type
    number
    Description

    Radius in miles from the lat/lng point. Optional.


Product Filters

  • Name
    q
    Type
    string
    Description

    Full-text search across product name, brand name, category, subcategory, strain type, zipcode, and SKU fields.

    q=blue dream
    
  • Name
    category
    Type
    string
    Description

    Product category. See Categories reference for valid values.

    category=Flower
    category=Edible
    category=Vape
    
  • Name
    subcategory
    Type
    string
    Description

    Product subcategory. See Subcategories reference for valid values.

    subcategory=Gummy
    subcategory=Cartridge
    
  • Name
    tags
    Type
    string | string[]
    Description

    Product tags for detailed filtering. See Tags reference for valid values.

    tags=Live%20Resin
    tags=Indica&tags=Sugar%20Free
    
  • Name
    brand_name
    Type
    string
    Description

    Filter by brand name. Partial matching supported.

    brand_name=Stiiizy
    brand_name=Cookies
    
  • Name
    brands
    Type
    number | number[]
    Description

    Filter by brand IDs (faster than brand_name). Get IDs from the Brands endpoint.

    brands=789
    brands=789&brands=1042
    
  • Name
    retailers
    Type
    number | number[]
    Description

    Filter to specific retailer IDs. Use the Retailers endpoint to find IDs.

    retailers=10600
    retailers=10600&retailers=10601
    
  • Name
    product_name
    Type
    string
    Description

    Search product names. Partial matching supported.

    product_name=Blue%20Dream
    
  • Name
    skus
    Type
    string | string[]
    Description

    Filter by specific Cann SKU IDs.

    skus=SKU123
    skus=SKU123&skus=SKU456
    
  • Name
    display_weight
    Type
    string
    Description

    Filter by exact product weight/size.

    display_weight=3.5g
    
  • Name
    quantity_per_package
    Type
    number
    Description

    Filter by package quantity (e.g., number of gummies in a pack).

    quantity_per_package=10
    
  • Name
    menu_provider
    Type
    string
    Description

    Filter by menu data source. Options: Dutchie, Weedmaps, Leafly, IHeartJane.

    menu_provider=Dutchie
    

Potency Filters

Filter by exact cannabinoid content:

  • Name
    percentage_thc
    Type
    number
    Description

    Filter by exact THC percentage. For flower, concentrates, vapes.

    percentage_thc=25.5
    
  • Name
    percentage_cbd
    Type
    number
    Description

    Filter by exact CBD percentage.

    percentage_cbd=1.0
    
  • Name
    mg_thc
    Type
    number
    Description

    Filter by exact THC milligrams. For edibles, tinctures, topicals.

    mg_thc=100
    
  • Name
    mg_cbd
    Type
    number
    Description

    Filter by exact CBD milligrams.

    mg_cbd=50
    

Price Filter

  • Name
    latest_price
    Type
    number
    Description

    Filter by exact product price in dollars.

    latest_price=45.00
    

  • Name
    recreational
    Type
    boolean
    Description

    Filter to recreational products only.

    recreational=true
    
  • Name
    medical
    Type
    boolean
    Description

    Filter to medical products only.

    medical=true
    

Inventory & Source Filters

  • Name
    include_below_threshold
    Type
    boolean
    Description

    Whether to include low-inventory products that may be out of stock soon. Default: true. Set to false to exclude these products. Each product in the response includes an is_below_threshold field so you can identify them.

    include_below_threshold=false
    
  • Name
    include_all_menu_providers
    Type
    boolean
    Description

    Include listings from all menu data sources. Default: false (returns only the best source per retailer for faster results).

    include_all_menu_providers=true
    

Terpene Profiles

Terpene data is opt-in to keep default responses fast. Set include_terpenes=true to add terpene profiles.

  • Name
    include_terpenes
    Type
    boolean
    Description

    Include terpene profiles in response. Default: false. When true, products that have terpene data will include a terpene_profile object.

    include_terpenes=true
    
  • Name
    include_terpene_fallback
    Type
    boolean
    Description

    When include_terpenes=true, also check other listings of the same product (same UUID) for terpene data. This increases coverage from ~13% to ~20% of products. Adds terpene_source and terpene_source_product_id fields for data provenance. Default: false.

    include_terpenes=true&include_terpene_fallback=true
    

Terpene Response Fields

When include_terpenes=true:

FieldTypeDescription
terpene_profileobjectTerpene concentrations as fractions (0-1). Keys are terpene names. null if no data available.
terpene_sourcestringexact = from this listing, uuid_match = from another listing of same product. Only present with include_terpene_fallback=true.
terpene_source_product_idnumberSource product ID when terpene_source is uuid_match.

Pagination

  • Name
    page
    Type
    number
    Description

    Page number to retrieve. Default: 1.

  • Name
    per_page
    Type
    number
    Description

    Results per page. Default: 20. Max: 200. Use higher values to reduce total API calls for bulk pulls.

    per_page=200
    

Example Requests

Basic Search

Search for flower in California:

curl "https://api.cannmenus.com/v2/products?states=California&category=Flower&page=1" \
  -H "X-Token: YOUR_API_TOKEN"

Location-Based Search

Find edibles within 5 miles of Denver:

curl "https://api.cannmenus.com/v2/products?states=Colorado&category=Edible&lat=39.7392&lng=-104.9903&distance=5&page=1" \
  -H "X-Token: YOUR_API_TOKEN"

Search by Brand Name

Find Stiiizy products in California:

curl "https://api.cannmenus.com/v2/products?states=California&brand_name=Stiiizy&page=1" \
  -H "X-Token: YOUR_API_TOKEN"

Full-Text Search

Search for "blue dream" across all fields:

curl "https://api.cannmenus.com/v2/products?states=Washington&q=blue%20dream&page=1" \
  -H "X-Token: YOUR_API_TOKEN"

Multiple Filters

Live resin vapes from a specific brand in Nevada:

curl "https://api.cannmenus.com/v2/products?states=Nevada&category=Vape&brand_name=Stiiizy&tags=Live%20Resin&page=1" \
  -H "X-Token: YOUR_API_TOKEN"

Response

{
  "data": [
    {
      "retailer_id": "10600",
      "sku": "07A0Sbb8OieeaR8rIW6MVHRw",
      "products": [
        {
          "cann_sku_id": "07A0Sbb8OieeaR8rIW6MVHRw",
          "brand_name": "Cookies",
          "brand_id": 1042,
          "url": "https://dutchie.com/store/example/product/gary-payton",
          "image_url": "https://images.dutchie.com/products/gary-payton.jpg",
          "raw_product_name": "Cookies - Gary Payton 3.5g",
          "product_name": "Gary Payton",
          "raw_weight_string": "3.5g",
          "display_weight": "3.5g",
          "raw_product_category": "Flower",
          "category": "Flower",
          "raw_subcategory": "Flower",
          "subcategory": null,
          "product_tags": ["Hybrid", "Indoor"],
          "percentage_thc": 28.5,
          "percentage_cbd": 0.1,
          "mg_thc": null,
          "mg_cbd": null,
          "quantity_per_package": 1,
          "medical": false,
          "recreational": true,
          "latest_price": 55.00,
          "original_price": 65.00,
          "menu_provider": "Dutchie",
          "is_below_threshold": false,
          "is_stock_image": false,
          "terpene_profile": {
            "Myrcene": 0.0089,
            "Limonene": 0.0072,
            "Caryophyllene": 0.0045
          },
          "terpene_source": "uuid_match",
          "terpene_source_product_id": 193452100
        }
      ]
    }
  ],
  "pagination": {
    "total_records": 1250,
    "current_page": 1,
    "total_pages": 63,
    "next_page": 2,
    "prev_page": null
  }
}

Response Fields

FieldTypeDescription
retailer_idstringUnique dispensary identifier
skustringUnique product variant identifier
productsarrayListings for this SKU (may include multiple menu providers)
cann_sku_idstringSame as parent sku
brand_namestringBrand name
brand_idnumberUnique brand identifier
urlstringDirect link to product on dispensary menu
image_urlstringProduct image URL
raw_product_namestringOriginal product name from menu provider
product_namestringNormalized product name
display_weightstringProduct size/weight
categorystringNormalized category
subcategorystringNormalized subcategory (if applicable)
product_tagsarrayApplied product tags
percentage_thcnumberTHC percentage (for flower, concentrates)
percentage_cbdnumberCBD percentage
mg_thcnumberTHC milligrams (for edibles, tinctures)
mg_cbdnumberCBD milligrams
latest_pricenumberCurrent retail price
original_pricenumberOriginal price before discount (if on sale)
menu_providerstringSource platform (Dutchie, Weedmaps, Leafly, IHeartJane)
is_below_thresholdbooleantrue if inventory is low and the product may be out of stock soon. Use include_below_threshold=false to hide these.
is_stock_imagebooleantrue if the product image is a stock/placeholder photo. V1 only — not included in V2 responses.
terpene_profileobjectTerpene percentages (e.g., {"Myrcene": 0.0089}). Present on flower and some concentrates. null when not available. When the specific listing lacks terpene data but another listing of the same product has it, the profile is populated from that source (see terpene_source).
terpene_sourcestringSource of terpene data: exact = from this specific product listing, uuid_match = from another listing of the same product (same UUID). null if no terpene data available.
terpene_source_product_idnumberWhen terpene_source is uuid_match, the product ID the terpene data was sourced from. Useful for tracing data provenance.
recreationalbooleanAvailable for recreational purchase
medicalbooleanAvailable for medical purchase

Best Practices

Use Location Filters for Performance

Queries with lat/lng are faster and return more relevant results:

# Faster - location-scoped
?states=California&lat=34.0522&lng=-118.2437&distance=10

# Slower - state-wide scan
?states=California

Use IDs Instead of Text Search

Use brands and retailers with numeric IDs instead of brand_name for significantly faster queries:

# Faster - ID-based
?states=California&brands=789

# Slower - text search
?states=California&brand_name=Stiiizy

Paginate Through All Results

Don't assume all results fit on one page. Always check pagination.next_page:

all_products = []
page = 1

while True:
    response = requests.get(
        f"{API_URL}/products",
        headers=headers,
        params={"states": "California", "category": "Flower", "page": page}
    )
    data = response.json()
    all_products.extend(data["data"])

    if data["pagination"]["next_page"] is None:
        break
    page += 1

Cache Retailer IDs

If you frequently query products from the same dispensaries, cache their retailer IDs and use the retailers parameter for faster lookups.

Use per_page=200 for Bulk Pulls

The default is 20 results per page. Set per_page=200 to reduce total API calls by 10x:

?states=Colorado&per_page=200&page=1

Always Filter Medical vs Recreational

For states with adult-use/recreational markets (CA, CO, IL, MI, MA, NV, AZ, NJ, etc.), always set recreational=true to avoid double-counting from shared med/rec inventory systems. For medical-only states (FL, PA, OK), use medical=true.

# Recreational state
?states=California&recreational=true

# Medical-only state
?states=Florida&medical=true

V1 Legacy Format

V1 uses repeated query parameters instead of comma-separated strings:

GET https://api.cannmenus.com/v1/products
V2 (Recommended)V1 (Legacy)
states=California,Coloradostates=California&states=Colorado
brands=123,456brands=123&brands=456
retailers=100,200retailers=100&retailers=200
tags=Indica,Livetags=Indica&tags=Live
category=Flower,Ediblecategory=Flower (single value only)

V1 returns the same data, response format, and supports the same features (per_page, terpene profiles, all filters). The only difference is the parameter format.


Meta SKU Endpoint

The meta endpoint groups products by Meta SKU (a UUID that identifies the same product across different retailers) instead of by retailer SKU. This is useful for cross-retailer product matching and deduplication.

GET https://api.cannmenus.com/v2/products/meta

Parameters

All parameters from the main products endpoint are supported, plus:

ParameterTypeDescription
uuidsstring[]Filter by specific Meta SKU UUIDs (repeated param)

Response

The response uses the same pagination format but groups by meta_sku instead of sku:

{
  "data": [
    {
      "retailer_id": "10600",
      "meta_sku": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
      "products": [
        {
          "display_product_name": "Blue Dream 3.5g",
          "brand_name": "Cookies",
          "category": "Flower",
          "latest_price": 45.00
        }
      ]
    }
  ],
  "pagination": {
    "total_records": 523,
    "current_page": 1,
    "total_pages": 27,
    "next_page": 2,
    "prev_page": null
  }
}

Example Request

curl "https://api.cannmenus.com/v2/products/meta?states=California&category=Flower&page=1" \
  -H "X-Token: YOUR_API_KEY"

The V1 meta endpoint is also available at /v1/products/meta with the same functionality.