# Google Maps

Search Google Maps and retrieve structured place records in JSON format.

## Endpoint

```
GET /v1/maps/search/{query}
```

## Description

The Google Maps endpoint returns structured place records for a location or category search: name, address, coordinates, rating, categories, phone, timezone, and opening hours.

This endpoint takes its parameters differently from the rest of the API. Everywhere else, options are packed into the path itself (`/v1/search/q=coffee+shops&num=100`). Here the path segment is the search text, and `num`, `hl`, and `gl` are ordinary query parameters after a `?`:

```
https://api.serply.io/v1/maps/search/coffee%20shops%20in%20Chicago%2C%20IL?num=20&hl=en&gl=us
```

A leading `q=` in the path is accepted for consistency with the other endpoints, but it is not required. Anything after an `&` inside the path segment is ignored, so refinements must go after the `?` to take effect.

Responses are cached for 10 minutes. A cached response does not consume prepaid credits.

## Authentication

All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details.

## Path Parameters

### `query` (required)

**Type:** `string`

The place or category to search for, URL-encoded.

**Examples:**
- `coffee%20shops%20in%20Chicago%2C%20IL`
- `dentists%20near%20Austin%20TX`

## Query Parameters

### `num` (optional)

**Type:** `integer`

How many places to return. Defaults to `20`. Accepts `1` to `200`. Google often returns fewer places than requested.

### `hl` (optional)

**Type:** `string`

Interface language code. Defaults to `en`.

### `gl` (optional)

**Type:** `string`

Two-letter country code. Defaults to `us`.

## Request Headers

`X-Proxy-Location` and `X-User-Agent` are **not** supported on this endpoint. It reads Google's non-JavaScript Maps transport directly rather than going through a proxy, so there is no proxy region or device to select. Use `gl` and `hl` to control locale instead.

## Request Example

### Using cURL

```bash
curl --request GET \
  --url 'https://api.serply.io/v1/maps/search/coffee%20shops%20in%20Chicago%2C%20IL?num=20&hl=en&gl=us' \
  --header 'X-Api-Key: YOUR_API_KEY'
```

### Using JavaScript/Node.js

```javascript
const query = encodeURIComponent('coffee shops in Chicago, IL');
const params = new URLSearchParams({ num: '20', hl: 'en', gl: 'us' });

const response = await fetch(
  `https://api.serply.io/v1/maps/search/${query}?${params}`,
  {
    headers: {
      'X-Api-Key': 'YOUR_API_KEY'
    }
  }
);
const data = await response.json();
console.log(data);
```

### Using Python

```python
import requests
from urllib.parse import quote

query = quote('coffee shops in Chicago, IL')

response = requests.get(
    f'https://api.serply.io/v1/maps/search/{query}',
    params={'num': 20, 'hl': 'en', 'gl': 'us'},
    headers={'X-Api-Key': 'YOUR_API_KEY'}
)
data = response.json()
print(data)
```

## Response

The API returns a JSON object containing an array of place records.

### Response Structure

```json
{
  "search_engine": "google_maps",
  "query": "coffee shops in Chicago, IL",
  "places": [
    {
      "position": 1,
      "name": "Place Name",
      "data_id": "0x880e2cb109470fb1:0x1bfa35f0425ae540",
      "place_id": "ChIJsQ9HCbEsDogRQOVaQvA1-hs",
      "google_maps_url": "https://www.google.com/maps/search/?api=1&query=...",
      "website": "https://example.com/",
      "domain": "example.com",
      "address": "346 N Clark St Unit 4709, Chicago, IL 60654",
      "address_lines": ["346 N Clark St Unit 4709", "Chicago, IL 60654"],
      "district": "Near North Side",
      "latitude": 41.8887579,
      "longitude": -87.6312297,
      "rating": 4.6,
      "review_count": null,
      "review_url": null,
      "categories": ["Coffee shop", "Espresso bar"],
      "category_ids": [],
      "phone": null,
      "phone_e164": null,
      "timezone": "America/Chicago",
      "thumbnail": "https://lh3.googleusercontent.com/...",
      "opening_hours": { "Friday": "7 AM-5 PM" }
    }
  ],
  "result_count": 20,
  "parsed_at": "2026-08-14T20:32:17.293873Z",
  "metadata": {
    "schema": "tbm-map-positional-v1",
    "transport": "direct",
    "requested_count": 20,
    "language": "en",
    "country": "us"
  }
}
```

### Response Fields

- **`search_engine`** (string): Always `google_maps`
- **`query`** (string): The decoded search text
- **`places`** (array): An array of place objects
  - **`position`** (number): Rank within the result set, starting at 1
  - **`name`** (string): The name of the place
  - **`data_id`** (string): Google's internal identifier for the place
  - **`place_id`** (string): The Places API identifier, when available
  - **`google_maps_url`** (string): A link to the place on Google Maps
  - **`website`** (string | null): The place's own website
  - **`domain`** (string | null): The bare hostname of `website`
  - **`address`** (string | null): The full formatted address
  - **`address_lines`** (array[string]): The address split into display lines
  - **`district`** (string | null): Neighborhood or district name
  - **`latitude`** (number): Latitude in decimal degrees
  - **`longitude`** (number): Longitude in decimal degrees
  - **`rating`** (number | null): Average star rating out of 5
  - **`review_count`** (number | null): Number of reviews
  - **`review_url`** (string | null): A link to the reviews listing
  - **`categories`** (array[string]): Human-readable category labels
  - **`category_ids`** (array[string]): Google category identifiers, usually empty
  - **`phone`** (string | null): Phone number as displayed
  - **`phone_e164`** (string | null): The same number in E.164 format
  - **`timezone`** (string | null): IANA timezone name
  - **`thumbnail`** (string | null): A photo URL
  - **`opening_hours`** (object | null): Day name to hours string
- **`result_count`** (number): The number of places returned
- **`parsed_at`** (string): ISO 8601 timestamp of when the response was parsed
- **`metadata`** (object): Details about how the result was produced

Treat every place field except `position`, `name`, `data_id`, `latitude`, and `longitude` as optional. `review_count` and `review_url` in particular are frequently `null` on broad category searches and populated on narrow ones.

Values in `opening_hours` come verbatim from Google and can contain non-ASCII characters such as narrow no-break spaces and en dashes. Normalize them before display.

## Status Codes

- **200 OK** - Successful response
- **400 Bad Request** - Empty query, `num` outside 1-200, or a malformed `hl` or `gl`
- **429 Too Many Requests** - Rate limit exceeded
- **502 Bad Gateway** - Google Maps structured search is temporarily unavailable

A `502` means the upstream fetch or the response parse failed. The underlying format is positional and undocumented, so a Google-side layout change surfaces as a `502` rather than as a partial result.

## Error Responses

See the [Errors guide](/docs/guides/errors) for information on error response formats.
