Python SDK

The official Python SDK for HempData API. Supports Python 3.8+ with both synchronous and asynchronous clients.

Package: hempdataapi

Installation

pip install hempdataapi
poetry add hempdataapi

Quick Start

from hempdataapi import HempDataClient

client = HempDataClient(api_key="YOUR_API_KEY")

# Check if Delta-9 edibles are legal in Colorado
regulations = client.regulations.list(
    state="CO",
    substance="delta-9-thc",
    product_type="edibles",
)

print(regulations.data[0].legal_status)
# "legal_with_restrictions"

Async Client

from hempdataapi import AsyncHempDataClient
import asyncio

async def main():
    client = AsyncHempDataClient(api_key="YOUR_API_KEY")

    regulations = await client.regulations.list(state="CO")
    print(regulations.data[0].summary)

    await client.close()

asyncio.run(main())

Async Context Manager

async with AsyncHempDataClient(api_key="YOUR_API_KEY") as client:
    regulations = await client.regulations.list(state="CO")

Configuration

client = HempDataClient(
    api_key="YOUR_API_KEY",                          # Required
    base_url="https://api.hempdataapi.com",          # Optional
    timeout=30.0,                                     # Seconds (default: 30)
    max_retries=3,                                    # Default: 3
    retry_delay=1.0,                                  # Seconds (default: 1.0)
)

Environment Variable

If no api_key is passed, the SDK reads from the HEMPDATA_API_KEY environment variable:

import os
os.environ["HEMPDATA_API_KEY"] = "YOUR_API_KEY"

client = HempDataClient()  # Reads from env

Regulations

List Regulations

result = client.regulations.list(
    state="CO",                          # optional
    substance="delta-9-thc",             # optional
    product_type="edibles",              # optional
    legal_status="legal_with_restrictions",  # optional
    min_confidence=0.8,                  # optional
    verified_after="2026-01-01",         # optional
    include_changelog=True,              # optional
    include_sources=True,                # optional
    include_bills=True,                  # optional
    page=1,                              # optional
    per_page=25,                         # optional
)

for reg in result.data:
    print(f"{reg.state} — {reg.legal_status}: {reg.summary}")

Get Single Regulation

regulation = client.regulations.get("reg_a1b2c3d4-5678-9012-abcd-ef3456789012")
print(regulation.data.summary)
print(regulation.data.confidence)

Get Changelog

changes = client.regulations.changelog(
    state="TX",
    since="2026-01-01T00:00:00Z",
    per_page=50,
)

for change in changes.data:
    print(f"[{change.event_type}] {change.state}: {change.plain_english_summary}")

Compare States

comparison = client.regulations.compare(
    states=["CO", "CA", "TX", "FL", "NY"],
    substance="delta-9-thc",
    product_type="edibles",
)

print(comparison.data.summary)
for state in comparison.data.comparison:
    print(f"  {state.state_name}: {state.legal_status}")

States

List All States

states = client.states.list(sort="avg_confidence", order="desc")

for state in states.data:
    print(f"{state.name}: {state.total_regulations} regulations "
          f"(avg confidence {state.avg_confidence:.2f})")

Get State Profile

colorado = client.states.get("CO", include_sources=True, include_bills=True)

print(colorado.data.regulatory_authority)
print(colorado.data.hr5371_impact_assessment)

for highlight in colorado.data.key_highlights:
    print(f"  - {highlight}")

Substances and Product Types

# List tracked substances
substances = client.substances.list()
for sub in substances.data:
    print(f"{sub.name}: {sub.federal_status} "
          f"({sub.states_prohibited} states prohibit)")

# List product types
product_types = client.product_types.list()
for pt in product_types.data:
    print(f"{pt.name}: {pt.states_with_regulations} states")

Webhooks

Create Webhook

webhook = client.webhooks.create(
    endpoint_url="https://yourapp.com/webhooks/hempdata",
    filters={
        "states": ["CO", "CA", "TX"],
        "substances": ["delta-9-thc"],
        "event_types": ["legal_status_changed"],
    },
    description="Monitor THC regulation changes",
)

# IMPORTANT: Save the signing secret — only returned once
print(f"Signing secret: {webhook.data.signing_secret}")
print(f"Webhook ID: {webhook.data.id}")

List Webhooks

webhooks = client.webhooks.list()
for wh in webhooks.data:
    print(f"{wh.id}: {wh.status} — {wh.total_deliveries} deliveries")

Delete Webhook

client.webhooks.delete("whk_a1b2c3d4e5f6")

Verify Webhook Signature

from hempdataapi import verify_webhook_signature

# In your Flask/FastAPI handler
def handle_webhook(request):
    raw_body = request.data  # bytes
    signature = request.headers.get("X-HempData-Signature", "")
    secret = os.environ["HEMPDATA_WEBHOOK_SECRET"]

    if not verify_webhook_signature(raw_body, signature, secret):
        return "Invalid signature", 401

    event = request.get_json()
    # Process event...
    return "OK", 200

FastAPI Webhook Handler

from fastapi import FastAPI, Request, HTTPException
from hempdataapi import verify_webhook_signature
import os

app = FastAPI()

@app.post("/webhooks/hempdata")
async def hempdata_webhook(request: Request):
    raw_body = await request.body()
    signature = request.headers.get("x-hempdata-signature", "")

    if not verify_webhook_signature(raw_body, signature, os.environ["HEMPDATA_WEBHOOK_SECRET"]):
        raise HTTPException(status_code=401, detail="Invalid signature")

    event = await request.json()

    if event["event_type"] == "legal_status_changed":
        state = event["jurisdiction"]["state"]
        new_status = event["after"]["legal_status"]
        print(f"ALERT: {state} changed to {new_status}")

    return {"status": "ok"}

Widget Check

result = client.widget.check(
    substance="delta-9-thc",
    product_type="edibles",
    state="CO",
)

print(result.data.badge_color)   # "yellow"
print(result.data.headline)      # "Legal with Restrictions"
for restriction in result.data.restrictions:
    print(f"  - {restriction}")

CSV Export

# Get CSV as a string
csv_data = client.regulations.list(
    state="CO",
    format="csv",
    include_sources=True,
)

# Write to file
with open("colorado-regulations.csv", "w") as f:
    f.write(csv_data)

Billing

# Create checkout session
checkout = client.billing.create_checkout(
    tier="professional",
    billing_period="annual",
    email="user@company.com",
    success_url="https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}",
    cancel_url="https://yourapp.com/cancel",
)
print(f"Redirect to: {checkout.data.checkout_url}")

# Create portal session
portal = client.billing.create_portal(
    return_url="https://yourapp.com/dashboard",
)
print(f"Redirect to: {portal.data.portal_url}")

Error Handling

from hempdataapi import (
    HempDataError,
    RateLimitError,
    AuthenticationError,
    NotFoundError,
    ValidationError,
)

try:
    result = client.regulations.list(state="XX")
except AuthenticationError:
    print("Invalid API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except ValidationError as e:
    print(f"Bad request: {e.message} (field: {e.field})")
except NotFoundError:
    print("Resource not found")
except HempDataError as e:
    print(f"API error [{e.code}]: {e.message}")

Pagination

Manual Pagination

page = 1
while True:
    result = client.regulations.list(
        substance="delta-9-thc",
        page=page,
        per_page=50,
    )
    for reg in result.data:
        print(f"{reg.state}: {reg.legal_status}")

    if page >= result.meta.total_pages:
        break
    page += 1

Auto-Pagination Iterator

# Iterate through all pages automatically
for regulation in client.regulations.paginate(substance="delta-9-thc"):
    print(f"{regulation.state}: {regulation.legal_status}")

Async Auto-Pagination

async for regulation in client.regulations.paginate(substance="delta-9-thc"):
    print(f"{regulation.state}: {regulation.legal_status}")

Django Integration Example

# settings.py
HEMPDATA_API_KEY = os.environ["HEMPDATA_API_KEY"]

# views.py
from django.http import JsonResponse
from hempdataapi import HempDataClient
from django.conf import settings

client = HempDataClient(api_key=settings.HEMPDATA_API_KEY)

def check_compliance(request):
    state = request.GET.get("state", "CO")
    substance = request.GET.get("substance", "cbd")
    product_type = request.GET.get("product_type", "edibles")

    result = client.regulations.list(
        state=state,
        substance=substance,
        product_type=product_type,
    )

    if not result.data:
        return JsonResponse({"can_sell": False, "reason": "No data available"})

    reg = result.data[0]
    can_sell = reg.legal_status in ("legal", "legal_with_restrictions")

    return JsonResponse({
        "can_sell": can_sell,
        "legal_status": reg.legal_status,
        "summary": reg.summary,
        "confidence": reg.confidence,
    })

Type Hints

All SDK methods and response objects are fully typed. Use with mypy or Pyright for full type checking:

from hempdataapi.types import (
    Regulation,
    State,
    Substance,
    ProductType,
    Webhook,
    WidgetCheckResult,
    ChangelogEntry,
    ComparisonResult,
    BillInfo,
    PaginatedResponse,
)