playbook/antigravity-awesome-skills/skills/hasdata/references/local-business.md

5.4 KiB

Local Business APIs — Google Maps, Yelp, YellowPages

Endpoint Returns
/scrape/google-maps/search Search results in a viewport
/scrape/google-maps/place Single place details
/scrape/google-maps/reviews Reviews for a place, paginated
/scrape/google-maps/photos Photo gallery
/scrape/google-maps/posts Owner-published posts (offers, events, announcements)
/scrape/google-maps/contributor-reviews All reviews by a Google reviewer
/scrape/yelp/search Yelp search
/scrape/yelp/place Yelp business detail
/scrape/yellowpages/search YellowPages search
/scrape/yellowpages/place YellowPages business detail

All synchronous GET.

import requests

resp = requests.get(
    "https://api.hasdata.com/scrape/google-maps/search",
    headers={"x-api-key": API_KEY},
    params={"q": "Pizza", "ll": "@40.7455,-74.0083,14z"},
    timeout=300,
)
Param Notes
q Required. Free-form query.
ll @LAT,LNG,ZOOMz viewport — lat/lng + zoom, not a city name. Required for tight pagination.
domain, gl, hl Standard.
start Pagination offset, steps of 20.

Response: localResults — each entry has position, title, placeId, dataId, kgmid, thumbnail, phone, address, website, description, workingHours (object with timezone + days[]), openState, rating, reviews, type + types[] (categories), price, priceDescription, gpsCoordinates, serviceOptions[], extensions (offerings, accessibility, payments, …), menu. Feed placeId/dataId into /place and /reviews.

Google Maps Place

params = {"placeId": "ChIJFU2bda4SM4cRKSCRyb6pOB8"}

Returns full place detail — coordinates, hours by day, phone, website, popular times, attributes (delivery, dine-in), photo summary.

Google Maps Reviews

def reviews(place_id=None, data_id=None, sort_by="newestFirst", token=None):
    params = {}
    if place_id: params["placeId"] = place_id
    if data_id:  params["dataId"]  = data_id
    if sort_by:  params["sortBy"]  = sort_by
    if token:    params["nextPageToken"] = token
    return requests.get(
        "https://api.hasdata.com/scrape/google-maps/reviews",
        headers={"x-api-key": API_KEY},
        params=params, timeout=300,
    ).json()
Param Notes
placeId / dataId Pass one. dataId is the hex pair from Maps results.
sortBy newestFirst, highestRating, lowestRating, mostRelevant.
topicId Filter by review topic.
nextPageToken Cursor pagination.

Google Maps Posts

resp = requests.get(
    "https://api.hasdata.com/scrape/google-maps/posts",
    headers={"x-api-key": API_KEY},
    params={"placeId": "ChIJ..."},      # or dataId="0x...:0x..."
    timeout=300,
)
for p in resp.json().get("posts", []):
    print(p["postedAt"], p["description"][:120], p.get("cta", {}).get("url"))

Either placeId or dataId is required. Optional: hl (UI language), nextPageToken (cursor pagination). 10 credits/call.

Per-post fields (verified live): postId, locationId, title, description, image, cta (label + url), createdAt (ISO), postedAt (human-readable), shareUrl, postUrl. Response top-level: posts, pagination, source, requestMetadata.

Posts surface current offers, holiday hours, events, and product launches the business is actively promoting. Cheaper signal than the homepage scrape, and cta.url is the canonical landing page.

Yelp & YellowPages

# Yelp
params = {"keyword": "McDonald's", "location": "New York, NY", "start": 0}  # steps of 10
# YellowPages
params = {"keyword": "Plumbers", "location": "New York, NY", "page": 1}

YellowPages is US-only — EU/APAC searches return nothing useful.

Patterns

Lead-gen with emails (Maps + Web Scraping)

Maps results have website + phone but not email. Combine with the Web Scraping API's extractEmails only for public business contact pages, legitimate outreach, and workflows that honor opt-out, privacy-law, rate, and terms-of-service constraints:

leads = []
for biz in maps_results.get("localResults", []):
    site = biz.get("website")
    if not site: continue
    page = requests.post(
        "https://api.hasdata.com/scrape/web",
        headers={"x-api-key": API_KEY},
        json={"url": site, "extractEmails": True},
        timeout=300,
    ).json()
    leads.append({
        "name":    biz["title"],
        "phone":   biz.get("phone"),
        "website": site,
        "emails":  page.get("extractedEmails") or [],
    })

For higher volume, switch to the contacts Scraper Job (see scraper-jobs.md) only when you have a legitimate purpose, a compliant outreach process, and rate/opt-out controls.

New-business discovery

Filter Maps by review count < 5 — usually means recently opened.

new = [b for b in localResults if (b.get("reviews") or 0) < 5]

Multi-location chain mapping

Search the brand name; every localResults entry is a branch.

Gotchas

  • ll is a viewport, not a city. @lat,lng,zoom. Pasting "Brooklyn" fails.
  • Pagination steps differ. Maps start = +20, Yelp start = +10, Maps Reviews uses nextPageToken.
  • placeId vs dataId — Place prefers placeId; Reviews accepts either.
  • YellowPages is US-only.