{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Maps Leads Scraper – Emails & AI Scoring",
    "description": "Turn Google Maps into qualified leads. Businesses with phone, website, hours and rating for $3.40 per 1,000. Add emails and socials from each business's own website (mail-server checked, billed only when found), full listing details and AI lead scoring. Export to CSV or CRM.",
    "version": "0.4",
    "x-build-id": "XNcDAfaCKgQlQ4UJz"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/computerdoit~google-maps-leads-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-computerdoit-google-maps-leads-scraper",
        "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/computerdoit~google-maps-leads-scraper/runs": {
      "post": {
        "operationId": "runs-sync-computerdoit-google-maps-leads-scraper",
        "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/computerdoit~google-maps-leads-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-computerdoit-google-maps-leads-scraper",
        "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": {
          "search_query": {
            "title": "Search query",
            "type": "string",
            "description": "Business type or search phrase to send to Google Maps, for example cafe or bicycle repair shop. Required for an ordinary search; omit both this and location when using targetJobs."
          },
          "location": {
            "title": "Location",
            "type": "string",
            "description": "Required for an ordinary search, combined with search_query. Omit both when using targetJobs. It biases results but is not a strict boundary or an automatically geocoded area. With an explicit area, it is descriptive metadata and may be used as a fallback city value."
          },
          "max_results": {
            "title": "Maximum business rows",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Maximum accepted business rows, not a promised count. Console prefill: 5. API omission default: 50. Filters, Google restrictions and work budgets can stop collection earlier. This is a row cap, not an Apify currency spending limit.",
            "default": 50
          },
          "featurePacks": {
            "title": "Add data (optional packs)",
            "maxItems": 64,
            "type": "array",
            "description": "Optional packs that run with the scrape. Website emails & socials: $0.002 per business or company website with contacts found. Full listing details: $0.001 per business with details found. Photo links: free (photo URLs and categories, no image downloads; included in Full listing details). AI lead scoring: $0.008 per business scored, plus $0.004 per website whose owners or team are named (needs Website emails & socials). Packs marked coming soon are previews only and are rejected outside preview mode. Selections never silently enable other paid dependencies. Duplicate selections are normalized in catalog order.",
            "items": {
              "type": "string",
              "enum": [
                "advancedDiscovery",
                "extendedMaps",
                "reviews",
                "media",
                "websiteContacts",
                "peopleCompany",
                "contactVerification",
                "richSocialProfiles",
                "aiProfiles"
              ],
              "enumTitles": [
                "Advanced discovery (coming soon)",
                "Full listing details",
                "Review mining (coming soon)",
                "Photo links",
                "Website emails & socials",
                "Decision-maker data (coming soon)",
                "Email verification (coming soon)",
                "Social profile enrichment (coming soon)",
                "AI lead scoring"
              ]
            },
            "default": []
          },
          "idealCustomer": {
            "title": "Ideal customer (AI lead scoring)",
            "maxLength": 1500,
            "type": "string",
            "description": "Used by AI lead scoring: describe who you want to sell to, for example \"Independent dental clinics with 2+ dentists that accept new patients\". Each business gets a 0-100 fit score with reasons. Leave empty for AI profiles without scores."
          },
          "findDecisionMakers": {
            "title": "Find owners & team (AI lead scoring)",
            "type": "boolean",
            "description": "With AI lead scoring, list the owners, managers and staff the business's own website names, with their roles. $0.004 per website where people are found; nobody is invented.",
            "default": true
          },
          "minFitScore": {
            "title": "Only deliver leads scoring at least (AI lead scoring)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "Optional, needs an Ideal customer brief. The run checks up to Max results businesses and delivers only those whose 0-100 fit score is at least this number. Businesses below it go to the Screened table with their score and reasons, charged $0.008 AI scoring each with no business fee. Businesses that could not be scored are not delivered and not charged. Not available with Your own list."
          },
          "language": {
            "title": "Language",
            "type": "string",
            "description": "Google Maps interface language code. English (en) is the verified parser language. Other codes are passed to Maps, but field extraction may differ.",
            "default": "en"
          },
          "search_mode": {
            "title": "Search mode",
            "enum": [
              "single",
              "geographic"
            ],
            "type": "string",
            "description": "An ordinary single search uses one root cell. Explicit targetJobs require single mode but may contain multiple ordered text searches, one cell each. Geographic requires explicit bounds or a center/radius and may subdivide cells based on a saturation heuristic; it cannot be combined with targetJobs. An explicit area applies pin-based containment. None guarantees complete coverage.",
            "default": "single"
          },
          "targetJobs": {
            "title": "Ordered Maps target jobs (alternative input)",
            "minItems": 1,
            "maxItems": 1000,
            "type": "array",
            "description": "Alternative to top-level search_query and location: provide 1–1000 ordered search or direct jobs. A search entry needs its own query and location and may set a per-search accepted-row cap within max_results. Direct URL, Place ID and CID entries omit query/location; URL entries require a supported HTTPS Google Maps detail path. A direct URL with a Place ID plus a FID or CID is rejected even when syntactically compatible, until source-corroborated provenance is implemented. Shortlinks, search URLs and conflicting assertions are not resolved. All jobs share one browser pool, identity owner and global run/candidate/result budgets. An intentional per-search quota can advance to the next explicit job; source restrictions, stalls and global budget stops prevent later jobs. Each effective detail URL must retain the claimed FID, Place ID or CID, and decoded source identity must independently contain every asserted ID; a CID-only URL cannot confirm a FID. Any conflicting workload identity fails the run instead of publishing a silently retracted result. Direct browser work is blocked until outgoing redirect protection is proven. Direct jobs require explicit http-first transport and core profile, with browser fallback blocked on unavailable material. Search-only jobs retain browser support. Requires enableCache:false and single search mode without an explicit area. Place ID/CID locator formats and all source coverage require separate real-source proof; an offline test does not establish a successful listing or comprehensive results.",
            "items": {
              "type": "object",
              "required": [
                "kind"
              ],
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "title": "Job kind",
                  "type": "string",
                  "description": "search, supported detail URL, canonical Place ID or string CID.",
                  "enum": [
                    "search",
                    "url",
                    "placeId",
                    "cid"
                  ]
                },
                "search_query": {
                  "title": "Search query",
                  "type": "string",
                  "description": "Required only for kind search; up to 500 characters.",
                  "minLength": 1,
                  "maxLength": 500
                },
                "location": {
                  "title": "Search location",
                  "type": "string",
                  "description": "Required only for kind search; not a strict geographic boundary.",
                  "minLength": 1,
                  "maxLength": 500
                },
                "max_results": {
                  "title": "Maximum accepted rows from this search",
                  "type": "integer",
                  "description": "Optional only for kind search; capped by the original and effective global max_results. Candidate discovery is separately bounded and can inspect more than this row cap.",
                  "minimum": 1,
                  "maximum": 1000
                },
                "url": {
                  "title": "Google Maps detail URL",
                  "type": "string",
                  "description": "Required only for kind url. Strict HTTPS Google Maps detail URLs only; no shortlink, search URL, credentials, custom port or fragment. Mixed Place ID with FID/CID claims are rejected before navigation.",
                  "minLength": 1,
                  "maxLength": 4096
                },
                "placeId": {
                  "title": "Canonical Place ID",
                  "type": "string",
                  "description": "Required only for kind placeId; case-sensitive, not a Maps FID.",
                  "minLength": 8,
                  "maxLength": 200,
                  "pattern": "^[A-Za-z0-9_-]+$"
                },
                "cid": {
                  "title": "Decimal or hexadecimal CID string",
                  "type": "string",
                  "description": "Required only for kind cid; never a JavaScript number, including for zero.",
                  "minLength": 1,
                  "maxLength": 64,
                  "pattern": "^(0[xX][0-9a-fA-F]+|[0-9]+)$"
                }
              }
            }
          },
          "records": {
            "title": "Your own list (instead of a search)",
            "minItems": 1,
            "maxItems": 1000,
            "type": "array",
            "description": "Enrich a list you already have instead of a Google Maps search: 1–1000 businesses, each with a name and website (plus optional id, phone, address and category). To use it, clear Search query and Location. Needs Website emails & socials; add AI lead scoring for fit scores and owners. Records go to their own Enriched records dataset and carry no $0.0034 business fee: you pay only the enrichment events they earn. Maximum business rows still caps how many records are enriched, so set it to at least your list size.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "title": "Your ID",
                  "description": "Your own record ID, returned as input_id (text or a whole number, up to 128 characters)."
                },
                "name": {
                  "type": "string",
                  "title": "Business name",
                  "description": "The business name, as in your list."
                },
                "website": {
                  "type": "string",
                  "title": "Website",
                  "description": "The business's own public website, for example https://example.com/."
                },
                "phone": {
                  "type": "string",
                  "title": "Phone",
                  "description": "Optional. Helps match the website's phone numbers to this business."
                },
                "address": {
                  "type": "string",
                  "title": "Address",
                  "description": "Optional. Given to AI lead scoring as context."
                },
                "category": {
                  "type": "string",
                  "title": "Category",
                  "description": "Optional, for example Dentist. Given to AI lead scoring as context."
                }
              },
              "required": [
                "name",
                "website"
              ]
            }
          },
          "detailProfile": {
            "title": "Detail field profile",
            "enum": [
              "full",
              "core"
            ],
            "type": "string",
            "description": "Default: core, read from the search feed; a run that asks for email (has_email or an email selection) uses full in the browser. Console starter: core. Both attempt IDs, business pins, names, categories, addresses, phones, website, aggregate rating/count, description, plus code, hours and status. Full additionally attempts email, social_links, booking_url, menu_url and service_options from supported Maps controls. Core excludes those five fields. Missing optional values are not verified absences. Neither profile performs external website enrichment or contact verification, or provides individual reviews, photos or popular times."
          },
          "detailTransport": {
            "title": "Detail transport",
            "enum": [
              "browser",
              "http-first",
              "feed"
            ],
            "type": "string",
            "description": "Default: search feed (core), or browser when the run asks for email or sets full. Search feed requires core: each business the search results feed already lists is read from that feed without opening its page, several times faster and cheaper, and any business the feed cannot supply opens its page as usual. Its category is the category Google shows for the search, which can differ from the business's primary category. Experimental http-first requires explicit core and uses the browser context's shared-cookie HTTP client for bounded structured details, with permitted browser fallback. Discovery still uses a browser. Available trial evidence showed fallback only, not a demonstrated HTTP-only benefit. Blocking or unsafe redirects stop the run. Its 4 MiB limit bounds parsing/retention, not Playwright download buffering."
          },
          "search_bounds": {
            "title": "Search bounds",
            "required": [
              "south",
              "west",
              "north",
              "east"
            ],
            "type": "object",
            "description": "Explicit rectangle. Supply this or search_center plus radius_km, never both. Unknown or outside business pins are excluded. Dateline-crossing and polar areas are unsupported. Leave unset for the text-location starter.",
            "properties": {
              "south": {
                "title": "South latitude",
                "type": "number",
                "description": "Southern edge of the search rectangle in decimal degrees.",
                "minimum": -85,
                "maximum": 85
              },
              "west": {
                "title": "West longitude",
                "type": "number",
                "description": "Western edge of the search rectangle in decimal degrees.",
                "minimum": -180,
                "maximum": 180
              },
              "north": {
                "title": "North latitude",
                "type": "number",
                "description": "Northern edge of the search rectangle in decimal degrees.",
                "minimum": -85,
                "maximum": 85
              },
              "east": {
                "title": "East longitude",
                "type": "number",
                "description": "Eastern edge of the search rectangle in decimal degrees.",
                "minimum": -180,
                "maximum": 180
              }
            },
            "additionalProperties": false
          },
          "search_center": {
            "title": "Search center",
            "required": [
              "latitude",
              "longitude"
            ],
            "type": "object",
            "description": "Circle center in decimal degrees. Requires radius_km. Do not also supply search_bounds. A text location does not supply this center automatically.",
            "properties": {
              "latitude": {
                "title": "Latitude",
                "type": "number",
                "description": "Latitude of the circle center in decimal degrees.",
                "minimum": -85,
                "maximum": 85
              },
              "longitude": {
                "title": "Longitude",
                "type": "number",
                "description": "Longitude of the circle center in decimal degrees.",
                "minimum": -180,
                "maximum": 180
              }
            },
            "additionalProperties": false
          },
          "radius_km": {
            "title": "Radius (km)",
            "minimum": 0.01,
            "maximum": 500,
            "type": "number",
            "description": "Radius for an explicitly supplied search_center. A text location alone does not define a geographic boundary. Leave unset unless you supply a center."
          },
          "geographicOptions": {
            "title": "Per-cell discovery and subdivision",
            "type": "object",
            "description": "resultLimit controls URLs collected per cell in both modes. The starter sets it to 10. Other options control geographic subdivision. A collection quota below saturationThreshold marks truncation, not evidence of a Maps cap. Increasing these values does not guarantee more or complete results.",
            "properties": {
              "maxDepth": {
                "title": "Maximum depth (root is zero)",
                "type": "integer",
                "description": "Maximum subdivision depth, counting the root search cell as zero.",
                "minimum": 0,
                "maximum": 6
              },
              "minCellSizeKm": {
                "title": "Minimum child side (km)",
                "type": "number",
                "description": "Minimum permitted side length of a child search cell in kilometers.",
                "minimum": 0.01,
                "maximum": 50
              },
              "resultLimit": {
                "title": "Maximum URLs per cell",
                "type": "integer",
                "description": "Maximum candidate URLs collected from each search cell before global deduplication and filtering.",
                "minimum": 1,
                "maximum": 1000
              },
              "saturationThreshold": {
                "title": "Suspected saturation count",
                "type": "integer",
                "description": "Per-cell identity count that suggests saturation and permits subdivision within the remaining limits.",
                "minimum": 1,
                "maximum": 1000
              }
            },
            "default": {
              "maxDepth": 3,
              "minCellSizeKm": 0.5,
              "resultLimit": 120,
              "saturationThreshold": 120
            },
            "additionalProperties": false
          },
          "min_rating": {
            "title": "Minimum rating",
            "minimum": 1,
            "maximum": 5,
            "type": "number",
            "description": "Filter by the extracted aggregate Maps rating. Leave unset for the starter. This is not an independent quality or accuracy check."
          },
          "min_reviews": {
            "title": "Minimum review count",
            "minimum": 0,
            "type": "integer",
            "description": "Filter by the extracted aggregate review count. Leave unset for the starter. Individual review text is not collected."
          },
          "has_website": {
            "title": "Require a website",
            "type": "boolean",
            "description": "Keep only rows with an extracted website URL. Does not crawl or verify that website. False leaves this filter off.",
            "default": false
          },
          "has_phone": {
            "title": "Require a phone number",
            "type": "boolean",
            "description": "Keep only rows with an extracted phone number. Does not verify ownership, reachability or carrier. False leaves this filter off.",
            "default": false
          },
          "has_email": {
            "title": "Require an email on the Google listing (rare)",
            "type": "boolean",
            "description": "Google rarely shows an email on a listing, so this filter returns few businesses. For emails, add the Website emails & socials pack, which reads each business's own website. Full can collect email from supported dedicated Maps controls, without external website enrichment or email verification. Core does not attempt email and rejects an active has_email filter before launch. False leaves this filter off.",
            "default": false
          },
          "maxRunTimeSecs": {
            "title": "Maximum scraper work time",
            "minimum": 1,
            "maximum": 3600,
            "type": "integer",
            "description": "Absolute scraper work budget, followed by bounded cleanup. Console starter: 60 seconds. API omission default: 600 seconds. This is not the Apify platform timeout or a billing cap. Leave separate platform time for startup, cleanup and publication. Deadline-limited output is marked truncated.",
            "default": 600
          },
          "maxSearchCells": {
            "title": "Maximum search cells",
            "minimum": 1,
            "maximum": 256,
            "type": "integer",
            "description": "Maximum logical search cells across the invocation, including each explicit targetJobs text search and an ordinary root. Direct targets do not consume a search cell. Starter: 1. Hitting this budget marks incomplete exploration, not complete geographic coverage.",
            "default": 21
          },
          "maxSearchAttempts": {
            "title": "Maximum search attempts",
            "minimum": 1,
            "maximum": 1024,
            "type": "integer",
            "description": "Maximum search-page navigation attempts, including retries. Starter: 1.",
            "default": 32
          },
          "maxNavigationAttempts": {
            "title": "Maximum navigation attempts",
            "minimum": 1,
            "maximum": 10000,
            "type": "integer",
            "description": "Shared cap across searches, HTTP detail dispatches and manually followed redirects, browser detail navigations, consent submissions and retries. Starter: 12. This does not count page subresource requests.",
            "default": 256
          },
          "maxCandidateUrls": {
            "title": "Maximum candidate identities",
            "minimum": 1,
            "maximum": 10000,
            "type": "integer",
            "description": "Maximum distinct candidates admitted for details before final qualification. Starter: 10. Known outside-area pins do not consume this budget. A candidate is not a delivered business row.",
            "default": 500
          },
          "maxConcurrentSearches": {
            "title": "Concurrent searches",
            "minimum": 1,
            "maximum": 16,
            "type": "integer",
            "description": "Maximum concurrent searches. Starter and API default: 1. Global run and navigation budgets still apply.",
            "default": 1
          },
          "maxConcurrentExtractions": {
            "title": "Concurrent extractions",
            "minimum": 1,
            "maximum": 64,
            "type": "integer",
            "description": "Maximum simultaneous business-detail extractions. Console starter: 1. API omission default: 3.",
            "default": 3
          },
          "baseDelayMs": {
            "title": "Base pacing delay (ms)",
            "minimum": 0,
            "type": "integer",
            "description": "Base pacing delay between actions. Starter and API default: 500 ms. Keep direct runs small and stop on blocking rather than retrying for a larger count.",
            "default": 500
          },
          "enableCache": {
            "title": "Enable result cache",
            "type": "boolean",
            "description": "Reuse option-aware local cached results. Leave off for a fresh run. Explicit-area searches do not reuse cached coverage. targetJobs, evidence capture, active Maps selection and exclusions require this setting off.",
            "default": false
          },
          "categories": {
            "title": "Categories (not applied)",
            "type": "array",
            "description": "Compatibility input only. Category filtering is not currently applied by this Actor. Leave unset and use the search query to express the business type.",
            "items": {
              "type": "string"
            }
          },
          "exclude_chains": {
            "title": "Exclude chains (not applied)",
            "type": "boolean",
            "description": "Compatibility input only. Chain exclusion is not currently applied by this Actor, even when true.",
            "default": false
          },
          "skipWebsiteEnrichment": {
            "title": "Skip website enrichment (compatibility)",
            "type": "boolean",
            "description": "Compatibility setting. This Actor does not perform external website email/social enrichment, even when false.",
            "default": true
          },
          "skipReviewsDistribution": {
            "title": "Skip review distribution (compatibility)",
            "type": "boolean",
            "description": "Compatibility setting. Per-star review breakdown is not implemented, even when false. Both profiles retain aggregate rating and review count.",
            "default": true
          },
          "skipAttributes": {
            "title": "Skip attributes (compatibility)",
            "type": "boolean",
            "description": "Compatibility setting. General amenities/accessibility attributes are not implemented, even when false. Full still attempts dedicated Maps service_options controls. Core does not.",
            "default": true
          },
          "mapsSelection": {
            "title": "Evidence-aware Maps selection (experimental)",
            "type": "object",
            "description": "Optional AND-combined current-detail criteria, with OR within each exact category list. Requires enableCache:false. Zero and false are active. Contact scope defaults to maps-observation, not universal business absence. All contact absence is blocked until explicit source proof exists. Website-observation and core email are also blocked. previewFeatures:true reports these blockers without scraping. Empty objects/lists and any predicates are no-ops. Do not overlap legacy numeric filters or active compatibility flags. Maximum 8192 normalized UTF-8 JSON bytes; each raw category list is limited to 4096 JSON bytes before deduplication. No new price, advanced pack or source coverage is enabled.",
            "properties": {
              "contacts": {
                "title": "Contact predicates",
                "type": "object",
                "description": "All active contact requirements must match their declared producer scope. Legacy has_*:false remains filter off.",
                "additionalProperties": false,
                "properties": {
                  "website": {
                    "title": "Website",
                    "type": "object",
                    "description": "A selected Maps website value can establish presence. Absence remains blocked.",
                    "additionalProperties": false,
                    "required": [
                      "predicate"
                    ],
                    "properties": {
                      "predicate": {
                        "title": "Predicate",
                        "type": "string",
                        "description": "any is a no-op; present requires a validated value; absent is a preview-only source-proof gate.",
                        "enum": [
                          "any",
                          "present",
                          "absent"
                        ]
                      },
                      "scope": {
                        "title": "Observation scope",
                        "type": "string",
                        "description": "Omitted means maps-observation. website-observation depends on unavailable websiteContacts work.",
                        "enum": [
                          "maps-observation",
                          "website-observation"
                        ]
                      }
                    }
                  },
                  "phone": {
                    "title": "Phone",
                    "type": "object",
                    "description": "A dedicated Maps phone control or structured phone value can establish presence. Absence remains blocked.",
                    "additionalProperties": false,
                    "required": [
                      "predicate"
                    ],
                    "properties": {
                      "predicate": {
                        "title": "Predicate",
                        "type": "string",
                        "description": "any is a no-op; present requires a validated value; absent is a preview-only source-proof gate.",
                        "enum": [
                          "any",
                          "present",
                          "absent"
                        ]
                      },
                      "scope": {
                        "title": "Observation scope",
                        "type": "string",
                        "description": "Omitted means maps-observation. website-observation depends on unavailable websiteContacts work.",
                        "enum": [
                          "maps-observation",
                          "website-observation"
                        ]
                      }
                    }
                  },
                  "email": {
                    "title": "Email",
                    "type": "object",
                    "description": "Presence requires a dedicated email/mailto control in full profile. Core email and all absence predicates remain blocked.",
                    "additionalProperties": false,
                    "required": [
                      "predicate"
                    ],
                    "properties": {
                      "predicate": {
                        "title": "Predicate",
                        "type": "string",
                        "description": "any is a no-op; present requires a validated value; absent is a preview-only source-proof gate.",
                        "enum": [
                          "any",
                          "present",
                          "absent"
                        ]
                      },
                      "scope": {
                        "title": "Observation scope",
                        "type": "string",
                        "description": "Omitted means maps-observation. website-observation depends on unavailable websiteContacts work.",
                        "enum": [
                          "maps-observation",
                          "website-observation"
                        ]
                      }
                    }
                  }
                }
              },
              "rating": {
                "title": "Detail rating range",
                "type": "object",
                "description": "Inclusive detail-only bounds. Zero is active. Requires min <= max and cannot overlap min_rating.",
                "additionalProperties": false,
                "properties": {
                  "min": {
                    "title": "Minimum",
                    "type": "number",
                    "description": "Minimum observed detail rating.",
                    "minimum": 0,
                    "maximum": 5
                  },
                  "max": {
                    "title": "Maximum",
                    "type": "number",
                    "description": "Maximum observed detail rating.",
                    "minimum": 0,
                    "maximum": 5
                  }
                }
              },
              "reviewCount": {
                "title": "Detail review-count range",
                "type": "object",
                "description": "Inclusive safe-integer bounds. Zero is active. Requires min <= max and cannot overlap min_reviews.",
                "additionalProperties": false,
                "properties": {
                  "min": {
                    "title": "Minimum",
                    "type": "integer",
                    "description": "Minimum observed detail count.",
                    "minimum": 0,
                    "maximum": 9007199254740991
                  },
                  "max": {
                    "title": "Maximum",
                    "type": "integer",
                    "description": "Maximum observed detail count.",
                    "minimum": 0,
                    "maximum": 9007199254740991
                  }
                }
              },
              "categories": {
                "title": "Detail category labels",
                "type": "object",
                "description": "Exact whitespace-normalized lowercase labels, OR within each list and AND between lists. Secondary classification requires the structured category list, not the DOM aggregate.",
                "additionalProperties": false,
                "properties": {
                  "primary": {
                    "title": "Primary categories",
                    "type": "array",
                    "description": "At most 32 raw entries and 4096 UTF-8 JSON bytes before deduplication. Empty disables this clause.",
                    "maxItems": 32,
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 120
                    }
                  },
                  "secondary": {
                    "title": "Secondary categories",
                    "type": "array",
                    "description": "At most 32 raw entries and 4096 UTF-8 JSON bytes before deduplication. Empty disables this clause.",
                    "maxItems": 32,
                    "items": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 120
                    }
                  }
                }
              },
              "openNow": {
                "title": "Observed open state",
                "type": "boolean",
                "description": "true requires observed open, false requires observed closed (including source-declared closures). Omit to disable. Closed now does not establish temporary or permanent closure."
              },
              "closure": {
                "title": "Observed closure",
                "type": "string",
                "description": "Requires this explicit closure status from one coherent source. Cannot combine with openNow:true. Omit to disable.",
                "enum": [
                  "temporary",
                  "permanent"
                ]
              }
            },
            "additionalProperties": false
          },
          "mapsExclusions": {
            "title": "Identity-first Maps exclusions (experimental)",
            "type": "object",
            "description": "Optional exclusion keys. Requires enableCache:false. Omitted/empty controls are no-ops. At most 1000 raw entries combined and 262144 UTF-8 JSON bytes before deduplication. Place IDs remain case-sensitive; CIDs must be strings, including zero. Supplied keys are not business bridges. Identity reconciliation runs before selection and may retract earlier rows without refunding work. Contact matching and person scope are blocked, not silently ignored. Short and search URLs are rejected; no resolver is run. Prior business datasets require complete bounded reads owned by the same current run user, at most 4 datasets/1000 rows/33 requests within one 30,000 ms deadline captured before enqueue. Metadata checks are not a transaction. Targets are prepared lazily after identity ingestion; one shared logical source buffer retains actual readers through cleanup. These are not heap/RSS guarantees. Preview reports counts and gates without reading datasets. No new price, provider activation or source-coverage claim.",
            "properties": {
              "placeIds": {
                "title": "Place IDs to exclude",
                "type": "array",
                "description": "Exact case-sensitive canonical Place IDs, not FIDs or opaque card keys.",
                "maxItems": 1000,
                "items": {
                  "type": "string",
                  "minLength": 8,
                  "maxLength": 200,
                  "pattern": "^[A-Za-z0-9_-]+$"
                }
              },
              "cids": {
                "title": "CIDs to exclude",
                "type": "array",
                "description": "Decimal or hexadecimal unsigned 64-bit CID strings. Numbers are rejected because precision may already be lost.",
                "maxItems": 1000,
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 64,
                  "pattern": "^(0[xX][0-9a-fA-F]+|[0-9]+)$"
                }
              },
              "urls": {
                "title": "Maps detail URLs to exclude",
                "type": "array",
                "description": "Supported HTTPS Google Maps detail URLs only. Identityless place URLs match exact canonical URL.href, retaining path case and query content/order. No fragments, credentials or custom ports. Short and search URLs are rejected without navigation or resolution.",
                "maxItems": 1000,
                "items": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 4096
                }
              },
              "contacted": {
                "title": "Supplied contacted exclusions",
                "type": "array",
                "description": "Business identity keys can suppress rows. Business-contact matching and all person entries remain blocked pending attribution/source contracts. This instruction is not evidence of an observed contact event. One entry, in one of three closed shapes: {\"scope\": \"business\", \"identity\": {\"kind\": \"placeId\" | \"cid\" | \"url\", \"value\": string}} suppresses one Maps business; {\"scope\": \"business\", \"contact\": {\"kind\": \"email\" | \"phone\", \"value\": string}, \"sourceScope\": \"maps-observation\" | \"website-observation\"} and {\"scope\": \"person\", \"contact\": {...}} are accepted but blocked. Only business identity selectors execute. No other keys are allowed; the Actor validates each entry before any work starts.",
                "maxItems": 1000,
                "items": {
                  "type": "object"
                }
              },
              "unsubscribed": {
                "title": "Supplied unsubscribed exclusions",
                "type": "array",
                "description": "Uses the same scoped contract as contacted. Person scope never suppresses a business by shared company/domain association.",
                "maxItems": 1000,
                "items": {
                  "type": "object"
                }
              },
              "bounced": {
                "title": "Supplied bounced exclusions",
                "type": "array",
                "description": "Uses the same scoped contract as contacted. No inferred bounce, mailbox verification or business/person association is created.",
                "maxItems": 1000,
                "items": {
                  "type": "object"
                }
              },
              "priorDatasets": {
                "title": "Authorized prior datasets",
                "type": "array",
                "description": "Same-current-run-user business datasets only after native authorization. Person scope is previewable but blocked. Shared/public/foreign-account datasets, local execution and partial imports are unsupported. IDs, KVS records and checksums do not establish authorization.",
                "maxItems": 4,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "datasetId",
                    "scope"
                  ],
                  "properties": {
                    "datasetId": {
                      "title": "Dataset ID",
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 64,
                      "pattern": "^[A-Za-z0-9_-]+$",
                      "description": "Native dataset ID, not a URL or authorization token."
                    },
                    "scope": {
                      "title": "Entity scope",
                      "type": "string",
                      "enum": [
                        "business",
                        "person"
                      ],
                      "description": "Only business scope can execute. Person dataset mapping remains blocked."
                    }
                  }
                }
              }
            },
            "additionalProperties": false
          },
          "previewFeatures": {
            "title": "Preview feature roadmap without scraping",
            "type": "boolean",
            "description": "Inspect effective Maps settings and planned feature gates without scraping, website/model requests, dataset publication or charge calls. Writes only FEATURE_PREVIEW and preserves existing SUMMARY/publication evidence. Base-price estimates are not verified billing quotes. A normal Actor invocation can still consume platform resources.",
            "default": false
          },
          "captureDetailEvidence": {
            "title": "Capture one detail evidence artifact",
            "type": "boolean",
            "description": "Diagnostic opt-in for the first granted detail work item, never a replacement target. Requires result caching off. Captures only permitted field observations already consumed by extraction, not full HTML or session data. Stores at most one compact JSON artifact under DETAIL_EVIDENCE, up to 2 MiB. Missing, private, oversized or failed capture is not successful proof. No extra navigation, website enrichment or accuracy certification.",
            "default": false
          },
          "captureWorkCheckpoint": {
            "title": "Capture sealed work checkpoint",
            "type": "boolean",
            "description": "Diagnostic opt-in to capture actual sealed Maps records and native work/resource/cost state before business publication. Omitted or false adds no checkpoint I/O. Reserves exactly 30,000 ms (30 seconds) upfront before source work inside the existing configured/platform envelope, without increasing maxRunTimeSecs or renewing deadlines. Requires maxRunTimeSecs > 30 and enough platform reserve for capture, the existing cleanup allowance, up to 90 seconds for publication/SUMMARY and 5 seconds for exit. Hosted captures use WORK_CHECKPOINT_V1 and WC1_<sha256> KVS chunks. LOCAL captures use a persistent MemoryStorage sidecar, not Actor.getValue. Checksums establish integrity only: no automatic resume, authenticity, ownership, antirollback or trusted supplier-cost restoration. Existing manifests are refused before new source work, not adopted or overwritten. Any prior publication intent stays reconcile-only. Captured work is not proof of delivered or billed output. Feature previews never capture work.",
            "default": false
          },
          "proxyConfiguration": {
            "title": "Proxy (disabled in this build)",
            "type": "object",
            "description": "This build makes direct requests. Keep useApifyProxy false. Enabled proxy settings and nonempty or malformed proxyUrls are rejected before launch, not silently ignored.",
            "default": {
              "useApifyProxy": false
            }
          }
        }
      },
      "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "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
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}