Pagination

API responses are paginated to ensure fast response times. Each page contains up to 20 SKU groups.


Basic Usage

Add the page parameter to your requests:

# First page (default)
curl "https://api.cannmenus.com/v1/products?states=California&category=Flower&page=1" \
  -H "X-Token: YOUR_API_TOKEN"

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

Response Structure

Every paginated response includes a pagination object:

{
  "data": [...],
  "pagination": {
    "total_records": 1250,
    "current_page": 1,
    "total_pages": 63,
    "next_page": 2,
    "prev_page": null
  }
}
FieldTypeDescription
total_recordsnumberTotal SKU groups matching your query
current_pagenumberPage number of current results
total_pagesnumberTotal pages available
next_pagenumber | nullNext page number, or null if on last page
prev_pagenumber | nullPrevious page number, or null if on first page

Fetching All Pages

Python

import requests

API_URL = "https://api.cannmenus.com/v1"
headers = {"X-Token": "YOUR_API_TOKEN"}

def get_all_products(states, category):
    all_products = []
    page = 1

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

        all_products.extend(data["data"])
        print(f"Page {page}/{data['pagination']['total_pages']}: {len(data['data'])} SKUs")

        if data["pagination"]["next_page"] is None:
            break

        page += 1

    return all_products

# Fetch all flower products in California
products = get_all_products("California", "Flower")
print(f"Total: {len(products)} SKUs")

JavaScript

const API_URL = "https://api.cannmenus.com/v1";
const API_TOKEN = "YOUR_API_TOKEN";

async function getAllProducts(states, category) {
  const allProducts = [];
  let page = 1;

  while (true) {
    const response = await fetch(
      `${API_URL}/products?states=${states}&category=${category}&page=${page}`,
      { headers: { "X-Token": API_TOKEN } }
    );
    const data = await response.json();

    allProducts.push(...data.data);
    console.log(`Page ${page}/${data.pagination.total_pages}: ${data.data.length} SKUs`);

    if (data.pagination.next_page === null) break;
    page++;
  }

  return allProducts;
}

// Fetch all flower products in California
const products = await getAllProducts("California", "Flower");
console.log(`Total: ${products.length} SKUs`);

Best Practices

Check for Next Page

Always use next_page to determine if more results exist:

# Correct: Check next_page
if data["pagination"]["next_page"] is not None:
    # Fetch next page

# Incorrect: Manually calculate
if page < data["pagination"]["total_pages"]:  # May miss edge cases
    # Fetch next page

Add Delays for Large Fetches

When fetching many pages, add small delays to avoid rate limiting:

import time

while True:
    response = requests.get(...)
    # Process response

    if data["pagination"]["next_page"] is None:
        break

    time.sleep(0.1)  # 100ms delay between pages
    page += 1

Use Filters to Reduce Pages

Narrow your query to reduce the number of pages:

# Broad query: 63 pages
?states=California&category=Flower

# Focused query: 5 pages
?states=California&category=Flower&lat=34.05&lng=-118.24&distance=5

Handle Empty Results

Some pages may return fewer than 20 items, and queries may return zero results:

data = response.json()

if not data["data"]:
    print("No products found matching your criteria")
else:
    print(f"Found {data['pagination']['total_records']} products")

Page Size

Each page contains up to 20 SKU groups. This is fixed and cannot be changed.