{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Maps Scraper API — Emails, Reviews, Photos & Leads",
    "description": "Google Maps scraper and Google Places API alternative: extract business names, phones, websites, hours, ratings and reviews — plus emails and 7 social profiles pulled from each place's website. Bypass Google's 120-place limit. Export JSON, CSV, Excel. From $3/1k places, no API key.",
    "version": "1.0",
    "x-build-id": "SConCb1SjK7ZqrNvh"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/pro100chok~google-maps-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-pro100chok-google-maps-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/pro100chok~google-maps-scraper/runs": {
      "post": {
        "operationId": "runs-sync-pro100chok-google-maps-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/pro100chok~google-maps-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-pro100chok-google-maps-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": {
          "searchStringsArray": {
            "title": "🔍 Search terms",
            "type": "array",
            "description": "What to search for — one term per line (e.g. `restaurant`, `gym`, `dentist`). Each term is searched in every location below, so 3 terms x 5 cities = 15 searches. You can leave this empty ONLY if you paste Google Maps search links into 'Google Maps search URLs' below.",
            "items": {
              "type": "string"
            }
          },
          "locationQueries": {
            "title": "📍 Locations",
            "type": "array",
            "description": "One location per line — the search runs in every one of them. Format: free text as you'd type into Google Maps, from broad to narrow:\n• `New York` · `Rajasthan, India` · `Brooklyn, New York`\n• `Jaisalmer, Rajasthan, India` · `11201` (postal code)\n\nEach search term is run against each location, so 6 terms × 38 cities = 228 searches. For a single city just enter one line. Leave empty to use the structured City / State / Country fields below instead.",
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "🔗 Google Maps search URLs",
            "type": "array",
            "description": "Paste Google Maps SEARCH links (e.g. `google.com/maps/search/gyms+in+Jaipur`) and their query is added to the run. Links to one specific place are not supported — use the search fields above for those.",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "city": {
            "title": "🏙️ City",
            "type": "string",
            "description": "Optional — city name. Combined with the other fields into one location. Ignored if you filled the Locations list above."
          },
          "state": {
            "title": "🗺️ State / region",
            "type": "string",
            "description": "Optional — US state or other top-level region (e.g. `CA`, `Texas`). Combined with the other fields into one location."
          },
          "country": {
            "title": "🌍 Country",
            "type": "string",
            "description": "Optional — full country name, e.g. `USA`, `Germany`, `India`. Combined with the other fields into one location."
          },
          "county": {
            "title": "🗺️ County / district",
            "type": "string",
            "description": "Optional — county or district. Combined with the other fields into one location."
          },
          "postalCode": {
            "title": "📮 Postal code",
            "type": "string",
            "description": "Optional — narrows the search to one postal code. Combined with the city/state/country fields into a single location."
          },
          "maxItems": {
            "title": "🧮 Max results",
            "minimum": 1,
            "maximum": 100000,
            "type": "integer",
            "description": "Hard cap on how many places the run returns in total, across ALL search terms and locations. The actor stops as soon as it has this many — you are never charged for extras. Leave empty to keep going until Google runs out of results.",
            "default": 1000
          },
          "maxCrawledPlacesPerSearch": {
            "title": "🎯 Max results per search (term x location)",
            "minimum": 1,
            "maximum": 100000,
            "type": "integer",
            "description": "Cap for ONE search term in ONE location. With many locations this is what keeps coverage even: without it the first cities crawled can consume the whole 'Max results' budget and the rest return nothing. Leave empty for no per-search cap. The global 'Max results' still applies on top."
          },
          "deepSearch": {
            "title": "🌐 Deep search (split by postal code / district)",
            "type": "boolean",
            "description": "Google caps a single search at ~160 places. With this on, the actor splits the location into postal codes and districts and keeps searching until it hits your Max results — the only way to get thousands of places. On by default; it costs nothing on small runs, since crawling stops the moment Max results is reached.",
            "default": true
          },
          "categoryFilterWords": {
            "title": "🏷️ Category filter",
            "type": "array",
            "description": "Keep only places whose Google category matches one of these words (e.g. `gym`, `fitness`). Leave empty to keep every category. Filtered-out places cost you nothing — they are dropped before enrichment.",
            "items": {
              "type": "string"
            }
          },
          "searchMatching": {
            "title": "🎯 Name matching",
            "enum": [
              "all",
              "only_includes",
              "only_exact"
            ],
            "type": "string",
            "description": "How strictly a place name must match your search term. Google returns loosely related places by default, which is noise for lead lists.",
            "default": "all"
          },
          "placeMinimumStars": {
            "title": "⭐ Minimum rating",
            "enum": [
              "",
              "two",
              "twoAndHalf",
              "three",
              "threeAndHalf",
              "four",
              "fourAndHalf"
            ],
            "type": "string",
            "description": "Skip places rated below this. Places with no rating yet are always kept.",
            "default": ""
          },
          "minimumReviewsCount": {
            "title": "💬 Minimum number of reviews",
            "minimum": 0,
            "type": "integer",
            "description": "Skip places with fewer reviews than this — a quick way to drop inactive or barely-registered listings."
          },
          "website": {
            "title": "🌐 Website filter",
            "enum": [
              "allPlaces",
              "withWebsite",
              "withoutWebsite"
            ],
            "type": "string",
            "description": "Emails come from a place's website, so 'Only places WITH a website' is the setting for email-focused lead lists. 'Only WITHOUT' finds businesses to sell web design to.",
            "default": "allPlaces"
          },
          "skipClosedPlaces": {
            "title": "🚪 Skip closed places",
            "type": "boolean",
            "description": "Drop businesses Google marks as permanently or temporarily closed. Worth turning on for lead lists — a closed business is a wasted row.",
            "default": false
          },
          "scrapeContactsFromWebsite": {
            "title": "📧 Scrape emails + socials from websites",
            "type": "boolean",
            "description": "For every place that has a website, fetch homepage + contact pages and extract emails plus Facebook / Instagram / X / LinkedIn / YouTube / TikTok / Telegram links.",
            "default": true
          },
          "skipPlacesWithoutEmail": {
            "title": "📭 Skip places with no email",
            "type": "boolean",
            "description": "Keep ONLY places where an email was found. Requires email scraping to be on (above). Careful: this typically removes 50-60% of results, since most small businesses publish only a phone number.",
            "default": false
          },
          "skipDuplicateEmails": {
            "title": "🔁 Skip places with a duplicate email",
            "type": "boolean",
            "description": "Keep only the first place for each email address. Booking platforms and website templates put one shared address (e.g. `support@platform.com`) on dozens of listings — this drops those repeats so you don't pay for the same lead twice. Places with no email are never affected.",
            "default": true
          },
          "includeReviews": {
            "title": "💬 Include reviews (add-on)",
            "type": "boolean",
            "description": "Attach user reviews per place — text, rating, author, date, and attached photos. Paginates through Google's review API, so you get the full review history (up to your cap below). Billed per review extracted — see pricing.",
            "default": false
          },
          "maxReviewsPerPlace": {
            "title": "🔢 Max reviews per place",
            "minimum": 1,
            "maximum": 5000,
            "type": "integer",
            "description": "Hard cap on reviews attached to one place. The actor paginates Google's review API (~10 reviews per page) until it hits this cap or runs out of reviews.",
            "default": 20
          },
          "includeImages": {
            "title": "🖼️ Include image URLs (add-on)",
            "type": "boolean",
            "description": "Attach photo URLs to each place. Sources: 3-5 thumbnails from the search response + user-submitted photos from reviews (works only together with 💬 includeReviews). Higher review limit → more photos. Billed per image.",
            "default": false
          },
          "maxImagesPerPlace": {
            "title": "🔢 Max images per place",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "How many image URLs to collect per place (the URLs only, not the image files). Charged per image.",
            "default": 20
          },
          "language": {
            "title": "🗣️ Language (hl)",
            "enum": [
              "en",
              "es",
              "fr",
              "de",
              "it",
              "pt",
              "ru",
              "uk",
              "pl",
              "tr",
              "nl",
              "ja",
              "ko",
              "zh-CN"
            ],
            "type": "string",
            "description": "Language of the results, as a 2-letter code (`en`, `es`, `de`, `ru`, `hi`). Affects category names and review text.",
            "default": "en"
          },
          "countryCode": {
            "title": "🚩 Country code (gl)",
            "pattern": "^[a-zA-Z]{2}$",
            "type": "string",
            "description": "Google geolocation hint — 2-letter ISO code. Affects ranking, results, and currency.",
            "default": "us"
          },
          "maxRetries": {
            "title": "🔁 Max retries per request",
            "minimum": 1,
            "maximum": 15,
            "type": "integer",
            "description": "How many times to retry a search that fails. The default is fine; raise it only on a very unstable connection.",
            "default": 5
          },
          "concurrency": {
            "title": "⚡ Concurrent sessions",
            "minimum": 1,
            "maximum": 50,
            "type": "integer",
            "description": "Google sessions running in parallel, each on its own IP. Higher = faster. 10 is a good default; raise it for very large runs.",
            "default": 10
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}