{
  "openapi": "3.0.1",
  "info": {
    "title": "Google Maps Scraper - Business Data, Reviews & Leads",
    "description": "Scrape Google Maps business listings without an API key — name, address, phone, website, rating, reviews, photos, hours, amenities & GPS. Precision targeting (batch, map URLs, custom area) + optional add-ons: emails, socials, contacts & ad intelligence. Export to CSV, Excel & JSON.",
    "version": "1.11",
    "x-build-id": "Y4bRCBSl7Tfxzeyvb"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/buff_pineapple~google-maps-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-buff_pineapple-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/buff_pineapple~google-maps-scraper/runs": {
      "post": {
        "operationId": "runs-sync-buff_pineapple-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/buff_pineapple~google-maps-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-buff_pineapple-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": {
          "query": {
            "title": "Search Query",
            "type": "string",
            "description": "What to search for on Google Maps — exactly what you'd type in the search box. Works best as \"<category> in <location>\", e.g. \"restaurants in New York\" or \"dentists in Miami\". Plain phrases like \"coffee shops Seattle\" or \"plumbers 90210\" also work — the location is detected automatically. Required unless you use Batch Search Queries or Google Maps URLs. A Custom Area also needs this field, because it supplies the category to search for. API callers must provide it explicitly; the prefill only fills the Console form."
          },
          "searchQueries": {
            "title": "Batch Search Queries (Precision Targeting)",
            "type": "array",
            "description": "Run several searches in one go — one query per line. Each is run through the full pipeline and results are merged and de-duplicated by place. Example: [\"dentists in Miami\", \"orthodontists in Miami\", \"dental clinics Fort Lauderdale\"]. Leave empty to use the single Search Query above.",
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "Google Maps Start URLs",
            "type": "array",
            "description": "Scrape from Google Maps URLs. A PLACE link fetches that single business with its full details: the full URL from the browser's address bar (with or without @lat,lng), a maps.app.goo.gl share link, a ?cid= link, or a ?q=place_id: / query_place_id link (a link with only a /g/ id needs its name and coordinates). A SEARCH url (e.g. .../maps/search/...) re-runs that query. A place that could not be fetched, or came back incomplete, is named in the run's status message; URLs that are not Google Maps links are skipped with a warning. Accepts plain URLs or {\"url\": \"...\"} objects.",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "customGeolocation": {
            "title": "Custom Geolocation (coordinates / radius / polygon)",
            "type": "object",
            "description": "Search an EXACT area without geocoding a place name. Provide either a point + radius: {\"lat\": 40.7128, \"lng\": -74.006, \"radiusMeters\": 3000}, or a GeoJSON geometry: {\"type\": \"Polygon\", \"coordinates\": [[[lng,lat],...]]} (Polygon, MultiPolygon or Point supported). This sets WHERE to search, not WHAT to search: combine it with Search Query, which supplies the category. The area's bounding box is gridded directly."
          },
          "maxResults": {
            "title": "Max Results",
            "minimum": 0,
            "maximum": 100000,
            "type": "integer",
            "description": "Maximum number of businesses to extract. Set to 0 for unlimited, which collects everything found in the area and has no cost ceiling; use a small test run first and check its cost before scaling.",
            "default": 100
          },
          "language": {
            "title": "Language",
            "type": "string",
            "description": "Two-letter language code for result text, e.g. en, es, fr, de, it, pt, ja.",
            "default": "en"
          },
          "zoom": {
            "title": "Zoom Level",
            "minimum": 1,
            "maximum": 21,
            "type": "integer",
            "description": "Google Maps zoom level (1-21). Values 1-10 use the widest, coarsest bounded grid; values 11-15 progressively use a finer grid; values 16-21 use the finest bounded grid. 13 is a good default for city-level searches.",
            "default": 13
          },
          "includeDetails": {
            "title": "Include Place Details",
            "type": "boolean",
            "description": "Fetch detailed info for each business (hours, phone, website, amenities). Slower but more complete.",
            "default": true
          },
          "includePhotos": {
            "title": "Include Photos",
            "type": "boolean",
            "description": "Add a 'photos' array of up to 10 business photo URLs (from the place-details response). Requires 'Include Place Details' to be on.",
            "default": true
          },
          "includePlaceExtras": {
            "title": "Include Amenities, Plus Code & Closed Status",
            "type": "boolean",
            "description": "Add 'amenities' (attribute strings), 'plus_code' (Google Plus Code) and 'permanently_closed' (boolean). Best-effort — fields are null when not present in Google's response. Requires 'Include Place Details'.",
            "default": true
          },
          "includePopularTimes": {
            "title": "Include Popular Times (experimental)",
            "type": "boolean",
            "description": "EXPERIMENTAL / rarely available. Attempts to extract the weekly 'popular_times' busy histogram. Google's HTTP place endpoint almost never includes this data (it is normally loaded by a separate browser-only request), so in practice 'popular_times' is null for virtually all places. Kept for forward-compatibility; do not rely on it. Off by default. Requires 'Include Place Details'.",
            "default": false
          },
          "includeReviews": {
            "title": "Include Reviews",
            "type": "boolean",
            "description": "Fetch each business's Google reviews (the same list as the Reviews tab on Google Maps), with the author, rating, date, text, owner reply, reviewer details and photos. Each delivered review that has an author is billed separately; text-only highlights without an author are never billed. Increases run time and cost.",
            "default": false
          },
          "reviewsLimit": {
            "title": "Reviews per Business",
            "minimum": 1,
            "maximum": 1000,
            "type": "integer",
            "description": "Maximum number of reviews to fetch per business (only used when Include Reviews is enabled). The actor pages through the business's review list until this many reviews are collected (counting only reviews that pass the rating and keyword filters, if set) or the list ends. If a business's full list cannot be fetched, the run's status message says so.",
            "default": 5
          },
          "minReviewRating": {
            "title": "Minimum Review Rating",
            "minimum": 0,
            "maximum": 5,
            "type": "integer",
            "description": "Keep only reviews rated at or above this many stars (1-5). 0 = keep all ratings (off). Reviews with no rating are always kept. Applied while paging, so 'Reviews per Business' counts matching reviews; with a rating or keyword filter on, at most the first 2,000 reviews of each business are scanned (if that limit is reached before enough reviews match, the run's status message says so).",
            "default": 0
          },
          "reviewKeyword": {
            "title": "Review Keyword Filter",
            "type": "string",
            "description": "Keep only reviews whose text contains this phrase (case-insensitive substring). Leave empty to keep all reviews. Example: 'refund', 'rude', 'clean'. Applied while paging, so 'Reviews per Business' counts matching reviews; with a rating or keyword filter on, at most the first 2,000 reviews of each business are scanned (if that limit is reached before enough reviews match, the run's status message says so).",
            "default": ""
          },
          "reviewsSort": {
            "title": "Review Sort Order",
            "enum": [
              "newest",
              "relevant"
            ],
            "type": "string",
            "description": "Review ordering: 'Newest first' returns the most recent reviews first; 'Most relevant' uses Google's default order (the order shown first on Google Maps).",
            "default": "newest"
          },
          "includeEmails": {
            "title": "Find Email Addresses (experimental add-on)",
            "type": "boolean",
            "description": "EXPERIMENTAL / BETA. Optional lead-enrichment add-on. Visits each business website (plus its contact / about / imprint pages) and extracts email addresses. Adds run time. Billed separately for each distinct delivered address on the business's own website domain or on a free email provider (such as Gmail, Outlook or Yahoo); other addresses found on its pages (for example a web agency's, a vendor's or another location's mailbox) are still returned but not billed. A business whose Maps website is a social profile, directory, site-builder, booking or ordering page, or a store page on a shopping centre's or landlord's site, has no domain of its own, so only its free-provider addresses are billed. Off by default — the base scraper is unaffected.",
            "default": false
          },
          "includeSocials": {
            "title": "Find Social Media Profiles (experimental add-on)",
            "type": "boolean",
            "description": "EXPERIMENTAL / BETA. Optional lead-enrichment add-on. Extracts social profile links (Facebook, Instagram, LinkedIn, X/Twitter, YouTube, TikTok, WhatsApp) from each business website. Adds run time and is billed separately for each distinct delivered profile URL. The official accounts of website builders and of booking, listing and ordering platforms (for example Wix or Booksy) found in page templates and footers are not returned as the business's. A link to one of a profile's tabs is returned as the profile itself, and so is a Facebook link to a post, photo, video or event of a page (its page profile); a link to a single Instagram post or reel, X post, TikTok or YouTube video is not a profile, and neither is a WhatsApp share button. Off by default.",
            "default": false
          },
          "emailOnly": {
            "title": "Only Keep Businesses With an Email",
            "type": "boolean",
            "description": "Filter: only output businesses for which at least one email address was found. Automatically enables email finding, which is billed for each distinct delivered address on the business's own website domain or on a free email provider; other addresses are returned but not billed.",
            "default": false
          },
          "socialOnly": {
            "title": "Only Keep Businesses With a Social Profile",
            "type": "boolean",
            "description": "Filter: only output businesses for which at least one social profile was found. Automatically enables social profile finding, which is billed for each distinct delivered profile URL. A profile that is itself the business's Google Maps website, also a link to one of its tabs or posts that names the account, counts and is not billed.",
            "default": false
          },
          "onlyWithWebsite": {
            "title": "Only Keep Businesses With a Website",
            "type": "boolean",
            "description": "Filter: only output businesses that have a real website (excludes those with only a social page, a directory listing, or no site at all).",
            "default": false
          },
          "onlyWithoutWebsite": {
            "title": "Only Keep Businesses Without a Website",
            "type": "boolean",
            "description": "Filter: only output businesses that do NOT have a real website — prime prospects for web-design and digital agencies.",
            "default": false
          },
          "maxPagesPerSite": {
            "title": "Max Pages per Website (advanced)",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Advanced: max pages to crawl on each business website during lead/people enrichment (home + contact/about/team pages). Higher finds more contacts but is slower. Defaults to 4 (auto-raised to 6 when 'Find People / Contacts' is on)."
          },
          "includePersonnel": {
            "title": "Find People / Contacts (experimental add-on)",
            "type": "boolean",
            "description": "EXPERIMENTAL / BETA. Optional lead-enrichment add-on. Visits each business website and extracts the PEOPLE who work there — name, title, and their own email / phone / LinkedIn where published — as a 'contacts' list, plus a structured 'business_lead'. Only the business's own site is used; names are not taken from page footers, menus, lists of links or buttons, and the business's own phone, email and social profiles stay in 'business_lead' and are never shown as a person's. Billed once per delivered business with at least one proven person: someone with their own email, LinkedIn or direct phone, or someone with a role title (or Dr. / Prof. or credentials) in a real staff list (a team grid, a staff page, their own profile page or the site's structured-data record for that person). Names from page headings, single paragraphs, testimonials or the business's own record are never billed; institutions, practice-area headings and business names are not people. Product, class, award, brand, office, service-area and department cards are not staff either. Any emails or social profiles found on the way are still returned in the row, but they are billed only if 'Find Email Addresses' or 'Find Social Media Profiles' is turned on. Off by default.",
            "default": false
          },
          "maxContactsPerBusiness": {
            "title": "Max Contacts per Business",
            "minimum": 1,
            "maximum": 100,
            "type": "integer",
            "description": "Maximum personnel contacts to output per business (highest-confidence first; within a confidence tier, people who earn the People charge come first, then people with their own personal-name evidence such as Dr., credentials or their own email; one person who earns the charge is always kept when any passes the confidence floor). Only used when 'Find People / Contacts' is enabled.",
            "default": 10
          },
          "personnelMinConfidence": {
            "title": "Minimum Contact Confidence",
            "enum": [
              "low",
              "medium",
              "high"
            ],
            "type": "string",
            "description": "Drop personnel contacts below this confidence tier. 'low' keeps everything (recall-first); raise to 'medium' or 'high' for precision. Only used when 'Find People / Contacts' is enabled.",
            "default": "low"
          },
          "onlyWithPersonnel": {
            "title": "Only Keep Businesses With a Contact",
            "type": "boolean",
            "description": "Filter: only output businesses for which at least one named person was found. Automatically enables 'Find People / Contacts', billed once per delivered business with at least one proven person (their own email, LinkedIn or direct phone, or a role title in a real staff list); emails and social profiles found on the way are not billed.",
            "default": false
          },
          "includeMetaAds": {
            "title": "Find Meta (Facebook / Instagram) Ads (experimental add-on)",
            "type": "boolean",
            "description": "EXPERIMENTAL / BETA. Ad-intelligence add-on. Checks the public Meta Ad Library (https://www.facebook.com/ads/library) for each business and reports whether they are actively running ads on Facebook / Instagram — plus the active-ad count and a few sample creatives (call-to-action, landing page, ad copy). A strong buy signal for agencies. Matches conservatively on the business's Facebook page, or on an exact page name that is backed by the business's own website or Facebook link, so unrelated advertisers are not falsely attributed. Adds run time, uses residential proxy, billed separately for each business whose own advertiser is identified (including one with no active ads). A search that finds nothing, or only ads that cannot be attributed to the business, returns null and is not billed. Off by default.",
            "default": false
          },
          "includeGoogleAds": {
            "title": "Find Google Ads (experimental add-on)",
            "type": "boolean",
            "description": "EXPERIMENTAL / BETA. Ad-intelligence add-on. Checks the public Google Ads Transparency Center (https://adstransparency.google.com) for each business website and reports whether they are actively running ads on Google — Search, YouTube and Display — plus the advertiser name, ad count, when ads were last shown, and sample creatives. Looked up on the business's own website domain. The advertiser is attributed when its name matches the business or its domain, or when it is the only advertiser on that domain and is not a known agency, retail-media brand or ordering/booking platform (this covers owner-run personal and parent-company accounts). Ads that cannot be attributed (for example an agency, a brand selling through a retailer, an ordering-platform tenant, or several unrelated advertisers sharing the domain) return null and are not billed. Adds run time, billed separately for each completed website-backed business check whose advertiser is attributed to the business, including a verified no-match. Off by default.",
            "default": false
          },
          "onlyRunningAds": {
            "title": "Only Keep Businesses Currently Running Ads",
            "type": "boolean",
            "description": "Filter: only output businesses that are actively running ads. If neither 'Find Meta Ads' nor 'Find Google Ads' is selected, both are checked; otherwise only the selected source(s). Keeps any business currently advertising on an enabled platform. The ad checks it runs are billed for each business it outputs, exactly as if you had selected them.",
            "default": false
          },
          "adCountry": {
            "title": "Ad Library Country",
            "type": "string",
            "description": "Two-letter ISO country code for the ad lookups (where the ads are shown), e.g. US, GB, DE. Applies to both the Meta Ad Library search and the Google Ads Transparency Center region. Defaults to US. Used when an ad-intelligence add-on is enabled.",
            "default": "US"
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Required for reliable results. Google blocks datacenter IPs on Maps search, so this defaults to Apify Residential proxy. Leave as-is unless you know what you're doing.",
            "default": {
              "useApifyProxy": true,
              "apifyProxyGroups": [
                "RESIDENTIAL"
              ]
            }
          },
          "proxyUrl": {
            "title": "Custom proxy URL (advanced, optional)",
            "type": "string",
            "description": "Optional. A single HTTP proxy URL (http://user:pass@host:port) to use INSTEAD of Apify Proxy above. Leave empty to use the Proxy configuration. The proxy is tested before the run starts, with up to 3 attempts on fresh connections; the run stops with a message, and collects nothing, only if the proxy rejects its credentials (HTTP 407) or every attempt fails the same way."
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}