- Move deprecated directories to src/_deprecated/: - hydration/ (old pipeline approach) - scraper-v2/ (old Puppeteer scraper) - canonical-hydration/ (merged into tasks) - Unused services: availability, crawler-logger, geolocation, etc - Unused utils: age-gate-playwright, HomepageValidator, stealthBrowser - Archive outdated docs to docs/_archive/: - ANALYTICS_RUNBOOK.md - ANALYTICS_V2_EXAMPLES.md - BRAND_INTELLIGENCE_API.md - CRAWL_PIPELINE.md - TASK_WORKFLOW_2024-12-10.md - WORKER_TASK_ARCHITECTURE.md - ORGANIC_SCRAPING_GUIDE.md - Add docs/CODEBASE_MAP.md as single source of truth - Add warning files to deprecated/archived directories - Slim down CLAUDE.md to essential rules only 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
12 KiB
Analytics V2 API Examples
Overview
All endpoints are prefixed with /api/analytics/v2
Filtering Options
Time Windows:
?window=7d- Last 7 days?window=30d- Last 30 days (default)?window=90d- Last 90 days
Legal Type Filtering:
?legalType=recreational- Recreational states only?legalType=medical_only- Medical-only states (not recreational)?legalType=no_program- States with no cannabis program
1. Price Analytics
GET /price/product/:id
Get price trends for a specific store product.
Request:
GET /api/analytics/v2/price/product/12345?window=30d
Response:
{
"store_product_id": 12345,
"product_name": "Blue Dream 3.5g",
"brand_name": "Cookies",
"category": "Flower",
"dispensary_id": 101,
"dispensary_name": "Green Leaf Dispensary",
"state_code": "AZ",
"data_points": [
{
"date": "2024-11-06",
"price_rec": 45.00,
"price_med": 40.00,
"price_rec_special": null,
"price_med_special": null,
"is_on_special": false
},
{
"date": "2024-11-07",
"price_rec": 42.00,
"price_med": 38.00,
"price_rec_special": null,
"price_med_special": null,
"is_on_special": false
}
],
"summary": {
"current_price": 42.00,
"min_price": 40.00,
"max_price": 48.00,
"avg_price": 43.50,
"price_change_count": 3,
"volatility_percent": 8.2
}
}
GET /price/rec-vs-med
Get recreational vs medical-only price comparison by category.
Request:
GET /api/analytics/v2/price/rec-vs-med?category=Flower
Response:
[
{
"category": "Flower",
"rec_avg": 38.50,
"rec_median": 35.00,
"med_avg": 42.00,
"med_median": 40.00
},
{
"category": "Concentrates",
"rec_avg": 45.00,
"rec_median": 42.00,
"med_avg": 48.00,
"med_median": 45.00
}
]
2. Brand Analytics
GET /brand/:name/penetration
Get brand penetration metrics with state breakdown.
Request:
GET /api/analytics/v2/brand/Cookies/penetration?window=30d
Response:
{
"brand_name": "Cookies",
"total_dispensaries": 125,
"total_skus": 450,
"avg_skus_per_dispensary": 3.6,
"states_present": ["AZ", "CA", "CO", "NV", "MI"],
"state_breakdown": [
{
"state_code": "CA",
"state_name": "California",
"legal_type": "recreational",
"dispensary_count": 45,
"sku_count": 180,
"avg_skus_per_dispensary": 4.0,
"market_share_percent": 12.5
},
{
"state_code": "AZ",
"state_name": "Arizona",
"legal_type": "recreational",
"dispensary_count": 32,
"sku_count": 128,
"avg_skus_per_dispensary": 4.0,
"market_share_percent": 15.2
}
],
"penetration_trend": [
{
"date": "2024-11-01",
"dispensary_count": 120,
"new_dispensaries": 0,
"dropped_dispensaries": 0
},
{
"date": "2024-11-08",
"dispensary_count": 123,
"new_dispensaries": 3,
"dropped_dispensaries": 0
},
{
"date": "2024-11-15",
"dispensary_count": 125,
"new_dispensaries": 2,
"dropped_dispensaries": 0
}
]
}
GET /brand/:name/rec-vs-med
Get brand presence in recreational vs medical-only states.
Request:
GET /api/analytics/v2/brand/Cookies/rec-vs-med
Response:
{
"brand_name": "Cookies",
"rec_states_count": 4,
"rec_states": ["AZ", "CA", "CO", "NV"],
"rec_dispensary_count": 110,
"rec_avg_skus": 3.8,
"med_only_states_count": 2,
"med_only_states": ["FL", "OH"],
"med_only_dispensary_count": 15,
"med_only_avg_skus": 2.5
}
3. Category Analytics
GET /category/:name/growth
Get category growth metrics with state breakdown.
Request:
GET /api/analytics/v2/category/Flower/growth?window=30d
Response:
{
"category": "Flower",
"current_sku_count": 5200,
"current_dispensary_count": 320,
"avg_price": 38.50,
"growth_data": [
{
"date": "2024-11-01",
"sku_count": 4800,
"dispensary_count": 310,
"avg_price": 39.00
},
{
"date": "2024-11-15",
"sku_count": 5000,
"dispensary_count": 315,
"avg_price": 38.75
},
{
"date": "2024-12-01",
"sku_count": 5200,
"dispensary_count": 320,
"avg_price": 38.50
}
],
"state_breakdown": [
{
"state_code": "CA",
"state_name": "California",
"legal_type": "recreational",
"sku_count": 2100,
"dispensary_count": 145,
"avg_price": 36.00
},
{
"state_code": "AZ",
"state_name": "Arizona",
"legal_type": "recreational",
"sku_count": 950,
"dispensary_count": 85,
"avg_price": 40.00
}
]
}
GET /category/rec-vs-med
Get category comparison between recreational and medical-only states.
Request:
GET /api/analytics/v2/category/rec-vs-med
Response:
[
{
"category": "Flower",
"recreational": {
"state_count": 15,
"dispensary_count": 650,
"sku_count": 12500,
"avg_price": 35.50,
"median_price": 32.00
},
"medical_only": {
"state_count": 8,
"dispensary_count": 220,
"sku_count": 4200,
"avg_price": 42.00,
"median_price": 40.00
},
"price_diff_percent": -15.48
},
{
"category": "Concentrates",
"recreational": {
"state_count": 15,
"dispensary_count": 600,
"sku_count": 8500,
"avg_price": 42.00,
"median_price": 40.00
},
"medical_only": {
"state_count": 8,
"dispensary_count": 200,
"sku_count": 3100,
"avg_price": 48.00,
"median_price": 45.00
},
"price_diff_percent": -12.50
}
]
4. Store Analytics
GET /store/:id/summary
Get change summary for a store over a time window.
Request:
GET /api/analytics/v2/store/101/summary?window=30d
Response:
{
"dispensary_id": 101,
"dispensary_name": "Green Leaf Dispensary",
"state_code": "AZ",
"window": "30d",
"products_added": 45,
"products_dropped": 12,
"brands_added": ["Alien Labs", "Connected"],
"brands_dropped": ["House Brand"],
"price_changes": 156,
"avg_price_change_percent": 3.2,
"stock_in_events": 89,
"stock_out_events": 34,
"current_product_count": 512,
"current_in_stock_count": 478
}
GET /store/:id/events
Get recent product change events for a store.
Request:
GET /api/analytics/v2/store/101/events?window=7d&limit=50
Response:
[
{
"store_product_id": 12345,
"product_name": "Blue Dream 3.5g",
"brand_name": "Cookies",
"category": "Flower",
"event_type": "price_change",
"event_date": "2024-12-05T14:30:00.000Z",
"old_value": "45.00",
"new_value": "42.00"
},
{
"store_product_id": 12346,
"product_name": "OG Kush 1g",
"brand_name": "Alien Labs",
"category": "Flower",
"event_type": "added",
"event_date": "2024-12-04T10:00:00.000Z",
"old_value": null,
"new_value": null
},
{
"store_product_id": 12300,
"product_name": "Sour Diesel Cart",
"brand_name": "Select",
"category": "Vaporizers",
"event_type": "stock_out",
"event_date": "2024-12-03T16:45:00.000Z",
"old_value": "true",
"new_value": "false"
}
]
5. State Analytics
GET /state/:code/summary
Get market summary for a specific state with rec/med breakdown.
Request:
GET /api/analytics/v2/state/AZ/summary
Response:
{
"state_code": "AZ",
"state_name": "Arizona",
"legal_status": {
"recreational_legal": true,
"rec_year": 2020,
"medical_legal": true,
"med_year": 2010
},
"coverage": {
"dispensary_count": 145,
"product_count": 18500,
"brand_count": 320,
"category_count": 12,
"snapshot_count": 2450000,
"last_crawl_at": "2024-12-06T02:30:00.000Z"
},
"pricing": {
"avg_price": 42.50,
"median_price": 38.00,
"min_price": 5.00,
"max_price": 250.00
},
"top_categories": [
{ "category": "Flower", "count": 5200 },
{ "category": "Concentrates", "count": 3800 },
{ "category": "Vaporizers", "count": 2950 },
{ "category": "Edibles", "count": 2400 },
{ "category": "Pre-Rolls", "count": 1850 }
],
"top_brands": [
{ "brand": "Cookies", "count": 450 },
{ "brand": "Alien Labs", "count": 380 },
{ "brand": "Connected", "count": 320 },
{ "brand": "Stiiizy", "count": 290 },
{ "brand": "Raw Garden", "count": 275 }
]
}
GET /state/legal-breakdown
Get breakdown by legal status (recreational, medical-only, no program).
Request:
GET /api/analytics/v2/state/legal-breakdown
Response:
{
"recreational_states": {
"count": 24,
"dispensary_count": 850,
"product_count": 125000,
"snapshot_count": 15000000,
"states": [
{ "code": "CA", "name": "California", "dispensary_count": 250 },
{ "code": "CO", "name": "Colorado", "dispensary_count": 150 },
{ "code": "AZ", "name": "Arizona", "dispensary_count": 145 },
{ "code": "MI", "name": "Michigan", "dispensary_count": 120 }
]
},
"medical_only_states": {
"count": 18,
"dispensary_count": 320,
"product_count": 45000,
"snapshot_count": 5000000,
"states": [
{ "code": "FL", "name": "Florida", "dispensary_count": 120 },
{ "code": "OH", "name": "Ohio", "dispensary_count": 85 },
{ "code": "PA", "name": "Pennsylvania", "dispensary_count": 75 }
]
},
"no_program_states": {
"count": 9,
"states": [
{ "code": "ID", "name": "Idaho" },
{ "code": "WY", "name": "Wyoming" },
{ "code": "KS", "name": "Kansas" }
]
}
}
GET /state/recreational
Get list of recreational state codes.
Request:
GET /api/analytics/v2/state/recreational
Response:
{
"legal_type": "recreational",
"states": ["AK", "AZ", "CA", "CO", "CT", "DE", "IL", "MA", "MD", "ME", "MI", "MN", "MO", "MT", "NJ", "NM", "NV", "NY", "OH", "OR", "RI", "VA", "VT", "WA"],
"count": 24
}
GET /state/medical-only
Get list of medical-only state codes (not recreational).
Request:
GET /api/analytics/v2/state/medical-only
Response:
{
"legal_type": "medical_only",
"states": ["AR", "FL", "HI", "LA", "MS", "ND", "NH", "OK", "PA", "SD", "UT", "WV"],
"count": 12
}
GET /state/rec-vs-med-pricing
Get rec vs med price comparison by category.
Request:
GET /api/analytics/v2/state/rec-vs-med-pricing?category=Flower
Response:
[
{
"category": "Flower",
"recreational": {
"state_count": 15,
"product_count": 12500,
"avg_price": 35.50,
"median_price": 32.00
},
"medical_only": {
"state_count": 8,
"product_count": 5200,
"avg_price": 42.00,
"median_price": 40.00
},
"price_diff_percent": -15.48
}
]
How These Endpoints Support Portals
Brand Portal Use Cases
- Track brand penetration: Use
/brand/:name/penetrationto see how many stores carry the brand - Compare rec vs med markets: Use
/brand/:name/rec-vs-medto understand footprint by legal status - Identify expansion opportunities: Use
/state/coverage-gapsto find underserved markets - Monitor pricing: Use
/price/brand/:brandto track pricing by state
Buyer Portal Use Cases
- Compare stores: Use
/store/:id/summaryto see activity levels - Track price changes: Use
/store/:id/eventsto monitor competitor pricing - Analyze categories: Use
/category/:name/growthto identify trending products - State-level insights: Use
/state/:code/summaryfor market overview
Time Window Filtering
All time-based endpoints support the window query parameter:
| Value | Description |
|---|---|
7d |
Last 7 days |
30d |
Last 30 days (default) |
90d |
Last 90 days |
The window affects:
store_product_snapshots.captured_atfor historical datastore_products.first_seen_at/last_seen_atfor product lifecyclecrawl_runs.started_atfor crawl-based metrics
Rec/Med Segmentation
All state-level endpoints automatically segment by:
- Recreational:
states.recreational_legal = TRUE - Medical-only:
states.medical_legal = TRUE AND states.recreational_legal = FALSE - No program: Both flags are FALSE or NULL
This segmentation appears in:
legal_typefield in responses- State breakdown arrays
- Price comparison endpoints