API Reference

Google Maps data, one request away

A ready-to-use REST API for Google Maps. Search a point for its local businesses, resolve any place by its place_id into its full record, pull the complete review set, or rank a business across a local search grid, all matched by stable place id, billed only for successful calls.

https://api.hexscraper.comAuth · API keyJSON
Overview

Introduction

The Hexscraper API is a synchronous request/response service: you call an endpoint, you get the data back in the same HTTP response, no queue or webhook to manage. Every business is addressed by its Google place_id (a CID-backed ChIJ… id), so results are never confused between same-name look-alikes.

All responses are JSON. The base URL is https://api.hexscraper.com.

Security

Authentication

Authenticate every request with an API key in the Authorization header as a Bearer token. Keys are issued per account, so keep them server-side and never ship one to a browser.

Authorization header
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx

A missing or invalid key returns 401.

Pricing

Billing & credits

Usage is metered in credits. Billing is reserve-then-refund: a call reserves its credits up front and refunds anything unused, so you only pay for successful results. A 4xx, a 503, or an upstream failure is never charged.

EndpointCost
GET /v1/search1 credit per 100 businesses returned (minimum 1); empty searches are free
GET /v1/places/{id}1 credit per lookup
GET /v1/places/{id}/reviews1 credit per 10 reviews returned (min 1)
GET /v1/places/{id}/photos1 credit per 100 photos returned (minimum 1)
GET /v1/visibilitygrid_size² credits (e.g. a 5×5 grid = 25)

A cache hit still bills (it saves you the latency, not the credit). Running out of credits returns 402.

Limits

Rate limits

Limits are per account, over a fixed one-minute window: 240/min for point search, place lookups and the visibility grid, 180/min for reviews (each reviews call can page a lot of data). Exceeding a limit returns 429 with a Retry-After header, and is never billed.

Reliability

Idempotency

Send an Idempotency-Key header on any request. A retry carrying the same key replays the stored response without re-charging or re-scraping; a concurrent duplicate gets 409 while the first is still running.

Idempotent retry
Idempotency-Key: 9f1c2b7a-restaurant-batch-042
Reference

Errors

Errors use standard HTTP status codes and a JSON body { "detail": "…" }. A 503 means the data was temporarily unavailable (not that the place doesn't exist). Retry after the Retry-After delay; it is never billed.

StatusMeaning
400Malformed place_id (not a CID-backed ChIJ… id) or invalid parameters.
401Missing or invalid API key.
402Out of credits. Top up to continue.
409A request with the same Idempotency-Key is already in progress.
429Rate limit exceeded. Honour the Retry-After header. Not billed.
503Temporarily unavailable (upstream/farm). Retry after Retry-After. Not billed.

Query parameters

FieldTypeDescription
keywordrequiredstringThe search term, e.g. "tapas" or "dentist". URL-encode spaces and accents (a space becomes %20).
latrequirednumberLatitude of the search centre (−90 to 90).
lonrequirednumberLongitude of the search centre (−180 to 180).
limitintegerHow many businesses to return, 1 to 400 (default 100). The list Google keeps for a search usually ends before 400: 170 to 340 in a city centre. More takes longer: about 1.2 s for 100 and 2 to 4 s for 400.
hlstringGoogle Maps host language for the response, e.g. "es" or "pt-BR". Categories, attributes and price formatting come back in that language, and they are Google's own strings, not a translation. Defaults to English.

Request

curl "https://api.hexscraper.com/v1/search?keyword=tapas&lat=36.3707&lon=-6.1809" \
  -H "Authorization: Bearer $HEXSCRAPER_API_KEY"

Response

200 OK · application/json
{
  "keyword": "tapas",
  "latitude": 36.3707,
  "longitude": -6.1809,
  "count": 20,
  "results": [
    {
      "place_id": "ChIJx3-pvNszDA0R837NptBLjM4",
      "name": "Café bar la Almadraba",
      "rating": 4.6,
      "reviews_count": 1071,
      "price_level": "€10-20",
      "phone": null,
      "website": null,
      "address": "Avenida la Barrosa, 11130 Chiclana, Cádiz",
      "city": "Chiclana",
      "latitude": 36.3707792,
      "longitude": -6.180956,
      "categories": ["Restaurante", "Bar", "Pub"],
      "primary_category": "Restaurante",
      "business_status": "Open"
    }
  ]
}

An empty result (no businesses at that point) returns count: 0 and is not billed. For richer fields on a single business (amenities, popular times, the full review set), feed a result's place_id into the endpoints below.


Place details

1 credit
GET/v1/places/{place_id}
Turns a place_id into its full public record: name, rating, review count and per-star histogram, price, contact details, the address in parts (street, neighbourhood, city, province, postcode, Plus Code), opening hours, status (open, temporarily or permanently closed), categories, popular times, photo count and photo tabs, the "About" tab (attributes: dine-in, takeout, delivery, outdoor seating, accessibility… each yes or no), the business's latest posts, the menu, the book, order and menu buttons (action_links, each with its kind), related places, the review keyword cloud and a sample of the most relevant reviews.

Path parameters

FieldTypeDescription
place_idrequiredstringA CID-backed Google place_id (ChIJ…).

Query parameters

FieldTypeDescription
hlstringGoogle Maps host language for the response, e.g. "es" or "pt-BR". Defaults to English.
freshbooleantrue to skip the cache and read Google now (the fresh answer is still cached for the next call).

Request

curl "https://api.hexscraper.com/v1/places/ChIJx3-pvNszDA0R837NptBLjM4" \
  -H "Authorization: Bearer $HEXSCRAPER_API_KEY"

Response

200 OK · application/json
{
  "place_id": "ChIJx3-pvNszDA0R837NptBLjM4",
  "name": "Café bar la Almadraba",
  "rating": 4.6,
  "reviews_count": 1071,
  "price_level": "€10-20",
  "website": null,
  "phone": null,
  "address": "Avenida la Barrosa, 11130 Chiclana, Cádiz",
  "street": "Avenida la Barrosa",
  "district": null,
  "city": "Chiclana",
  "state": "Cádiz",
  "postal_code": "11130",
  "country_code": "ES",
  "plus_code": "8C8M9RC9+8J",
  "latitude": 36.3707792,
  "longitude": -6.180956,
  "categories": ["Restaurante", "Bar", "Pub"],
  "primary_category": "Restaurante",
  "business_status": "Open",
  "num_photos": 775,
  "is_claimed": true,
  "owner_name": "Café bar la Almadraba",
  "time_spent": "People typically spend 45 min to 1.5 hr here",
  "reviews_tags": [
    { "keyword": "tapas", "count": 83 },
    { "keyword": "fried fish", "count": 12 }
  ],
  "popular_times": { "Friday": [[12, 0], [13, 54], [20, 88], [21, 71]] },
  "people_also_search": [
    { "name": "EL ARBOL TAPAS", "cid": "0x0:0x9f69...", "rating": 4.7, "reviews": 6988 }
  ],
  "rating_summary": [
    { "stars": 5, "amount": 784 },
    { "stars": 4, "amount": 199 }
  ],
  "reviews": [
    {
      "review_id": "ChZ...",
      "author_name": "A VG",
      "review_rating": 2,
      "review_text": "Overpriced for what it is.",
      "review_datetime_utc": "2025-06-29T20:58:02+00:00"
    }
  ],
  "business_description": "Pollos asados y pescado frito en casa, también para llevar.",
  "popular_dishes": [{ "name": "Medio Pollo Con Patatas", "photo": "https://lh3..." }],
  "menu": [
    {
      "section": "Pizzas y pasta",
      "items": [
        { "name": "Pizza Margarita", "description": "Base fina…", "price": "€10.50", "photo": "https://lh3..." }
      ]
    }
  ],
  "photo_categories": ["Del propietario", "Carta", "Comida y bebida", "Ambiente"],
  "posts": [
    { "text": "QUESO GORGONZOLA: origen y sabor…", "date": "Aug 4, 2026", "timestamp": 1785848535, "image": "https://lh3…", "cta_label": "Book", "cta_url": "https://…" }
  ],
  "action_links": [
    { "url": "https://www.google.com/maps/reserve/v/dine/c/…", "label": "Reservar mesa", "kind": "reservation" },
    { "url": "https://glovoapp.com/es/es/chiclana/…", "label": "glovoapp.com", "kind": "order" }
  ],
  "attributes": [
    {
      "key": "service_options",
      "label": "Opciones de servicio",
      "items": [
        { "key": "has_takeout", "label": "Para llevar", "available": true, "text": "Ofrece comida para llevar" },
        { "key": "has_delivery", "label": "Entrega a domicilio", "available": false, "text": "No ofrece servicio de entrega a domicilio" }
      ]
    }
  ]
}

Reviews

1 credit / 10 reviews
GET/v1/places/{place_id}/reviews
Fetch the full review set for a place, well past the handful embedded in a place lookup. Reviews are paginated for you and returned in one response, each in a stable, Outscraper-shaped schema. A place with no reviews returns an empty list (billed 1 credit).

Path parameters

FieldTypeDescription
place_idrequiredstringA CID-backed Google place_id (ChIJ…).

Query parameters

FieldTypeDescription
limitintegerMax reviews to return. Default 10, no hard cap; bounded by the place's actual review count and your credit balance.
sortstringOrder: relevant (Google's, the default), newest, highest or lowest.
page_tokenstringThe next_page_token of an earlier response (same sort and hl) to carry on reading from there.
hlstringGoogle Maps host language for the response, e.g. "es" or "pt-BR". Categories, attributes and price formatting come back in that language, and they are Google's own strings, not a translation. Defaults to English.
freshbooleantrue to skip the cache and read Google now (the fresh answer is still cached for the next call).

Request

curl "https://api.hexscraper.com/v1/places/ChIJTzciJkUxDA0RtK7jOAlZTKo/reviews?limit=50" \
  -H "Authorization: Bearer $HEXSCRAPER_API_KEY"

Response

200 OK · application/json
{
  "place_id": "ChIJTzciJkUxDA0RtK7jOAlZTKo",
  "count": 50,
  "reviews": [
    {
      "review_id": "ChdD...",
      "author_id": "1094...",
      "author_name": "Pilar Pavón",
      "author_link": "https://www.google.com/maps/contrib/1094...",
      "review_rating": 5,
      "review_text": "Absolutely phenomenal food. The garlic prawns...",
      "review_timestamp": 1751803869,
      "review_datetime_utc": "2025-07-06T12:11:09+00:00",
      "relative_date": "2 months ago"
    }
  ]
}

Photos

1 credit / 100 photos
GET/v1/places/{place_id}/photos
A place's photos by category (the tabs of its gallery): all, latest, by the owner, menu, food & drink, vibe, videos, Street View and each dish. Every response carries the total Google gives for the category, the photos with their author, size and upload date, and the categories the place has (categories).

Path parameters

FieldTypeDescription
place_idrequiredstringA CID-backed Google place_id (ChIJ…).

Query parameters

FieldTypeDescription
categorystringall (default), latest, owner, menu, food, vibe, videos, street_view, or a dish tab's key as it comes in categories ("tab:…").
limitintegerMax photos to return, 1 to 500. Default 100.
page_tokenstringThe next_page_token of an earlier response (same category and hl) for the next photos; null when there are no more.
hlstringGoogle Maps host language for the response, e.g. "es" or "pt-BR". Defaults to English.
freshbooleantrue to skip the cache and read Google now (the fresh answer is still cached for the next call).

Request

curl "https://api.hexscraper.com/v1/places/ChIJx3-pvNszDA0R837NptBLjM4/photos?category=owner&limit=100" \
  -H "Authorization: Bearer $HEXSCRAPER_API_KEY"

Response

200 OK · application/json
{
  "place_id": "ChIJx3-pvNszDA0R837NptBLjM4",
  "category": "owner",
  "total": 53,
  "count": 47,
  "photos": [
    {
      "photo_id": "CIHM0ogKEICAgIDhnv2FTg",
      "kind": "photo",
      "url": "https://lh3.googleusercontent.com/gps-cs-s/…=w2945-h2209-k-no",
      "width": 4000,
      "height": 3000,
      "author_name": "Café bar la Almadraba",
      "author_link": "https://maps.google.com/maps/contrib/1028…",
      "uploaded_on": "2021-09-05"
    }
  ],
  "categories": [
    { "key": "all", "label": "Todas" },
    { "key": "menu", "label": "Carta" },
    { "key": "food", "label": "Comida y bebida" },
    { "key": "tab:CgwqCC9tLzAxNTk5MAs", "label": "Cerveza" },
    { "key": "owner", "label": "Del propietario" }
  ],
  "next_page_token": null
}

The total is Google's count and usually runs a little above the photos that can be listed (Google counts some it does not show). For all it is null: the place's photo count is the record's num_photos. A business that never uploaded returns an empty owner category and leaves it out of categories. URLs take a size: replace the trailing =w…-h…-k-no with, e.g., =w1200-h1200-k-no.


Visibility grid

grid_size² credits
GET/v1/visibility
Rank a business for a keyword across an N×N grid of local searches centred on it: a local-visibility report. Every cell is matched by place_id, so a same-name look-alike can never inflate or corrupt the target's rank or the competitor table. Each cell reads the first 100 results, so rank runs from 1 to 100 and null means the business is not among them. Each cell also lists its first 10 results (top_places). The competitor table reads every cell's 100 and is ordered by avg_total_rank: the mean rank over all measured cells, a cell where the business does not appear counted as 101.

Query parameters

FieldTypeDescription
place_idrequiredstringThe business to rank (a CID-backed ChIJ… id).
keywordrequiredstringThe search term to rank for, e.g. "tapas" (URL-encode special characters).
grid_sizeintegerOdd number 3–7. Default 5 (a 5×5 grid = 25 searches).
radius_mintegerGrid half-width in metres, 1–50000. Default 1000. The response echoes it as radius_km.
searcherstringWhere the searcher stands: cell (the default) places them at each cell, like someone with a phone on that spot, which is what geo-grid tools measure; ocean places them nowhere and ranks the map around the point. The response says which in searcher_location.
hlstringGoogle Maps host language for the response, e.g. "es" or "pt-BR". Defaults to English.

Request

curl "https://api.hexscraper.com/v1/visibility?place_id=ChIJx3-pvNszDA0R837NptBLjM4&keyword=tapas&grid_size=5&radius_m=1000" \
  -H "Authorization: Bearer $HEXSCRAPER_API_KEY"

Response

200 OK · application/json
{
  "keyword": "tapas",
  "target_place_id": "ChIJx3-pvNszDA0R837NptBLjM4",
  "target_name": "Café bar la Almadraba",
  "center_lat": 36.3707792,
  "center_lon": -6.180956,
  "grid_size": 5,
  "radius_km": 1.0,
  "total_points": 25,
  "found_in": 18,
  "average_rank": 3.4,
  "grid": [
    { "row": 0, "col": 0, "lat": 36.3752, "lon": -6.1849,
      "rank": 2, "top_result": "Café bar la Almadraba", "total_results": 100,
      "top_places": [
        { "rank": 1, "place_id": "0x0:0x9f69...", "name": "EL ARBOL TAPAS",
          "rating": 4.7, "reviews": 6988, "photo": "https://lh3.googleusercontent.com/…" },
        { "rank": 2, "place_id": "ChIJx3-pvNszDA0R837NptBLjM4", "name": "Café bar la Almadraba",
          "rating": 4.6, "reviews": 1204 }
      ] }
  ],
  "competitors": [
    { "place_id": "0x0:0x9f69...", "name": "EL ARBOL TAPAS",
      "avg_total_rank": 2.3, "appearances": 25, "avg_rank": 2.3, "best_rank": 1, "top3_count": 20,
      "total_reviews": 6988, "avg_rating": 4.7, "is_target": false }
  ],
  "top_rankings": [ { "name": "Café bar la Almadraba", "count": 18 } ],
  "degraded": false,
  "searcher_location": "cell"
}

If the results look degraded (Google blanked review counts on a burned session), the response sets degraded: true. The data may be incomplete and is not cached.


Ready to build?

Get an API key and your first credits to start pulling Google Maps data.

Get your API key