{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Maps Scraper",
    "description": "Extract business leads from Google Maps with verified emails, phones & social profiles. 50+ niches (HVAC, dentists, lawyers, real estate & more). DNS/SMTP email verification. $0.003/lead — up to 10x cheaper than alternatives. HubSpot/Salesforce export. MCP-ready for AI agents.",
    "version": "1.0",
    "x-build-id": "LAa8RWfzMAYCyGAOK"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/get-leads~google-maps-scraper---best-value-for-money/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-get-leads-google-maps-scraper---best-value-for-money",
        "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/get-leads~google-maps-scraper---best-value-for-money/runs": {
      "post": {
        "operationId": "runs-sync-get-leads-google-maps-scraper---best-value-for-money",
        "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/get-leads~google-maps-scraper---best-value-for-money/run-sync": {
      "post": {
        "operationId": "run-sync-get-leads-google-maps-scraper---best-value-for-money",
        "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": {
          "businessType": {
            "title": "Business Type",
            "enum": [
              "general",
              "hvac",
              "plumber",
              "roofer",
              "electrician",
              "contractor",
              "landscaper",
              "pest_control",
              "cleaning_service",
              "solar_installer",
              "painter",
              "pool_service",
              "garage_door",
              "fencing",
              "moving_company",
              "appliance_repair",
              "dentist",
              "chiropractor",
              "medical_clinic",
              "med_spa",
              "veterinarian",
              "physical_therapy",
              "orthodontist",
              "oral_surgeon",
              "optometrist",
              "pharmacy",
              "lawyer",
              "personal_injury_lawyer",
              "criminal_defense_lawyer",
              "family_lawyer",
              "immigration_lawyer",
              "bankruptcy_lawyer",
              "real_estate_agent",
              "property_management",
              "mortgage_broker",
              "insurance_agent",
              "accountant",
              "financial_advisor",
              "restaurant",
              "bar",
              "cafe",
              "catering",
              "auto_repair",
              "auto_dealer",
              "car_detailing",
              "towing",
              "hair_salon",
              "nail_salon",
              "day_spa",
              "gym",
              "pet_grooming",
              "daycare",
              "tutoring",
              "driving_school",
              "wedding_venue",
              "wedding_photographer",
              "wedding_planner",
              "florist",
              "home_inspector",
              "tattoo_shop",
              "storage_facility",
              "laundromat",
              "printing"
            ],
            "type": "string",
            "description": "Select a business niche for auto-generated search queries. Choose 'General Business' to use your own custom queries.",
            "default": "general"
          },
          "locations": {
            "title": "Locations",
            "type": "array",
            "description": "Cities, ZIP codes, or areas to search. Queries are auto-generated from Business Type + Locations. Example: 'Miami FL', '90210', 'Downtown Chicago'",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "searchQueries": {
            "title": "Custom Search Queries",
            "type": "array",
            "description": "Custom Google Maps search queries. Use this for full control, or combine with Business Type + Locations for extra queries.",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "zipCodes": {
            "title": "ZIP Codes",
            "type": "array",
            "description": "US ZIP codes to search (alternative to Locations). Bare 5-digit ZIPs are automatically expanded with the matching state name (e.g., '90210' → '90210 California') so Google Maps doesn't fall back to IP-geolocation and return wrong-region results.",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "maxResultsPerSearchTerm": {
            "title": "Max Results Per Search Term",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Maximum number of business listings to extract PER generated search term. Each business niche auto-generates 2–4 search terms (e.g. plumber → \"plumber\", \"plumbing company\", \"plumbing contractor\"). If you set 50 here and the niche has 3 terms, you can get up to ~150 leads per location after dedup. Use `maxResultsTotal` below to cap the overall run size. Defaults to 50 if omitted."
          },
          "maxResultsTotal": {
            "title": "Max Results Total (Overall Cap)",
            "minimum": 1,
            "maximum": 100000,
            "type": "integer",
            "description": "Strict hard cap on total results across all queries + locations. Enforced at push-time (parallel queries can't race past the cap) so request 5, get exactly 5. Applied after deduplication so you don't pay PPE for leads you'd discard. Leave empty for no cap. Useful when you want exactly N leads regardless of how many search terms / cities the niche expands into."
          },
          "maxResultsPerQuery": {
            "title": "Max Results Per Query (DEPRECATED — use maxResultsPerSearchTerm)",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "DEPRECATED alias for maxResultsPerSearchTerm. Kept for backward compatibility with existing runs/schedules. If both are set, maxResultsPerSearchTerm wins."
          },
          "language": {
            "title": "Language",
            "enum": [
              "en",
              "es",
              "fr",
              "de",
              "it",
              "pt",
              "nl",
              "ja",
              "ko",
              "zh-CN",
              "zh-TW",
              "ar",
              "ru",
              "pl",
              "sv",
              "da",
              "no",
              "fi",
              "cs",
              "tr",
              "th",
              "vi",
              "id",
              "el",
              "he",
              "hi",
              "ro",
              "hu",
              "uk"
            ],
            "type": "string",
            "description": "Language for results (ISO 639-1). Affects business names, descriptions, review text, and Google's category labels. End-to-end verified on en / es / fr / de / pt / ja — native-language categories like 'Restaurant français', 'Friseursalon', and 'ビュッフェ レストラン' are returned correctly.",
            "default": "en"
          },
          "countryCode": {
            "title": "Country",
            "type": "string",
            "description": "Country to focus results on (ISO 3166-1 alpha-2). Affects result ordering and local relevance. End-to-end verified on US / UK / AU / MY (plus FR / DE / BR / JP via language tests). For non-US markets pair with the matching `language` code; for IP-geolocation parity point `RESIDENTIAL_PROXY_URL` at a region-matched Evomi sticky-hardsession URL.",
            "default": "us"
          },
          "enrichmentDepth": {
            "title": "Enrichment Depth (one toggle for everything)",
            "enum": [
              "fast",
              "balanced",
              "deep"
            ],
            "type": "string",
            "description": "Single dial that controls scrape depth. **Recommended over the individual flags below.** When set, this OVERRIDES the individual `enrichWebsites` / `verifyEmails` / `returnAllEmails` / `includeImages` / `includeReviewsDistribution` flags. Leave unset to use the individual flags.\n\n- **`fast`** = scraping only, no website enrichment, no email verification, no reviews distribution. Cheapest + quickest. Good for bulk-prospecting where you just want place names + phone numbers.\n- **`balanced`** (recommended) = website enrichment + email verification + reviews distribution. The standard mode used by the audit baseline. ~80% email hit rate, ~97% margin.\n- **`deep`** = balanced + `returnAllEmails` (all candidate emails with confidence scores) + `includeImages` (top 10 photo URLs per place). Maximum data per lead.\n\nLeave empty (`null`) to use individual flags as before."
          },
          "leadQualityScoreMinimum": {
            "title": "Minimum Lead Quality Score (filter)",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "Drop leads below this `leadQualityScore` (0-100) at push time. **Saves PPE on cold leads.** A score of 75+ is a hot lead (verified email + active reviews + claimed listing + good rating). 50-74 is decent. <50 is skeleton listing. Leave empty for no filter (push all results)."
          },
          "scrapeAfterDate": {
            "title": "Skip Stale Listings (lastReviewDate ≥ this date)",
            "type": "string",
            "description": "ISO 8601 date string (e.g. `2026-01-01`). Drops businesses whose most-recent review is OLDER than this date. **Useful for filtering abandoned listings** — a business with no reviews in 6+ months may not be a viable lead. The filter runs at push-time, so dropped leads aren't billed (no PPE charge). Leave empty to keep all results."
          },
          "enrichWebsites": {
            "title": "Enrich with Website Data",
            "type": "boolean",
            "description": "Visit each business website to extract emails, social media URLs, and license numbers. (Ignored when `enrichmentDepth` is set.)",
            "default": true
          },
          "verifyEmails": {
            "title": "Verify Emails (DNS/SMTP)",
            "type": "boolean",
            "description": "Verify extracted emails using DNS MX lookup and SMTP handshake. Adds verification status fields. Slightly slower but improves data quality.",
            "default": true
          },
          "returnAllEmails": {
            "title": "Return All Emails (with confidence scores)",
            "type": "boolean",
            "description": "When enabled, adds an `emails` array to each lead with every discovered email + its `source` (scraped / deep-crawl / guessed) + a relevance `score`. The single-value `email` field still holds the top-scored winner. Useful for sales teams that want multiple contact candidates per business.",
            "default": false
          },
          "skipCategoryMismatches": {
            "title": "Skip Category Mismatches (drop off-niche results)",
            "type": "boolean",
            "description": "When enabled, drops leads whose Google-assigned categoryName doesn't match the niche you selected. Every lead always carries a `categoryMismatch: true/false` flag regardless of this setting. Niche→category keyword banks have been tightened so strict-intent niches (tutoring, appliance_repair, pet_grooming, daycare, driving_school) rarely surface false positives even with this flag off — but enable it when you want zero off-niche noise before PPE billing.",
            "default": false
          },
          "outputFormat": {
            "title": "Output Format",
            "enum": [
              "default",
              "hubspot",
              "salesforce",
              "fullcontact",
              "pipedrive",
              "zoho",
              "airtable",
              "csvflat"
            ],
            "type": "string",
            "description": "Choose output format: `default` (all raw fields), `hubspot` (HubSpot Contact import), `salesforce` (Salesforce Lead import), `fullcontact` (Full Contact JSON), `pipedrive` (Pipedrive Person/Org import), `zoho` (Zoho CRM Lead import), `airtable` (Airtable-friendly Title Case columns), or `csvflat` (flattens nested objects into underscore-separated columns — removes Excel friction for non-coder users).",
            "default": "default"
          },
          "includeOpeningHours": {
            "title": "Include Opening Hours",
            "type": "boolean",
            "description": "Extract detailed opening hours for each day of the week.",
            "default": true
          },
          "includeReviewsDistribution": {
            "title": "Include Star Distribution",
            "type": "boolean",
            "description": "Fetch 1-5 star review breakdown for each place. Disabling this reduces cost by ~30% and speeds up scraping. Most lead generation use cases don't need star breakdowns.",
            "default": true
          },
          "includeImages": {
            "title": "Include Image URLs",
            "type": "boolean",
            "description": "Extract photo URLs from each place listing.",
            "default": false
          },
          "maxImages": {
            "title": "Max Images Per Place",
            "minimum": 0,
            "maximum": 20,
            "type": "integer",
            "description": "Maximum number of image URLs to extract per place.",
            "default": 5
          },
          "queryConcurrency": {
            "title": "Query Concurrency",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "How many Google Maps search queries to run in parallel. Each niche expands into 2–4 auto-generated terms per location, and the default `3` roughly halves wall time for multi-query runs. Set to `1` for sequential, human-like cadence. Max 5 to stay under Google's per-IP burst threshold.",
            "default": 3
          },
          "resumePlaceIds": {
            "title": "Resume: placeIds to skip",
            "type": "array",
            "description": "Array of Google `placeId` strings to pre-seed the dedupe set. Any matching place discovered during this run is skipped entirely (no re-scrape, no re-enrichment, no PPE charge). Use this to stitch together a crashed long run — pull the placeIds from the partial dataset of the failed run and pass them here.",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "maxEnrichmentConcurrency": {
            "title": "Enrichment Concurrency",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Number of websites to process in parallel during enrichment.",
            "default": 5
          },
          "enrichmentPages": {
            "title": "Custom Website Pages to Check",
            "type": "array",
            "description": "Override which URL paths to visit on each website. Leave empty to use niche-optimized defaults.",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}