google-maps-api
Google Maps API
Google Maps place search as JSON. Twenty places a page, each with its coordinates, rating, address, opening hours and the identifiers you need to look it up again, searched from a point on the map you choose.
Endpoint
One credit per request. Coordinates are the search origin, not a refinement: send lat and lon together and Google searches outward from that point. Leave them off and it centres wherever the request appears to come from, which is not a place you chose.
curl -G "https://api.serpix.io/v1/maps" \
--data-urlencode "q=coffee" \
--data-urlencode "lat=30.2672" \
--data-urlencode "lon=-97.7431" \
-H "Authorization: Bearer $SERPIX_API_KEY"The same parameters are accepted as a JSON body on POST /v1/maps:
curl -X POST "https://api.serpix.io/v1/maps" \
-H "Authorization: Bearer $SERPIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"q": "coffee", "lat": 30.2672, "lon": -97.7431, "zoom": 14}'Parameters
| field | type | default | description |
|---|---|---|---|
| qrequired | string | – | What to search for, like coffee or hardware store. |
| lat | number | – | Latitude of the map centre. Send with lon; one without the other is a 422. |
| lon | number | – | Longitude of the map centre. Send with lat. |
| zoom | integer | 14 | How much map to cover, 3 for a continent and 21 for a street corner. Defaults to 14, which is roughly a neighbourhood. |
Results
| field | type | default | description |
|---|---|---|---|
| gl | string | – | Country, two-letter code. Defaults to us. |
| hl | string | en | Language of the results, two-letter code. |
| page | integer | 1 | 1-based page, twenty places each. Pages do not overlap: page 2 is twenty different places, not the same list shifted along. One credit per page. |
Rejected parameters
Unknown parameters are never silently ignored. A request carrying one is rejected with 422 before any search runs, so a typo can't bill you for results you didn't ask for. Common parameters from raw Google URLs and other SERP APIs get a pointed message naming the equivalent here:
| field | type | default | description |
|---|---|---|---|
| location | 422 | – | This vertical needs real coordinates, so send lat and lon. The geotarget dataset carries names and countries but no coordinates, so a city name has no centre to resolve to here. |
| ll | 422 | – | Use lat, lon and zoom. The @lat,lng,zoom encoding is handled for you. |
| pb | 422 | – | Not needed. The Maps protobuf is compiled from lat, lon and zoom. |
| num | 422 | – | Google serves twenty places a page. Paginate with page. |
| start | 422 | – | Use page, a 1-based page number, instead of a result offset. |
| place_id / data_cid / type | 422 | – | This endpoint is the place search. Single-place lookup by id is not wired yet; search by q and every result carries its own ids. |
| data | 422 | – | Not supported. |
| google_domain | 422 | – | Not supported on this vertical. |
| tbm | 422 | – | Not needed. This endpoint is Google Maps. |
| engine | 422 | – | Not needed. This endpoint is Google Maps. |
| api_key | 422 | – | Authenticate with the Authorization header, never the query string. |
HTTP/2 422
content-type: application/json
{
"detail": [
{
"type": "value_error",
"loc": ["query"],
"msg": "Value error, `location` is not supported: this vertical needs real coordinates: send `lat`/`lon`. The geotarget dataset has no coordinates, so a city name cannot be resolved to a map centre here",
"input": { "q": "coffee", "location": "Austin", ... }
}
]
}Response
A search returns one JSON object: two envelope objects that are always present (search_metadata, search_parameters), the places as local_results, and pagination. A field that wasn't served is omitted entirely, never null, never [], so a presence check is all you need before reading one.
{
"search_metadata": {
"id": "srx_q1-fNfV_VuwbwvsA",
"status": "success",
"created_at": "2026-07-28T19:24:51Z",
"took_ms": 5195,
"parser_version": "2026.07.1",
"google_url": "https://www.google.com/search?tbm=map&hl=en&gl=us&q=coffee&pb=!1scoffee!4m8!1m3!1d27567.5...",
"html_url": "https://api.serpix.io/searches/srx_q1-fNfV_VuwbwvsA/GG8owWxcUIMZluIr.html",
"json_url": "https://api.serpix.io/searches/srx_q1-fNfV_VuwbwvsA/GG8owWxcUIMZluIr.json"
},
"search_parameters": {
"q": "coffee",
"gl": "us",
"hl": "en",
"page": 1,
"zoom": 14,
"latitude": 30.2672,
"longitude": -97.7431
},
"local_results": [
{
"position": 1,
"title": "UFO Coffee & Tea",
"place_id": "ChIJ0S8AJAC1RIYRLIa71SkPRXc",
"data_id": "0x8644b50024002fd1:0x77450f29d5bb862c",
"gps_coordinates": { "latitude": 30.2844171, "longitude": -97.71734699999999 },
"rating": 4.9,
"reviews": 40,
"type": "Coffee shop",
"types": ["Coffee shop"],
"type_id": "coffee_shop",
"address": "2307 1/2 Manor Rd, Austin, TX 78722",
"neighborhood": "East Austin",
"country": "US",
"timezone": "America/Chicago",
"phone": "(512) 850-5150",
"phone_e164": "+15128505150",
"website": "http://ufo-coffee.com/",
"provider_id": "/g/11yr_2ytl2",
"reviews_link": "https://search.google.com/local/reviews?placeid=ChIJ0S8AJAC1RIYRLI...",
"operating_hours": {
"Monday": "7 AM–4 PM",
"Tuesday": "7 AM–4 PM",
...
}
},
...
],
"pagination": {
"current": 1,
"next": "https://www.google.com/search?tbm=map&hl=en&gl=us&q=coffee&pb=!1scoffee!4m8...!8i20!..."
}
}Every response also reports its cost in the X-Serpix-Credits-Charged and X-Serpix-Credits-Remaining headers.
search_metadata
| field | type | default | description |
|---|---|---|---|
| id | string | – | Unique id of this search, srx_ prefixed. |
| status | string | – | success on every 200. |
| created_at | string | – | When the search ran, ISO 8601 UTC. |
| took_ms | integer | – | End-to-end time to serve this response, in milliseconds. |
| parser_version | string | – | Dated version of the maps parser that produced this response, so any response can be tied to the parsing behaviour that made it. Each vertical carries its own. |
| google_url | string | – | The exact Maps URL fetched, protobuf and all, so a response can be traced back to the map window it came from. |
| html_url | string | – | Archived copy of the payload exactly as served. Included when available. |
| json_url | string | – | Archived copy of this JSON response. Included when available. |
search_parameters
The parameters the search actually ran with, defaults filled in.
| field | type | default | description |
|---|---|---|---|
| q | string | – | The query as received. |
| gl | string | – | Country the search ran from. |
| hl | string | – | Language used. |
| page | integer | – | Page served. |
| zoom | integer | – | Zoom level used. |
| latitude | number | – | Latitude of the map centre. Present when sent. |
| longitude | number | – | Longitude of the map centre. Present when sent. |
Three identifiers
Google keeps three ids for a place and they are not interchangeable, so each keeps its own name here rather than being flattened into one:
| field | type | default | description |
|---|---|---|---|
| place_id | string | – | The Places reference, like ChIJ0S8AJAC1RIYRLIa71SkPRXc. This is the id most Google tooling means by “place id”. |
| data_id | string | – | The legacy hex pair, like 0x8644b50024002fd1:0x77450f29d5bb862c. |
| data_cid | string | – | The decimal customer id. Confusingly, this is the value other Google surfaces call place_id, including the local pack on a web search. |
local_results
| field | type | default | description |
|---|---|---|---|
| position | integer | – | Rank in the results, starting at 1. |
| title | string | – | The place's name. |
| place_id | string | – | Places reference. See above. |
| data_id | string | – | Legacy hex identifier. See above. |
| data_cid | string | – | Decimal customer id. See above. |
| gps_coordinates | object | – | The place's own latitude and longitude. |
| rating | number | – | Average rating out of 5. |
| reviews | integer | – | Number of reviews behind that rating. |
| type | string | – | Primary category as displayed, like Coffee shop. |
| types | list | – | Every category Google lists for the place. |
| type_id | string | – | Machine-readable category, like coffee_shop. |
| address | string | – | Street address on one line. |
| neighborhood | string | – | Neighbourhood, where Google names one. |
| country | string | – | Two-letter country code. |
| timezone | string | – | IANA timezone, like America/Chicago. |
| phone | string | – | Phone number as displayed, like (512) 850-5150. |
| phone_e164 | string | – | The same number in dialable E.164 form. |
| website | string | – | The business's own site, when it lists one. |
| provider_id | string | – | Google knowledge identifier, like /g/11njyz2l2k. |
| thumbnail | string | – | Place photo. |
| reviews_link | string | – | Google's reviews page for the place. |
| operating_hours | object | – | Opening hours by day, as displayed. |
| field | type | default | description |
|---|---|---|---|
| latitude | number | – | Latitude in decimal degrees. |
| longitude | number | – | Longitude in decimal degrees. |
Which fields a place carries follows the listing rather than the parser: on the sample request every place carried opening hours, and nine of the twenty carried a phone number, because the rest publish none.
pagination
Ask for the next page with page. pagination.next is present while another page exists, so its absence is how you know the results ran out.
| field | type | default | description |
|---|---|---|---|
| current | integer | – | The page this response is. |
| next | string | – | URL for the next page. Absent once the results run out. |
Errors
A rejected request answers with detail, naming the parameter and what to send in its place. A search that could not be completed answers with error and a search_id you can quote. No error is ever charged.
| field | type | default | description |
|---|---|---|---|
| 401 | unauthorized | −0 | Missing, malformed, or revoked key. See Authentication. |
| 402 | payment required | −0 | The balance can't cover a search. See Credits & billing. |
| 422 | invalid request | −0 | Unknown or invalid parameter, or lat without lon. The detail says what to send instead. |
| 502 | search_failed | −0 | The search could not be completed, which on this vertical also covers a payload whose shape drifted. Retry it; you were not charged. |
| 503 | search_unavailable | −0 | Google did not serve a usable payload. Retry it; you were not charged. |
HTTP/2 502
content-type: application/json
x-serpix-credits-charged: 0
{ "error": { "code": "search_failed", "search_id": "srx_kq3v9tR2xLw8mA1c" } }