{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Maps Website & Contact Extractor",
    "description": "Extract Google Maps business listings and enrich them with lightweight website contact details such as emails, contact page URL, phone numbers, and social profile links.",
    "version": "1.1",
    "x-build-id": "x7Kh5zJfD9niZ8AtY"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/coregent~google-maps-website-contact-extractor/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-coregent-google-maps-website-contact-extractor",
        "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/coregent~google-maps-website-contact-extractor/runs": {
      "post": {
        "operationId": "runs-sync-coregent-google-maps-website-contact-extractor",
        "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/coregent~google-maps-website-contact-extractor/run-sync": {
      "post": {
        "operationId": "run-sync-coregent-google-maps-website-contact-extractor",
        "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": {
          "searchQueries": {
            "title": "Search Queries (keyword + location)",
            "type": "array",
            "description": "List of keyword + location pairs to search on Google Maps. Add one row per search - \"Key\" is the business keyword (e.g. plumbers), \"Value\" is the location (e.g. Canberra ACT). Each row becomes one search like \"plumbers in Canberra ACT\". You can leave the location blank on every row and set Country / City / State / Postal code below instead. Clear this field entirely if you only want to process Start URLs or Place IDs.",
            "default": [
              {
                "key": "plumbers",
                "value": "Canberra ACT"
              }
            ],
            "items": {
              "type": "object",
              "required": [
                "key",
                "value"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "title": "Key"
                },
                "value": {
                  "type": "string",
                  "title": "Value"
                }
              }
            }
          },
          "searches": {
            "title": "Searches (structured form, for API and code)",
            "type": "array",
            "description": "The same keyword + location searches as the field above, written as objects: [{ \"query\": \"plumbers\", \"location\": \"Canberra ACT\" }]. This is the form to use from the API, a script or a code example, where the key/value editor above is awkward to express. Both fields are read, and an identical query+location appearing in both is only searched once. Leave this empty if you are using the editor above."
          },
          "startUrls": {
            "title": "Start URLs (Google Maps place or search URLs)",
            "type": "array",
            "description": "Google Maps URLs to process directly. A place URL (https://www.google.com/maps/place/...) is resolved straight to that one business and enriched - no searching, so no risk of matching the wrong listing. A search URL (https://www.google.com/maps/search/...) is run as a search, and any map viewport pinned into the URL is preserved. Non-Maps URLs are skipped with a warning.",
            "default": [],
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "placeIds": {
            "title": "Place IDs",
            "type": "array",
            "description": "Google place IDs to enrich directly, one per line. Three formats are accepted: the hex feature ID this actor outputs in its own place_id column (0x6b164d...:0xb3e59a...), a Places API ID (ChIJ...), and a numeric CID. This is the fastest way to re-enrich a list you already have - feed yesterday's place_id column straight back in.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxTotalResults": {
            "title": "Max results in total (whole run)",
            "minimum": 1,
            "maximum": 5000,
            "type": "integer",
            "description": "Hard ceiling on the number of business rows this run will save, across every search, Start URL and Place ID combined. This is your main cost control: you are charged one event per saved row, so this number caps your result-event spend for the run. Hard cap 5000.",
            "default": 500
          },
          "countryCode": {
            "title": "Country",
            "enum": [
              "",
              "AU",
              "AT",
              "BE",
              "BR",
              "CA",
              "CH",
              "CL",
              "CO",
              "CZ",
              "DE",
              "DK",
              "ES",
              "FI",
              "FR",
              "GB",
              "GR",
              "HK",
              "HU",
              "ID",
              "IE",
              "IL",
              "IN",
              "IT",
              "JP",
              "KR",
              "MX",
              "MY",
              "NL",
              "NO",
              "NZ",
              "PH",
              "PL",
              "PT",
              "RO",
              "SE",
              "SG",
              "TH",
              "TR",
              "TW",
              "US",
              "VN",
              "ZA"
            ],
            "type": "string",
            "description": "Country to search in, sent to Google Maps as the locale hint (gl=). It is not added to the search text, so a search with no location of its own should also set City, State / region or Postal code. If your country is not listed, leave this empty and use the legacy \"Country code (free text)\" field under Advanced settings.",
            "default": ""
          },
          "city": {
            "title": "City",
            "type": "string",
            "description": "City or suburb to search in, for example Fyshwick. Used for any search query that has no location of its own.",
            "default": ""
          },
          "state": {
            "title": "State / region",
            "type": "string",
            "description": "State, province or region, for example ACT or California. Used for any search query that has no location of its own.",
            "default": ""
          },
          "postalCode": {
            "title": "Postal code",
            "type": "string",
            "description": "Postal or ZIP code, for example 2609. Narrows the search area further; most useful combined with City.",
            "default": ""
          },
          "language": {
            "title": "Language",
            "type": "string",
            "description": "Language code used for Google Maps UI (IETF BCP-47), for example en, es, de.",
            "default": "en"
          },
          "websiteFilter": {
            "title": "Website filter",
            "enum": [
              "any",
              "hasWebsite",
              "missingWebsite"
            ],
            "type": "string",
            "description": "Filter by website availability. \"any\" keeps everything; \"hasWebsite\" keeps only businesses with a website (recommended for contact extraction); \"missingWebsite\" keeps only businesses without a website.",
            "default": "hasWebsite"
          },
          "phoneRequired": {
            "title": "Phone required",
            "type": "boolean",
            "description": "If enabled, only keep businesses that have a visible phone number (from Google Maps or the website).",
            "default": false
          },
          "emailRequired": {
            "title": "Email required",
            "type": "boolean",
            "description": "If enabled, only keep businesses where at least one email was found on the website. Needs \"Visit business websites for contact data\" turned on: with that off no website is read, so every business is filtered out. Filtered rows are never billed.",
            "default": false
          },
          "minRating": {
            "title": "Minimum rating",
            "minimum": 0,
            "maximum": 5,
            "type": "integer",
            "description": "Keep only businesses rated at least this highly on Google (1-5). Leave at 0 to keep every business. Businesses with no rating at all are removed when this is set, because an unrated listing cannot be shown to meet the bar. Filtered rows are never billed.",
            "default": 0
          },
          "minReviewCount": {
            "title": "Minimum number of reviews",
            "minimum": 0,
            "type": "integer",
            "description": "Keep only businesses with at least this many Google reviews - a good proxy for how established a business is. Leave at 0 to keep every business. IMPORTANT: Google usually omits the review count from search result cards, so this filter only works properly with \"Open each business listing for full details\" turned on. With that option off, most businesses have no review count to test and will be filtered out. Filtered rows are never billed.",
            "default": 0
          },
          "categoriesInclude": {
            "title": "Only these categories",
            "type": "array",
            "description": "Keep only businesses whose Google category matches one of these, one per line. Matching is case-insensitive and partial, so \"plumb\" keeps both \"Plumber\" and \"Emergency plumbing service\". Leave empty to keep every category.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "categoriesExclude": {
            "title": "Exclude these categories",
            "type": "array",
            "description": "Remove businesses whose Google category matches one of these, one per line. Same case-insensitive partial matching. Exclusions win over \"Only these categories\" when both match.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "skipClosedPlaces": {
            "title": "Skip permanently and temporarily closed businesses",
            "type": "boolean",
            "description": "Remove businesses Google marks as permanently or temporarily closed. Closed listings are detected from the words Google prints on the card or listing, so this works best on English-language runs (the default). Filtered rows are never billed.",
            "default": false
          },
          "searchMatching": {
            "title": "How strictly must the name match your keyword?",
            "enum": [
              "all",
              "includes",
              "exact"
            ],
            "type": "string",
            "description": "Google often returns loosely related businesses for a search term. \"All results\" keeps everything Google returns. \"Name contains the keyword\" keeps only businesses whose name includes your search keyword - this is a plain substring test, not a smart one: plural keywords also match their singular (\"plumbers\" matches \"Master Plumber\"), but a keyword will NOT match a different word form, so \"plumbers\" does not match \"Acme Plumbing\". Search for \"plumbing\" if that is what you want. \"Name is exactly the keyword\" is for finding a specific chain by name. Only applies to keyword searches - businesses supplied through Start URLs or Place IDs are never filtered by this.",
            "default": "all"
          },
          "scrapePlaceDetailPage": {
            "title": "Open each business listing for full details (slower)",
            "type": "boolean",
            "description": "When enabled, the actor opens each business's Google Maps listing to read fields the search results card often hides — most importantly the website link, but also the phone number, full address, place ID and opening hours. Strongly recommended when \"Website filter\" is set to \"Has website\": many businesses do have a website that simply is not shown on the card, and without this option those leads are missed entirely. Costs one extra Google Maps page load per business, so runs take significantly longer. Billing is unchanged — you still pay one flat event per saved row. Does not apply to Start URL place links or Place IDs: those always open the listing, because it is their only source of data.",
            "default": false
          },
          "includeWebsiteContactExtraction": {
            "title": "Visit business websites for contact data",
            "type": "boolean",
            "description": "When enabled, the actor visits each business website (homepage and a small number of likely contact/about pages) to extract emails, phone numbers, social links, and the contact page URL. Disable to return Google Maps fields only.",
            "default": true
          },
          "maxPagesPerWebsite": {
            "title": "Max pages per website (slower)",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "Upper bound on the number of pages fetched per business website (homepage + likely contact/about pages). Hard cap 5.",
            "default": 3
          },
          "includeSocialLinks": {
            "title": "Extract social links",
            "type": "boolean",
            "description": "Extract Facebook, Instagram, LinkedIn, X (Twitter), YouTube, TikTok, Pinterest and Discord profile links from website pages.",
            "default": true
          },
          "includeWebsitePhone": {
            "title": "Extract website phone",
            "type": "boolean",
            "description": "Extract phone numbers from website pages (in addition to the Google Maps phone).",
            "default": true
          },
          "includeOpeningHours": {
            "title": "Include opening hours",
            "type": "boolean",
            "description": "Include opening hours summary when visible on the Google Maps card. Default off to keep runtime predictable.",
            "default": false
          },
          "includeCoordinates": {
            "title": "Include coordinates",
            "type": "boolean",
            "description": "Include latitude/longitude when available from the listing URL.",
            "default": true
          },
          "deduplicateResults": {
            "title": "Deduplicate results",
            "type": "boolean",
            "description": "Remove duplicate businesses across queries using place_id, listing URL, website domain, and name+address keys.",
            "default": true
          },
          "skipPlaceIds": {
            "title": "Skip these place IDs",
            "type": "array",
            "description": "Businesses to leave out of this run, by place ID, one per line. Paste the place_id column from a previous run to avoid re-scraping and re-paying for businesses you already have. Accepts the same three formats as the Place IDs input above.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "skipGoogleMapsUrls": {
            "title": "Skip these Google Maps URLs",
            "type": "array",
            "description": "Businesses to leave out of this run, by their Google Maps listing URL, one per line. Paste the google_maps_url column from a previous run.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "skipWebsiteDomains": {
            "title": "Skip these website domains",
            "type": "array",
            "description": "Businesses to leave out of this run, by website domain, one per line - for example \"example.com\". Subdomains are skipped too, so \"example.com\" also skips \"shop.example.com\". Full URLs are accepted and reduced to their domain. Useful for excluding existing customers, competitors, or franchise head offices.",
            "default": [],
            "items": {
              "type": "string"
            }
          },
          "maxResultsPerSearch": {
            "title": "Max results per search",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Optional cap on rows collected from each individual search, so one broad search cannot consume the whole total. When an area is split into cells, each cell counts as a search. Leave empty to use the legacy \"Max results per query\" value, which defaults to 100. Hard cap 500 per search. Replaces the older \"maxResults\" field, which is still accepted; if both are set, this one wins."
          },
          "maxResults": {
            "title": "Max results per query (legacy)",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Deprecated name for \"Max results per search\", kept so existing Tasks and API calls keep working unchanged. If both are set, \"Max results per search\" wins.",
            "default": 100
          },
          "customGeolocation": {
            "title": "Custom geolocation (GeoJSON)",
            "type": "object",
            "description": "Pin searches to a specific map area instead of letting Google pick one. Accepts a GeoJSON geometry, Feature or FeatureCollection (Polygon, MultiPolygon or Point), or a plain { \"lat\": -35.28, \"lng\": 149.13, \"zoom\": 12 } object. The area's centre and an appropriate zoom are written into the Maps search URL. Google returns roughly 120 results for a plain keyword search no matter how far the list is scrolled; anchoring the search to an area is how you get past that."
          },
          "searchAreaCells": {
            "title": "Split the search area into this many cells",
            "minimum": 1,
            "maximum": 64,
            "type": "integer",
            "description": "Google returns roughly 120 results per search term no matter how far the list is scrolled. Splitting the area into cells and searching each one separately is how you get past that ceiling. Requires \"Custom geolocation\" to be set - that is the area being split. Leave at 1 for no splitting. Each cell is a separate Google Maps page load, so this multiplies run time and cost per row; your \"Max results in total\" cap still bounds what you are charged, and businesses found in more than one cell are deduplicated before anything is saved or billed.",
            "default": 1
          },
          "verifyEmails": {
            "title": "Verify emails (free)",
            "type": "boolean",
            "description": "Check each business's primary email without sending anything to it: is the address well formed, is the domain a throwaway-inbox provider, does the domain actually publish mail servers (MX records), and is it a shared mailbox like info@ rather than a person. Results land in email_status, email_mx_valid, email_is_role and email_is_disposable. This uses DNS only - there is no verification provider behind it and no extra cost to you, which is why it is on by default. It does NOT connect to the mail server to test whether the individual mailbox exists; that needs paid infrastructure and is not offered here. Note that email_status \"no_mx\" means the domain publishes no mail servers, which is a strong bounce risk rather than proof the address is dead.",
            "default": true
          },
          "includeContactForm": {
            "title": "Detect contact forms (free)",
            "type": "boolean",
            "description": "Record whether each business website has a contact form, where it is, and what it asks for (contact_form_url, has_contact_form, contact_form_action, contact_form_fields). Many small businesses deliberately publish no email address and take enquiries through a form instead - those businesses come back with email_count 0 from every Maps scraper, and look like dead ends when they are not. This reads pages that have already been fetched, so it costs nothing extra. Site search boxes, login forms and newsletter signups are deliberately not counted.",
            "default": true
          },
          "country": {
            "title": "Country code (free text, legacy)",
            "type": "string",
            "description": "Two-letter ISO country code used as a Google Maps locale hint, for example AU, US, GB. Use this if your country is not in the Country dropdown above. The dropdown wins if both are set.",
            "default": ""
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Apify Proxy configuration. Defaults to Apify Proxy enabled. Apify Residential is NOT supported and will fail the run at startup; if you need residential routing, supply your own provider via Custom proxy URLs (proxyUrls).",
            "default": {
              "useApifyProxy": true
            }
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}