{
  "openapi": "3.0.1",
  "info": {
    "title": "California Cannabis Dispensary Data",
    "description": "Official California cannabis license data merged with major platforms (Weedmaps, Leafly and more) into one deduplicated feed with automatic change detection, trend tracking, and self-healing monitoring, the same engine as our multi-state Actor covering 47 US states plus Canada.",
    "version": "0.1",
    "x-build-id": "dqbFDfhiz2g40rzCt"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/registryfeeds~california-cannabis-dispensary-data/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-registryfeeds-california-cannabis-dispensary-data",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/registryfeeds~california-cannabis-dispensary-data/runs": {
      "post": {
        "operationId": "runs-sync-registryfeeds-california-cannabis-dispensary-data",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/registryfeeds~california-cannabis-dispensary-data/run-sync": {
      "post": {
        "operationId": "run-sync-registryfeeds-california-cannabis-dispensary-data",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "testMode": {
            "title": "Test Mode",
            "type": "boolean",
            "description": "When enabled, collects only 3 results per source per state and does NOT update the run snapshot or change detection baseline. Use this to verify the Actor works before running a full job. Costs nearly nothing.",
            "default": false
          },
          "sources": {
            "title": "Platform Sources",
            "type": "array",
            "description": "Which platform sources to scrape. Weedmaps and Leafly are enabled by default and cover all US states with no extra setup. iHeartJane is also enabled by default but blocks non-residential traffic — it silently returns 0 results unless you configure a residential Proxy Configuration below. Dutchie requires the same residential proxy. Potguide and AllBud are included for completeness but currently return 0 results regardless of setup (potguide.com now redirects to an unrelated retailer; allbud.com requires JS rendering with no accessible API) — left selectable in case either comes back online, but not recommended.",
            "items": {
              "type": "string",
              "enum": [
                "weedmaps",
                "leafly",
                "iheartjane",
                "dutchie",
                "potguide",
                "allbud"
              ]
            },
            "default": [
              "weedmaps",
              "leafly",
              "iheartjane"
            ]
          },
          "states": {
            "title": "States / Provinces",
            "type": "array",
            "description": "Locked to California (CA) in this single-state edition: any value submitted here is ignored, every run scopes to CA only. Need other states? See our multi-state Cannabis Dispensary Monitor Actor.",
            "default": [
              "CA"
            ],
            "items": {
              "type": "string"
            }
          },
          "includeStateSources": {
            "title": "Include Official State Databases",
            "type": "boolean",
            "description": "When enabled, also pulls from official state licensing databases. Adds license numbers, license status, issue/expiry dates, and verified addresses the platforms may not have.",
            "default": true
          },
          "maxResultsPerSource": {
            "title": "Max Results Per Source Per State",
            "minimum": 0,
            "type": "integer",
            "description": "Maximum number of dispensaries to collect per source per state. Set to 0 for unlimited. Ignored when Test Mode is enabled. Default kept low (100) so a first run without a proxy stays well inside Apify's automated 5-minute reliability check — raise it for a full production run.",
            "default": 100
          },
          "sourceTimeoutSecs": {
            "title": "Per-Source Timeout (seconds)",
            "minimum": 30,
            "maximum": 1800,
            "type": "integer",
            "description": "Maximum seconds a single source is allowed to run before being cut off. Prevents one broken source from blocking the entire run and blowing up your compute budget. Default: 300 (5 minutes).",
            "default": 300
          },
          "includeMenuData": {
            "title": "Include Menu Stats",
            "type": "boolean",
            "description": "When enabled, scrapes basic menu statistics per dispensary: product counts by category and price ranges. Significantly increases run time and cost.",
            "default": false
          },
          "includeEnforcementData": {
            "title": "Include Enforcement & Compliance Actions",
            "type": "boolean",
            "description": "When enabled and Colorado is in your states list, scrapes Colorado MED's public enforcement/disciplinary action records (citations, fines, suspensions, license denials) into a separate 'enforcement-actions' dataset. Not merged into dispensary records — CO's list only has business names, no license numbers, so it can't be reliably matched by name alone. Currently Colorado only; more states planned.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy Configuration",
            "type": "object",
            "description": "Optional proxy settings. Recommended for large runs to avoid rate limiting. Required for Dutchie (currently blocks direct HTTP).",
            "default": {
              "useApifyProxy": false
            }
          },
          "geocodeMissingCoordinates": {
            "title": "Geocode Missing Coordinates",
            "type": "boolean",
            "description": "When enabled, uses the free OpenStreetMap Nominatim API to fill in lat/lng for dispensaries where state databases did not publish location data. Rate limited to 1 request/second. Adds significant run time for large datasets. Results are cached between runs.",
            "default": false
          },
          "maxGeocodePerRun": {
            "title": "Max Geocode Lookups Per Run",
            "minimum": 0,
            "type": "integer",
            "description": "Ceiling on how many missing-coordinate records to look up via Nominatim in a single run (0 = unlimited). At ~1.1 seconds per lookup, this bounds run time and cost; anything not resolved this run is attempted on the next run. Only applies when Geocode Missing Coordinates is enabled.",
            "default": 2000
          },
          "maxMenuItemsPerDispensary": {
            "title": "Max Menu Items Per Dispensary",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Maximum number of menu products to fetch per dispensary when 'Include Menu Stats' is enabled. Hard cap at 500. Higher values increase run time and cost significantly.",
            "default": 200
          },
          "webhookUrl": {
            "title": "Webhook URL",
            "type": "string",
            "description": "Optional HTTPS endpoint to receive change alerts after each run. When set, the Actor POSTs structured JSON events for new dispensary openings, closures, license suspensions, and expiring licenses. Useful for real-time monitoring without polling."
          },
          "webhookStateFilter": {
            "title": "Webhook State Filter",
            "type": "array",
            "description": "If set, only send webhook events for dispensaries in these states. Leave empty to receive events for all scraped states.",
            "items": {
              "type": "string"
            }
          },
          "licenseExpiryAlertDays": {
            "title": "License Expiry Alert Window (days)",
            "type": "array",
            "description": "Alert thresholds (in days) for license expiry detection. Dispensaries whose license expires within any of these windows are included in the EXPIRING_LICENSES KV store key and webhook events.",
            "default": [
              30,
              60,
              90
            ]
          },
          "suppressInitialStateChanges": {
            "title": "Suppress Initial State Change Events",
            "type": "boolean",
            "description": "When enabled (default), prevents webhook floods when adding a new state to an existing run. Adding CA for the first time would otherwise fire 1,200+ new_dispensary events — with this on, those events are suppressed on the first run and detected normally in subsequent runs.",
            "default": true
          },
          "onlyOutputChanges": {
            "title": "Only Output Changed Dispensaries",
            "type": "boolean",
            "description": "When enabled, the dataset only contains dispensaries that are new or changed since the last run — not the full multi-thousand-record dataset every time. Built for recurring/scheduled monitoring: if you're running this weekly to track openings, closures, and license changes rather than re-extracting the full market each time, this can cut per-run dataset costs dramatically. MARKET_INTELLIGENCE, TREND_HISTORY, and the standby API still reflect the full current dataset regardless — only what's written to the default dataset changes. The very first run (no previous snapshot) always outputs everything, since there's nothing yet to diff against. Ignored in testMode.",
            "default": false
          },
          "enrichSocialMedia": {
            "title": "Enrich Social Media Handles",
            "type": "boolean",
            "description": "When enabled, fetches each dispensary's website homepage and extracts publicly linked social media profiles (Instagram, Facebook, Twitter/X, TikTok, YouTube, LinkedIn). Results populate the socialMedia field. Adds significant run time — capped at 1,000 dispensaries per run. Disabled in testMode.",
            "default": false
          },
          "enrichEmail": {
            "title": "Enrich Contact Email",
            "type": "boolean",
            "description": "When enabled, fetches each dispensary's website homepage and extracts a publicly listed contact email (mailto: links first, then visible page text as a fallback). Results populate the email field for dispensaries whose source didn't already publish one. Adds significant run time — capped at 1,000 dispensaries per run. Disabled in testMode.",
            "default": false
          },
          "cities": {
            "title": "Cities",
            "type": "array",
            "description": "Only output dispensaries in these cities. Case-insensitive, matched against the normalized city on each record. Combine with States to disambiguate repeated city names (there is a Springfield in most states). Leave empty for every city.",
            "items": {
              "type": "string"
            }
          },
          "centerLat": {
            "title": "Center latitude",
            "type": "string",
            "description": "Latitude of the point to measure from. Used only when Radius (miles) is also set. Example: 39.7392 for Denver."
          },
          "centerLon": {
            "title": "Center longitude",
            "type": "string",
            "description": "Longitude of the point to measure from. Used only when Radius (miles) is also set. Example: -104.9903 for Denver."
          },
          "radiusMiles": {
            "title": "Radius (miles)",
            "minimum": 1,
            "type": "integer",
            "description": "Only output dispensaries within this many miles of the center point. Requires both Center latitude and Center longitude. Great-circle distance. Records with no coordinates cannot be measured and are excluded; the run log reports how many, so a coverage gap is never mistaken for a distance result. Turn on Geocode missing coordinates to resolve more of them."
          },
          "services": {
            "title": "Service types",
            "type": "array",
            "description": "Only output dispensaries offering ALL of the selected services. Leave empty for no service filtering.",
            "items": {
              "type": "string",
              "enum": [
                "delivery",
                "pickup",
                "storefront"
              ],
              "enumTitles": [
                "Delivery",
                "Pickup",
                "Storefront"
              ]
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}