# Cannabis Dispensary Monitor/Scraper (`ahmed_jasarevic/cannabis-scraper`) Actor

Cannabis dispensary contact data: business name, address, phone, email, Instagram, website, license fields, product categories and public review counts. For marketing agencies, ownership groups, researchers and compliance consultants.

- **URL**: https://apify.com/ahmed\_jasarevic/cannabis-scraper.md
- **Developed by:** [Ahmed Jasarevic](https://apify.com/ahmed_jasarevic) (community)
- **Categories:** Lead generation, Agents, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### What does Cannabis Scraper do?

**Cannabis Scraper** extracts structured **cannabis dispensary contact data** from public dispensary directories (currently AllBud.com). For every dispensary it produces a clean record with business name, full address, phone number, publicly displayed review counts and ratings, dispensary type (recreational / medical / hybrid), geo-coordinates and amenities - plus email, Instagram handle and website URL whenever they are publicly displayed on the source page.

The actor runs on Apify's infrastructure with **proxy support enabled by default**, so your own IP is never exposed. Results are available as JSON, CSV, Excel or via API.

### Why use Cannabis Scraper?

Built for the cannabis industry's B2B data consumers:

- **Cannabis marketing agencies** - build prospect lists of dispensaries by city/state with phone numbers and public review footprints.
- **Dispensary ownership groups** - monitor competitors' public ratings, review volume and market presence across cities.
- **Market researchers** - export consistent, machine-readable dispensary datasets for analysis.
- **Regulatory compliance consultants** - map the retail landscape; license-registry fields are reserved in the output schema for upcoming state-registry adapters.

### How to use Cannabis Scraper

1. Open the actor and go to the **Input** tab.
2. Paste one or more AllBud **listing URLs** into *Start URLs* - city level (`https://www.allbud.com/dispensaries/colorado/denver`) or state level (`https://www.allbud.com/dispensaries/massachusetts`). A single dispensary page URL also works.
3. Set *Max dispensaries* to control run size (free preview is capped at 10 records).
4. Click **Start**. Records stream into the dataset as they are extracted.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array (required) | Denver listing | AllBud listing or dispensary URLs |
| `maxItems` | integer | `100` | Max dispensary records (paid); hard ceiling 1000 |
| `maxRequestsPerCrawl` | integer | `300` | Hard safety cap on total HTTP requests (max 1000) |
| `maxConcurrency` | integer | `3` | Parallel requests (max 10) |
| `proxyConfiguration` | object | Apify proxy ON | Proxy groups; enabled by default |

### Output

Example dataset item:

```json
{
    "businessName": "Ballpark Holistic Dispensary",
    "dispensaryType": "Recreational",
    "productCategories": ["Recreational"],
    "phone": "303-590-9881",
    "email": null,
    "instagramHandle": null,
    "websiteUrl": null,
    "address": {
        "street": "2119 Larimer St.",
        "city": "Denver",
        "state": "CO",
        "postalCode": "80205",
        "country": "United States"
    },
    "latitude": 39.75438,
    "longitude": -104.9912187,
    "licenseNumber": null,
    "licenseIssuanceDate": null,
    "licenseType": null,
    "reviewCount": 2,
    "voteCount": 10,
    "ratingValue": 4.7,
    "amenities": ["ADA accessible", "Accepts debit cards"],
    "sourceUrl": "https://www.allbud.com/dispensaries/colorado/denver/ballpark-holistic-dispensary",
    "scrapedAt": "2026-08-23T09:30:00.000Z"
}
```

You can download the dataset in JSON, HTML, CSV or Excel.

### Data table

| Field | Description |
|---|---|
| `businessName` | Dispensary business name |
| `dispensaryType` | Recreational / Medical / Hybrid / Unknown |
| `productCategories` | Categories derivable from the source (type-based) |
| `phone` | Publicly listed phone number |
| `email` | Publicly displayed email (null when not shown) |
| `instagramHandle` | Instagram handle linked from the source page |
| `websiteUrl` | Official website URL when linked from the source page |
| `address` | Street, city, state, ZIP, country |
| `latitude` / `longitude` | Geo-coordinates from structured data |
| `licenseNumber`, `licenseIssuanceDate`, `licenseType` | State registry fields - reserved for registry adapters (null in this version) |
| `reviewCount` | Publicly displayed written review count |
| `voteCount` | Publicly displayed rating vote count |
| `ratingValue` | Average rating |
| `amenities` | e.g. ATM, ADA accessible, accepts debit cards |
| `sourceUrl` / `scrapedAt` | Provenance fields |

### Pricing / Cost estimation

This is a lightweight HTTP-only actor (no browser), so runs are cheap: roughly 1 request per dispensary plus a handful of listing pages. A 100-dispensary run typically finishes well under $0.05 of compute on standard plans. **Free-tier users get a capped preview of 10 records per run**; paying users can extract up to `maxItems` (ceiling 1000). Apify proxy usage is included in paid plans.

### Tips and Advanced options

- **City-level URLs give cleaner coverage** than state-level pages (state pages paginate through mixed areas).
- Keep `maxConcurrency` at 3-5 to stay gentle on the source site.
- Use `maxRequestsPerCrawl` as your budget guardrail - the crawl stops immediately when either cap is hit.
- For emails, websites and richer menus, pair this actor's output (name + city + state) with a Google Maps scraper enrichment run.

### FAQ, disclaimers and support

- **Where are license issuance dates?** State cannabis registries publish them, but not on the directory scraped here. The output schema reserves `licenseNumber`, `licenseIssuanceDate` and `licenseType` for the planned state-registry adapters; they are `null` in this version.
- **Why are some contact fields null?** Email, Instagram and website are only filled when the source page publicly displays them. Many AllBud listings don't include them.
- **Is scraping legal?** This actor only accesses publicly available pages and respects rate limits. Do not use the data in violation of any applicable law or the source site's terms.
- Found a bug or need a custom field? Open an issue on the actor's **Issues** tab.

# Actor input Schema

## `startUrls` (type: `array`):

AllBud dispensary listing URLs (state or city level, e.g. https://www.allbud.com/dispensaries/colorado/denver) or individual dispensary page URLs. The actor collects every dispensary listed on those pages.

## `maxItems` (type: `integer`):

Maximum number of dispensary records to extract. Free-tier users are capped at 10 regardless of this value.

## `maxRequestsPerCrawl` (type: `integer`):

Hard safety cap on total HTTP requests. Protects your compute budget from runaway runs.

## `maxConcurrency` (type: `integer`):

Maximum number of parallel requests. Keep low to stay gentle on the target site.

## `proxyConfiguration` (type: `object`):

Apify proxy settings. Enabled by default so requests never come from your own IP.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.allbud.com/dispensaries/colorado/denver"
    }
  ],
  "maxItems": 100,
  "maxRequestsPerCrawl": 300,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "BUYPROXIES94952"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://www.allbud.com/dispensaries/colorado/denver"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ahmed_jasarevic/cannabis-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": [{ "url": "https://www.allbud.com/dispensaries/colorado/denver" }] }

# Run the Actor and wait for it to finish
run = client.actor("ahmed_jasarevic/cannabis-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.allbud.com/dispensaries/colorado/denver"
    }
  ]
}' |
apify call ahmed_jasarevic/cannabis-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ahmed_jasarevic/cannabis-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/DQdXcQVt6Hiy4d6Hx/builds/XgUWHPZGsvNxKhb0H/openapi.json
