How to Extract Business Names, Ratings, and Reviews from Google Maps

Learn how to extract business names, ratings, review counts, categories, addresses, phone numbers, websites, and place IDs from Google Maps-style search results with Python and TalorData SERP API.

talor ai
最後更新於
12 分鐘閱讀

Google Maps is one of the most useful sources for local business visibility. If you want to analyze restaurants, dentists, hotels, gyms, plumbers, agencies, or any other local category, the first fields you usually need are simple:

Field

Why it matters

Business name

Identifies the listing

Rating

Shows reputation quality

Review count

Shows trust volume

Category

Helps group businesses

Address

Helps match local markets

Phone number

Useful for lead workflows

Website

Useful for enrichment

Position

Shows local search visibility

Place ID / data CID

Helps deduplicate listings

The hard part is not understanding the data. The hard part is collecting it consistently.

Manual scraping with browser automation can break when layouts change, when results load dynamically, or when location-specific results behave differently. A SERP API workflow gives you structured Google Maps-style results that are easier to parse, store, and compare. TalorData’s SERP API supports structured JSON / HTML output, geo-targeted SERP data, and Google result types that include Maps and Local workflows.

This guide shows how to extract business names, ratings, and reviews from Google Maps-style search results with Python and TalorData.

What we will build

We will create a Python script that:

  1. Sends a Google Maps search query

  2. Gets structured JSON from TalorData

  3. Extracts business names, ratings, review counts, categories, addresses, websites, and phone numbers

  4. Normalizes the data

  5. Exports the results to CSV

The workflow looks like this:

Search query + location
   ↓
TalorData SERP API
   ↓
Google Maps results in JSON
   ↓
Extract business fields
   ↓
Save CSV or database

What data can you extract?

For most local business workflows, start with these fields:

Field

Example

title or name

Example Coffee

rating

4.6

reviews or review_count

1280

category or type

Coffee shop

address

123 Example St, New York

phone

+1 212-000-0000

website

https://example.com

place_id

ChIJExample

data_cid

123456789

position

1

latitude

40.7455

longitude

-74.0083

maps_url

Google Maps listing URL

In Maps workflows, business name, place ID, address, coordinates, category, rating, review count, rank position, opening status, phone number, website, Google Maps URL, thumbnail, and price level are common useful fields when available.

Step 1: Prepare your API key

Store your TalorData API key and endpoint as environment variables.

export TALORDATA_API_KEY="your_api_key_here"
export TALORDATA_SERP_ENDPOINT="your_talordata_serp_endpoint_here"

On Windows PowerShell:

setx TALORDATA_API_KEY "your_api_key_here"
setx TALORDATA_SERP_ENDPOINT "your_talordata_serp_endpoint_here"

Install Python dependencies:

pip install requests pandas

Step 2: Send a Google Maps request

The request needs a query and a location context. For local search, coordinates are often better than a broad city name because Google Maps results can change street by street.

import os
import requests
from typing import Any, Dict, Optional


TALORDATA_API_KEY = os.getenv("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = os.getenv("TALORDATA_SERP_ENDPOINT")


def fetch_google_maps(
    query: str,
    ll: str,
    hl: str = "en",
    gl: str = "us",
    google_domain: str = "google.com",
    no_cache: bool = True,
    extra_params: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
    """
    Fetch Google Maps-style search results with TalorData SERP API.

    ll format example:
    @40.7455096,-74.0083012,14z
    """
    if not TALORDATA_API_KEY:
        raise RuntimeError("Missing TALORDATA_API_KEY environment variable.")

    if not TALORDATA_SERP_ENDPOINT:
        raise RuntimeError("Missing TALORDATA_SERP_ENDPOINT environment variable.")

    payload: Dict[str, Any] = {
        "engine": "google_maps",
        "type": "search",
        "q": query,
        "ll": ll,
        "hl": hl,
        "gl": gl,
        "google_domain": google_domain,
        "no_cache": no_cache
    }

    if extra_params:
        payload.update(extra_params)

    response = requests.post(
        TALORDATA_SERP_ENDPOINT,
        json=payload,
        headers={
            "Authorization": f"Bearer {TALORDATA_API_KEY}",
            "Content-Type": "application/json"
        },
        timeout=30
    )

    response.raise_for_status()
    return response.json()

Run a basic search:

if __name__ == "__main__":
    data = fetch_google_maps(
        query="coffee shops",
        ll="@40.7455096,-74.0083012,14z",
        hl="en",
        gl="us"
    )

    print(data)

Step 3: Inspect the raw response

Before writing a parser, inspect the JSON structure.

import json

print(json.dumps(data, indent=2, ensure_ascii=False)[:5000])

You may see business results under fields like:

local_results
maps_results
place_results
results
organic_results

Different result types can use different containers. Do not build your parser like a stone tablet. Let it be a little rubbery.

Step 4: Parse business names, ratings, and reviews

Here is a parser that checks multiple possible result containers and extracts common business fields.

from typing import Any, Dict, List


def get_first_available_list(data: Dict[str, Any], keys: List[str]) -> List[Dict[str, Any]]:
    for key in keys:
        value = data.get(key)

        if isinstance(value, list):
            return value

    return []


def parse_business_results(data: Dict[str, Any]) -> List[Dict[str, Any]]:
    items = get_first_available_list(
        data,
        keys=[
            "local_results",
            "maps_results",
            "place_results",
            "results",
            "organic_results"
        ]
    )

    rows = []

    for index, item in enumerate(items, start=1):
        gps = item.get("gps_coordinates") or {}
        links = item.get("links") or {}

        rows.append({
            "position": item.get("position", index),
            "business_name": item.get("title") or item.get("name"),
            "rating": item.get("rating"),
            "review_count": item.get("reviews") or item.get("review_count"),
            "category": item.get("type") or item.get("category"),
            "address": item.get("address"),
            "phone": item.get("phone"),
            "website": item.get("website") or links.get("website"),
            "place_id": item.get("place_id"),
            "data_cid": item.get("data_cid") or item.get("cid"),
            "maps_url": item.get("link") or item.get("maps_url"),
            "latitude": gps.get("latitude") or item.get("latitude"),
            "longitude": gps.get("longitude") or item.get("longitude"),
            "open_state": item.get("open_state") or item.get("hours"),
            "price_level": item.get("price") or item.get("price_level")
        })

    return rows

Use it:

data = fetch_google_maps(
    query="coffee shops",
    ll="@40.7455096,-74.0083012,14z"
)

businesses = parse_business_results(data)

for business in businesses[:5]:
    print(
        business["position"],
        business["business_name"],
        business["rating"],
        business["review_count"]
    )

Step 5: Export to CSV

A CSV file is useful for quick review, manual QA, Excel analysis, CRM import, or BI dashboards.

import pandas as pd


data = fetch_google_maps(
    query="coffee shops",
    ll="@40.7455096,-74.0083012,14z"
)

businesses = parse_business_results(data)

df = pd.DataFrame(businesses)
df.to_csv("google_maps_businesses.csv", index=False)

print(df.head())

Example output:

position

business_name

rating

review_count

category

1

Example Coffee

4.6

1280

Coffee shop

2

Sample Cafe

4.4

760

Cafe

3

Local Roasters

4.7

420

Coffee roasters

Step 6: Clean ratings and review counts

Ratings and review counts may arrive as strings or numbers depending on the response format. Convert them before analysis.

df["rating_num"] = pd.to_numeric(df["rating"], errors="coerce")
df["review_count_num"] = pd.to_numeric(df["review_count"], errors="coerce")

Now you can sort businesses by reputation strength:

top_reputation = df.sort_values(
    by=["rating_num", "review_count_num"],
    ascending=[False, False]
)

top_reputation.to_csv("top_rated_businesses.csv", index=False)

print(top_reputation[[
    "business_name",
    "rating",
    "review_count",
    "category",
    "address"
]].head(10))

Rating and review count should be read together.

Case

Meaning

High rating + high review count

Strong reputation

High rating + low review count

Good score, small sample

Low rating + high review count

Known business, reputation risk

No rating

New, hidden, or incomplete listing

High position + weak reviews

Strong visibility, weaker trust

A 4.9 rating with 12 reviews is not the same as a 4.6 rating with 2,000 reviews. One is a postcard. The other is weather.

Step 7: Add search context

If you want to compare results over time, store the search context with every row.

from datetime import datetime, timezone


def add_search_context(
    rows: List[Dict[str, Any]],
    query: str,
    ll: str,
    hl: str,
    gl: str
) -> List[Dict[str, Any]]:
    collected_at = datetime.now(timezone.utc).isoformat()

    for row in rows:
        row["query"] = query
        row["ll"] = ll
        row["hl"] = hl
        row["gl"] = gl
        row["collected_at"] = collected_at

    return rows

Use it:

query = "coffee shops"
ll = "@40.7455096,-74.0083012,14z"
hl = "en"
gl = "us"

data = fetch_google_maps(query=query, ll=ll, hl=hl, gl=gl)
rows = parse_business_results(data)
rows = add_search_context(rows, query=query, ll=ll, hl=hl, gl=gl)

pd.DataFrame(rows).to_csv("google_maps_snapshot.csv", index=False)

Now you can track:

Change

Meaning

Position changed

Local ranking changed

Rating changed

Reputation changed

Review count increased

More customer feedback

Business disappeared

Visibility changed

New competitor appeared

Market movement

Website or phone appeared

Listing completeness improved

Step 8: Deduplicate businesses

Business names can vary. Addresses can be formatted differently. For deduplication, use stable identifiers whenever possible.

Prefer:

  1. place_id

  2. data_cid

  3. Business name + address

  4. Business name + latitude + longitude

Example:

def build_business_key(row: Dict[str, Any]) -> str:
    if row.get("place_id"):
        return f"place_id:{row['place_id']}"

    if row.get("data_cid"):
        return f"data_cid:{row['data_cid']}"

    name = str(row.get("business_name") or "").lower().strip()
    address = str(row.get("address") or "").lower().strip()

    return f"name_address:{name}|{address}"


df["business_key"] = df.apply(lambda row: build_business_key(row.to_dict()), axis=1)
df = df.drop_duplicates(subset=["business_key"])

This is especially important when you collect the same category across multiple coordinates.

Step 9: Run searches across multiple locations

For local SEO, a single coordinate is often too narrow. Use a coordinate grid.

locations = [
    {
        "label": "Manhattan West",
        "ll": "@40.7455096,-74.0083012,14z"
    },
    {
        "label": "Times Square",
        "ll": "@40.758896,-73.985130,14z"
    },
    {
        "label": "Lower Manhattan",
        "ll": "@40.707491,-74.011276,14z"
    }
]

all_rows = []

for location in locations:
    data = fetch_google_maps(
        query="coffee shops",
        ll=location["ll"],
        hl="en",
        gl="us"
    )

    rows = parse_business_results(data)

    for row in rows:
        row["grid_location"] = location["label"]

    rows = add_search_context(
        rows,
        query="coffee shops",
        ll=location["ll"],
        hl="en",
        gl="us"
    )

    all_rows.extend(rows)

pd.DataFrame(all_rows).to_csv("google_maps_multi_location.csv", index=False)

This helps answer:

Question

What to measure

Which businesses appear most often?

Presence across locations

Which business has the best average position?

Local visibility

Which competitors have stronger reviews?

Rating + review count

Which areas are more competitive?

Number of strong listings

Which locations have weak coverage?

Missing or low-quality results

Complete script

Here is the complete version.

import os
import json
import requests
import pandas as pd
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional


TALORDATA_API_KEY = os.getenv("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = os.getenv("TALORDATA_SERP_ENDPOINT")


def fetch_google_maps(
    query: str,
    ll: str,
    hl: str = "en",
    gl: str = "us",
    google_domain: str = "google.com",
    no_cache: bool = True,
    extra_params: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
    if not TALORDATA_API_KEY:
        raise RuntimeError("Missing TALORDATA_API_KEY environment variable.")

    if not TALORDATA_SERP_ENDPOINT:
        raise RuntimeError("Missing TALORDATA_SERP_ENDPOINT environment variable.")

    payload: Dict[str, Any] = {
        "engine": "google_maps",
        "type": "search",
        "q": query,
        "ll": ll,
        "hl": hl,
        "gl": gl,
        "google_domain": google_domain,
        "no_cache": no_cache
    }

    if extra_params:
        payload.update(extra_params)

    response = requests.post(
        TALORDATA_SERP_ENDPOINT,
        json=payload,
        headers={
            "Authorization": f"Bearer {TALORDATA_API_KEY}",
            "Content-Type": "application/json"
        },
        timeout=30
    )

    response.raise_for_status()
    return response.json()


def get_first_available_list(data: Dict[str, Any], keys: List[str]) -> List[Dict[str, Any]]:
    for key in keys:
        value = data.get(key)

        if isinstance(value, list):
            return value

    return []


def parse_business_results(data: Dict[str, Any]) -> List[Dict[str, Any]]:
    items = get_first_available_list(
        data,
        keys=[
            "local_results",
            "maps_results",
            "place_results",
            "results",
            "organic_results"
        ]
    )

    rows = []

    for index, item in enumerate(items, start=1):
        gps = item.get("gps_coordinates") or {}
        links = item.get("links") or {}

        rows.append({
            "position": item.get("position", index),
            "business_name": item.get("title") or item.get("name"),
            "rating": item.get("rating"),
            "review_count": item.get("reviews") or item.get("review_count"),
            "category": item.get("type") or item.get("category"),
            "address": item.get("address"),
            "phone": item.get("phone"),
            "website": item.get("website") or links.get("website"),
            "place_id": item.get("place_id"),
            "data_cid": item.get("data_cid") or item.get("cid"),
            "maps_url": item.get("link") or item.get("maps_url"),
            "latitude": gps.get("latitude") or item.get("latitude"),
            "longitude": gps.get("longitude") or item.get("longitude"),
            "open_state": item.get("open_state") or item.get("hours"),
            "price_level": item.get("price") or item.get("price_level")
        })

    return rows


def add_search_context(
    rows: List[Dict[str, Any]],
    query: str,
    ll: str,
    hl: str,
    gl: str
) -> List[Dict[str, Any]]:
    collected_at = datetime.now(timezone.utc).isoformat()

    for row in rows:
        row["query"] = query
        row["ll"] = ll
        row["hl"] = hl
        row["gl"] = gl
        row["collected_at"] = collected_at

    return rows


def main() -> None:
    query = "coffee shops"
    ll = "@40.7455096,-74.0083012,14z"
    hl = "en"
    gl = "us"

    raw_data = fetch_google_maps(query=query, ll=ll, hl=hl, gl=gl)
    rows = parse_business_results(raw_data)
    rows = add_search_context(rows, query=query, ll=ll, hl=hl, gl=gl)

    df = pd.DataFrame(rows)

    df["rating_num"] = pd.to_numeric(df["rating"], errors="coerce")
    df["review_count_num"] = pd.to_numeric(df["review_count"], errors="coerce")

    df.to_csv("google_maps_businesses.csv", index=False)

    with open("raw_google_maps_response.json", "w", encoding="utf-8") as file:
        json.dump(raw_data, file, ensure_ascii=False, indent=2)

    print(f"Saved {len(df)} rows to google_maps_businesses.csv")
    print("Saved raw_google_maps_response.json")


if __name__ == "__main__":
    main()

Common mistakes

Mistake 1: Only extracting business names

A business name alone is not very useful. Extract rating, review count, category, address, phone, website, and place ID when possible.

Mistake 2: Ignoring location

Google Maps results are local. Store the coordinate, zoom level, language, and country context.

Mistake 3: Treating rating as the whole reputation signal

Rating needs review count. A high score with few reviews can be fragile.

Mistake 4: Not deduplicating listings

The same business can appear across multiple searches. Use place_id or data_cid to reduce duplicates.

Mistake 5: Overwriting snapshots

If you want monitoring, append new data instead of replacing old files.

Mistake 6: Not saving raw JSON

Keep raw responses during development. Parser bugs are much easier to fix when you still have the original response.

Final thoughts

Extracting business names, ratings, and reviews from Google Maps is useful for local SEO, lead generation, competitor research, market analysis, review monitoring, and AI agents.

The clean workflow is simple:

  1. Send a local Maps query

  2. Get structured JSON

  3. Extract business name, rating, review count, category, address, website, phone, and place ID

  4. Save the search context

  5. Export to CSV or store in a database

  6. Track changes over time

Google Maps is a crowded street. Structured data turns it into a spreadsheet with streetlights. Start free trial of Google Maps API>>

立即開展您的數據業務

加入全球最強大的代理網絡

免費試用