# CA Cannabis Licenses - DCC Commercial & Cultivation Register (`j0401/ca-cannabis`) Actor

California cannabis license register (DCC, public data, ~20.9k licenses): active / expired / revoked status, storefront (BCC), cultivation (CCL) & manufacturing (MCSB) licenses. Search by license number, business, county, status, license type.

- **URL**: https://apify.com/j0401/ca-cannabis.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 california cannabis license records

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

## CA Cannabis Licenses - California DCC Commercial, Cultivation & Manufacturing Register

The California Dept. of Cannabis Control (DCC) publishes the **full state cannabis license register** as open data - every license ever issued under the three authorities that merged into DCC in 2021, refreshed **nightly** and served directly from the state API. This actor turns that register into a **charged-per-record, searchable lookup**.

**Built for:** cannabis-industry due diligence - verifying an operator's license status, screening suppliers / business partners / acquisition targets, storefront & delivery footprint research, cultivation-license research by county, and state-level market sizing.

### What it covers

**~20,877 licenses** across all three licensing authorities:

| Authority | Code | Covers | Licenses |
|---|---|---|---|
| Bureau of Cannabis Control | `BCC` | commercial storefronts, delivery, distributors, microbusinesses, testing labs | 6,037 |
| CalCannabis Cultivation | `CCL` | cultivation (outdoor / indoor / mixed-light / nursery / processor...) | 13,282 |
| Manufactured Cannabis Safety Branch | `MCSB` | manufacturers (Types 6, 7, N, P, S), testing labs | 1,558 |

**Status mix (live):** Active **7,600** / Expired 9,010 / Surrendered 2,124 / Canceled 1,589 / Revoked 325 / Limited Operations 183 / Suspended 44. **Active by category:** Cultivation 4,429 / Commercial 2,729 / Manufacturer 442.

Each record is a full license profile: license number, **status + status date**, term (Annual / Provisional / Interim), exact license type + category, designation (Adult-Use / Medicinal / both), issue + expiration dates, business **legal name / DBA / owner(s) / structure**, premise county (street / city / ZIP / coordinates where the state publishes them), and **business email + phone** (~99-100% populated). Every license carries the source's nightly refresh stamp.

> Location privacy is **per authority**: the state withholds street address and coordinates for **cultivation (CCL) and manufacturing (MCSB)** licenses - those rows carry the premise **county only** (still with full business email + phone + owner). **Storefront (BCC)** rows carry street address and coordinates on roughly a third of licenses. Filtering by county works across the whole register; filtering by city / ZIP / coordinates only returns storefront licenses that carry them.

### Typical questions

- "Is this dispensary's license **active** right now?" (license-number lookup, any era / status.)
- "Show the cultivation licenses of every **grower in Humboldt County** - active and history."
- "How many **Active retail storefronts** are there in Los Angeles County?"
- "Find a manufacturer whose license was **revoked** or **surrendered**."
- "What's the **Adult-Use vs. Medicinal** split, by county?"
- "Screen this business name / owner across all three authorities."

### Inputs

| Input | What it does |
|---|---|
| `licenseNumber` | exact license number -> a single license, any era / status (e.g. `C10-0000001-LIC`, `CCL18-0000004`) |
| `keyword` | business-name substring (legal name + DBA + owner) |
| `status` | exact set: Active / Expired / Surrendered / Canceled / Revoked / Limited Operations / Suspended (blank = all) |
| `authority` | CCL / BCC / MCSB |
| `typeCategory` | Cultivation / Commercial / Manufacturer (mirrors authority) |
| `designation` | Adult-Use / Medicinal / Adult-Use and Medicinal |
| `term` | Annual / Provisional / Interim |
| `county` / `city` / `zipCode` | premise location filters (county covers all licenses; city/ZIP only hit storefront rows that publish them) |
| `aggregate` / `groupBy` | one summary row per group with a count - status / authority / typeCategory / designation / term / county |
| `maxResults` | cap records (default 250) |

**Default run = the first 250 licenses in the register** (page-1 order - the storefront block, not sorted by recency; no status default, so precise lookups of Expired / Revoked / older licenses are never shadowed). Fast enough for the daily auto-test. For a full scan, set `maxResults` to 0 or turn on `aggregate`.

### Low cost

**From $0.001 per record** - billed only for the rows you use, with a per-run charge cap so a broad query can't surprise-bill.

The state's license API *looks* filterable but **silently ignores every filter** - status, authority and even the page-size arguments are no-ops, so the only honest way to read it is a full paginated snapshot (84 pages) pulled fresh every run and filtered locally. That snapshot unifies California's **three legacy licensing regimes** (the storefront bureau BCC, the cultivation branch CCL, the manufacturing branch MCSB) into one schema, with one junk status row normalized out and cultivation / manufacturing rows' withheld street addresses handled explicitly. Every pull is integrity-checked against the register's known shape (total count band, Active-count band, per-authority floors), so a degraded source fails loudly instead of returning bad data.

### Source

- [DCC Cannabis License Search](https://cannabis.ca.gov/licensees/license-search/) - official public license search over the same register.
- License data is DCC's own published register; status reflects the state's record as of the nightly refresh.

# Actor input Schema

## `licenseNumber` (type: `string`):

Exact license number - returns a single license (any era / status, e.g. C10-0000001-LIC or CCL18-0000004). Leave blank to search instead.

## `keyword` (type: `string`):

Substring match against business legal name + DBA + owner name. E.g. a dispensary brand, grower, or manufacturer.

## `status` (type: `string`):

Filter to a license status. Leave blank for all statuses. Active ~7.6k of the ~20.9k register. Blank is default so an exact license-number lookup is never shadowed.

## `authority` (type: `string`):

CCL = cultivation (13,282), BCC = commercial storefront/delivery (6,037), MCSB = manufacturing/testing (1,558). Mirrored by the typeCategory filter.

## `typeCategory` (type: `string`):

Cultivation / Commercial / Manufacturer - mirrors authority (BCC=Commercial, CCL=Cultivation, MCSB=Manufacturer).

## `designation` (type: `string`):

Adult-Use / Medicinal / both. Blank = all.

## `term` (type: `string`):

Annual / Provisional / Interim. Blank = all.

## `county` (type: `string`):

Premise county (substring), e.g. Humboldt or Los Angeles. County is the one location field published for every license (cultivation / manufacturing withhold street + coordinates). Blank = all counties.

## `city` (type: `string`):

Premise city (substring). Blank = all cities.

## `zipCode` (type: `string`):

Premise ZIP (prefix match). Blank = all.

## `aggregate` (type: `boolean`):

When on, returns one summary row per group with a count (full register, client-side). Uses the filters above.

## `groupBy` (type: `string`):

Dimension to group by when aggregate is on: status / authority / typeCategory / designation / term / county.

## `maxResults` (type: `integer`):

Cap records pushed (0 = no cap). Default = the first 250 licenses in the register (page-1 order - the storefront block, not sorted by recency). To scan or aggregate the full register, use aggregate or raise this.

## Actor input object example

```json
{
  "aggregate": false,
  "groupBy": "status",
  "maxResults": 250
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

California cannabis license records - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/ca-cannabis").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/ca-cannabis").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 '{}' |
apify call j0401/ca-cannabis --silent --output-dataset

```

## MCP server setup

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

```

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/7hfF7SjVF8czGfh0b/builds/6ZlrlnI0Cw7a2I5fC/openapi.json
