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

146 lines
5.4 KiB
Markdown

# 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`.
## Google Maps Search
```python
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
```python
params = {"placeId": "ChIJFU2bda4SM4cRKSCRyb6pOB8"}
```
Returns full place detail — coordinates, hours by day, phone, website, popular times, attributes (delivery, dine-in), photo summary.
## Google Maps Reviews
```python
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
```python
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
```python
# 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:
```python
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.
```python
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.**