{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Maps Scraper",
    "description": "Stop wasting your budget on slow, resource-heavy browser-based scrapers. This is the fastest, most cost-effective, and data-rich Google Maps scraper on Apify, designed for high-scale lead generation and market research.",
    "version": "1.6",
    "x-build-id": "nIE3A6kh1v2SnoHh1"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/vortex_data~google-maps/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-vortex_data-google-maps",
        "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/vortex_data~google-maps/runs": {
      "post": {
        "operationId": "runs-sync-vortex_data-google-maps",
        "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/vortex_data~google-maps/run-sync": {
      "post": {
        "operationId": "run-sync-vortex_data-google-maps",
        "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 term(s)",
            "uniqueItems": true,
            "type": "array",
            "description": "Type what you would normally search for in Google Maps, for example <b>restaurant</b>, <b>recording studio</b>, <b>dentist</b>, or <b>taksi</b>. Add one search term per line.<br><br>💡 Use specific terms for cleaner results. Very similar terms may improve coverage, but they also make the run longer.<br><br>⚠️ Looking for a specific business name? Add a clear 📍 <b>Location</b> too, otherwise Google Maps may return the matching name from another city or country.",
            "items": {
              "type": "string"
            }
          },
          "locationQueries": {
            "title": "📍 Location(s)",
            "maxItems": 50,
            "uniqueItems": true,
            "type": "array",
            "description": "Define one or more search areas. Add one complete location per line, for example <b>Brooklyn, New York, United States</b>, <b>Seddon, Victoria, Australia</b>, or <b>Footscray, Victoria, Australia</b>.<br><br>Do not combine two cities in one line. Each location is geocoded separately, and <b>Number of places</b> applies per search term per location.<br><br>Simple formats usually work best: City + Country, or Neighborhood + City. Use 📡 <b>Geolocation parameters*</b> only when this field is empty. Use 🛰 <b>Custom search area</b> with a GeoJSON MultiPolygon when you need exact custom boundaries.",
            "items": {
              "type": "string"
            }
          },
          "geoStrictMatch": {
            "title": "📍 Keep only places inside the search area",
            "type": "boolean",
            "description": "Turn this on to drop Google Maps results whose coordinates are outside the resolved 📍 <b>Location(s)</b>, 📡 <b>Geolocation parameters</b>, or 🛰 <b>Custom search area</b>.<br><br>For multiple locations, each result is checked against the specific location area that produced it. GeoJSON Polygon and MultiPolygon inputs are checked against the exact polygon. Small geocoded areas such as suburbs and neighborhoods use the exact OpenStreetMap boundary when available; broader geocoded areas and Point/radius inputs use the resolved bbox.<br><br>Strict mode requires each location to resolve to a real area boundary. If a location resolves only to a point, that location is skipped with a clear warning instead of returning unchecked rows.<br><br>Leave it off to keep all places Google returns for the query, including nearby results outside the area. The dataset will mark them with <code>insideSearchArea=false</code> when a boundary is available.",
            "default": false
          },
          "maxCrawledPlacesPerSearch": {
            "title": "💯 Number of places to extract (per search term, location, or URL)",
            "minimum": 1,
            "maximum": 100000,
            "type": "integer",
            "description": "Maximum number of places to keep for each search term in each location, or for each direct URL. Higher values take longer and may use more requests.<br><br>For quick tests, use <b>50</b>. For city-scale lead lists, use <b>500</b> or more.",
            "default": 50
          },
          "language": {
            "title": "🌍 Language",
            "enum": [
              "en",
              "fi",
              "sv",
              "es",
              "fr",
              "de",
              "it",
              "pt",
              "ru",
              "uk",
              "pl",
              "tr",
              "nl",
              "ja",
              "ko",
              "zh-CN",
              "ar",
              "hi"
            ],
            "type": "string",
            "description": "Google Maps language used for names, categories, addresses, and labels.",
            "default": "en"
          },
          "skipClosedPlaces": {
            "title": "⏩ Skip closed places",
            "type": "boolean",
            "description": "Skip places that Google marks as temporarily or permanently closed. Useful when you only want currently active businesses.",
            "default": false
          },
          "extractContactsFromWebsite": {
            "title": "⏩ Add-on: Company contacts enrichment (from website)",
            "type": "boolean",
            "description": "Visit each business website and extract business emails, extra phone numbers, and social media links such as Facebook, Instagram, LinkedIn, X / Twitter, YouTube, TikTok, Pinterest, and WhatsApp. Places without websites are still saved; they simply cannot be website-enriched.",
            "default": false
          },
          "contactsFetchContactPage": {
            "title": "Try a contact page if homepage has no email",
            "type": "boolean",
            "description": "If the homepage has no email, try up to five likely contact-style pages such as <code>/contact</code>, <code>/contact-us</code>, <code>/contacto</code>, <code>/sucursales</code>, <code>/ubicacion</code>, <code>/prensa</code>, <code>/booking</code>, <code>/kundservice</code>, or <code>/impressum</code>.",
            "default": true
          },
          "contactsProxyFallback": {
            "title": "Retry failed website contact pages with proxy",
            "type": "boolean",
            "description": "If a business website or contact page does not open directly, retry only that failed website request through Apify residential proxy. The Actor still tries direct website fetching first to keep enrichment fast, then uses proxy only as a recovery fallback for sites that block direct traffic. Turn this off if you want the lowest possible proxy bandwidth usage.",
            "default": true
          },
          "contactsFilterEmailsByWebsiteDomain": {
            "title": "Keep only emails related to the website",
            "type": "boolean",
            "description": "Enabled by default for cleaner leads: save emails that look related to the business website, including same-domain emails, close domain variants, and common inboxes such as Gmail/Yahoo when they are found on the business site.<br><br>Turn this off to save every valid-looking public email found on the visited homepage/contact pages, even if it belongs to a parent company, agency, vendor, platform, or another domain. Obvious fake/broken placeholders are still removed.",
            "default": true
          },
          "contactsSkipChains": {
            "title": "Skip global chains during contact enrichment",
            "type": "boolean",
            "description": "Skip enrichment for large chains whose corporate websites rarely contain useful local branch emails.",
            "default": true
          },
          "contactsTimeoutSecs": {
            "title": "Website timeout",
            "minimum": 3,
            "maximum": 60,
            "type": "integer",
            "description": "Maximum seconds to wait for each business website during contact enrichment.",
            "default": 10
          },
          "maxReviewsPerPlace": {
            "title": "Number of reviews to extract",
            "minimum": 0,
            "maximum": 1000,
            "type": "integer",
            "description": "How many reviews to attach to each place. Set to <b>0</b> for the fastest run. Reviews are fetched from Google's paginated review feed; large values can take noticeably longer and use more requests.",
            "default": 0
          },
          "reviewsSort": {
            "title": "Sort reviews by",
            "enum": [
              "newest",
              "most_relevant",
              "highest",
              "lowest"
            ],
            "type": "string",
            "description": "Requested review ordering for Google's paginated reviews endpoint. Google can still return its default ordering for some places.",
            "default": "newest"
          },
          "additionalLanguages": {
            "title": "➕ Extra languages",
            "uniqueItems": true,
            "type": "array",
            "description": "Run the same search again in extra Google Maps languages, for example <code>fi</code>, <code>sv</code>, <code>es</code>, <code>de</code>. Each extra language adds another full pass and can uncover slightly different translated categories and rankings.",
            "items": {
              "type": "string"
            },
            "default": []
          },
          "countryCode": {
            "title": "🗺 Country",
            "type": "string",
            "description": "Alternative way to define the search area. Used only when 📍 <b>Location</b> is empty. Use ISO country code, for example <code>US</code>, <code>FI</code>, <code>DE</code>."
          },
          "city": {
            "title": "🌇 City",
            "type": "string",
            "description": "City name used only when 📍 <b>Location</b> is empty.<br><br>⚠️ Do not include state or country here; use the separate fields."
          },
          "state": {
            "title": "State / region",
            "type": "string",
            "description": "State, province, or region used only when 📍 <b>Location</b> is empty. Mainly useful for countries such as the United States."
          },
          "county": {
            "title": "County",
            "type": "string",
            "description": "County or similar regional unit used only when 📍 <b>Location</b> is empty."
          },
          "postalCode": {
            "title": "Postal code",
            "type": "string",
            "description": "ZIP / postal code used only when 📍 <b>Location</b> is empty.<br><br>⚠️ Use one postal code at a time."
          },
          "customGeolocation": {
            "title": "🛰 Custom search area (GeoJSON)",
            "type": "object",
            "description": "Define an exact custom search area with GeoJSON. Supported inputs: Polygon, MultiPolygon, or Point with <code>radiusKm</code>.<br><br>Use this when automatic city boundaries are too small, too large, or when you need a custom service area."
          },
          "startUrls": {
            "title": "🔗 Google Maps URLs",
            "type": "array",
            "description": "Paste full Google Maps URLs for exact places or copied Google Maps search result pages.<br><br><b>Exact places:</b> open the place in Google Maps, click <b>Share</b>, then <b>Copy link</b>. Accepted: <code>google.com/maps/place/...</code>, <code>maps.app.goo.gl/...</code>, URLs with <code>query_place_id</code>, and URLs with <code>cid</code>.<br><br><b>Search pages:</b> copied <code>google.com/maps/search/.../@lat,lng,zoom</code> or <code>google.com/maps/search/.../@lat,lng,meters</code> URLs are scraped as searches in that visible map area. The Actor uses the search text and viewport from the URL; it does not guess one exact place.<br><br>If your link contains <code>/g/...</code>, <code>0x...:0x...</code>, or <code>cid=...</code>, keep it as a full URL here. Do not paste those identifiers into <b>Place IDs</b>.<br><br>Search URLs without <code>@lat,lng,zoom</code> or <code>@lat,lng,meters</code> are ignored because no search area is available.",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "placeIds": {
            "title": "🗃 Place IDs",
            "uniqueItems": true,
            "type": "array",
            "description": "Paste only raw Google Place IDs, one per line.<br><br><b>Where to get them:</b> from Google Places API, Google's Place ID Finder, or from a Google Maps URL parameter named <code>query_place_id</code>.<br><br>Valid examples look like <code>ChIJreV9aqYWdkgROM_boL6YbwA</code>, <code>GhIJQWDl0CIeQUARxks3icF8U8A</code>, or longer IDs returned by Places API.<br><br>Do <b>not</b> paste full URLs, short <code>/g/...</code> IDs such as <code>11lmncvyn7</code>, fids such as <code>0x317067ffdca15d1b:0x367befc8c87c6fef</code>, or numeric CIDs here. If you have any of those, paste the full Google Maps URL into <b>Google Maps URLs</b> instead.<br><br>The Actor accepts a Place ID only when Google returns the same exact <code>placeId</code>.",
            "items": {
              "type": "string"
            }
          },
          "maxPlacesPerViewport": {
            "title": "Places per map viewport",
            "minimum": 1,
            "maximum": 120,
            "type": "integer",
            "description": "Maximum places fetched from one Google Maps viewport. Values above 120 do not help because Google caps each viewport response.",
            "default": 80
          },
          "enableSubdivision": {
            "title": "Split dense areas automatically",
            "type": "boolean",
            "description": "When a map area is dense, split it into smaller quadrants to get past Google's visible-result cap. Recommended for city and region searches.",
            "default": true
          },
          "maxSubdivisionDepth": {
            "title": "Maximum split depth",
            "minimum": 0,
            "maximum": 7,
            "type": "integer",
            "description": "How deep automatic area splitting can go. Higher values can find more places in dense cities, but use more requests.",
            "default": 4
          },
          "multiZoomDelta": {
            "title": "Extra zoom passes",
            "minimum": 0,
            "maximum": 3,
            "type": "integer",
            "description": "Search nearby zoom levels around the resolved area. <b>0</b> disables this. <b>1</b> can improve coverage but roughly triples search requests.",
            "default": 0
          },
          "zoom": {
            "title": "Override zoom level",
            "minimum": 1,
            "maximum": 21,
            "type": "integer",
            "description": "Override the automatically chosen Google Maps zoom level. Leave empty unless you need precise control."
          },
          "reverseGeocodeMissingAddress": {
            "title": "Fill missing addresses from coordinates",
            "type": "boolean",
            "description": "When Google returns coordinates but no address text, backfill the address from OpenStreetMap-based geocoding. This is free and cached.",
            "default": true
          },
          "concurrency": {
            "title": "Concurrent workers",
            "minimum": 1,
            "maximum": 32,
            "type": "integer",
            "description": "How many scraping tasks run in parallel. Higher values are faster but use more proxy bandwidth.",
            "default": 8
          },
          "requestTimeoutSecs": {
            "title": "Google request timeout",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "Maximum seconds to wait for each Google request. Increase this if residential proxy responses are slow.",
            "default": 30
          },
          "minRequestIntervalMs": {
            "title": "Delay between Google requests",
            "minimum": 0,
            "maximum": 5000,
            "type": "integer",
            "description": "Minimum delay in milliseconds between Google requests. Lower values are faster; higher values are gentler when proxies are unstable.",
            "default": 250
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}