---
title: "Search for places"
method: POST
path: "/places/search"
tags: ["Places"]
---

# Search for places

`POST /places/search`

## Request

- Base URL: `https://api.gmaps.dev/v1`
- URL: `https://api.gmaps.dev/v1/places/search`
- Auth: HTTP bearer

Find places by what they are and where they are, such as bakeries in Curitiba. Places we saved from earlier searches come back at once, for free. If they do not fill `limit`, a collection starts to find the rest and the answer includes it: each new place costs one credit as it arrives, and it stops at `limit`, so a search never costs more than `limit` credits. Search again later, or read the collection, to get the new ones; with `wait`, the answer waits up to 60 seconds for them. Needs an API key.

## Request body

- object
  - `keywords` string[], required: What to search for, such as ["bakery"]. One search per term.
  - `geo` union, required: Where to search. Give exactly one of: a Brazilian city as location, a lat/lng point with radiusM, a bbox, or a cityIbge with radiusKm.
    - object
      - `location` string, required
    - object
      - `lat` number, required
      - `lng` number, required
      - `radiusM` number, required
    - object
    - object
      - `cityIbge` integer, required
      - `radiusKm` number, required
  - `lang` string: The language to search in. New places come back in it. Two letters, such as pt or en.
  - `minRating` number, nullable: Keep only places rated at least this, from 1 to 5. Null keeps every place.
  - `limit` integer: How many places you want, from 1 to 120. Also the most a search can cost.
  - `wait` integer: Seconds to wait for new places when we have to collect them, up to 60. 0 answers at once with what we already have.

## Response `200`

Search for places

- object
  - `data` object, required
    - `places` object[], required: Up to `limit` places. When a collection is running, these are its places so far, each saying whether we already had it. That is how you check what it cost.
      - `id` string, uuid, required: Our id for the place. It never changes, and get_place takes it. Google's own id is `identity.cid`.
      - `identity` object, required: Who the place is: its name, category and ids.
        - `cid` string, nullable, required: Google's id for the place: up to 25 digits, always as a string. It does not fit in a JavaScript number, so reading it as one loses the last digits. Null when Maps didn't give one.
        - `placeId` string, nullable, required: Google's place ID. Null when Maps didn't give one.
        - `dataId` string, nullable, required: Google Maps' own id for the listing: two hexadecimal numbers joined by a colon. Null when Maps didn't give one.
        - `name` string, required: The business's name, as Maps shows it.
        - `category` string, nullable, required: The kind of business, in the language of the last search that collected it: "Bakery", "Padaria".
        - `mapsUrl` string, uri, required: The place's page on Google Maps.
        - `description` string, nullable, required: What the business wrote about itself on Maps. Null when it wrote nothing.
        - `status` string, nullable, required: What its Maps listing said when we collected it (`meta.scrapedAt`), such as "Permanently closed".
      - `contact` object, required: How to reach the business.
        - `phones` string[], required: Phone numbers, as Maps shows them. Empty when it lists none.
        - `website` string, uri, nullable, required: The business's own website. Null when Maps lists none.
        - `emails` string[]: Email addresses we found for the place. Sent only on a plan with emails, after you accept the acceptable use policy, and only when we have at least one. So if you have access and `emails` is missing, we have none.
      - `location` object, required: Where the place is.
        - `address` string, nullable, required: The whole address on one line, as Maps writes it.
        - `completeAddress` object, nullable, required: The same address in parts. Null when Maps didn't split it.
          - `borough` string, nullable, required: The neighborhood: the bairro, in Brazil.
          - `street` string, nullable, required
          - `city` string, nullable, required
          - `postalCode` string, nullable, required: The postal code: the CEP, in Brazil.
          - `state` string, nullable, required
          - `country` string, nullable, required
        - `city` string, nullable, required: The city, taken from `completeAddress`.
        - `state` string, nullable, required: The state, taken from `completeAddress`.
        - `country` string, nullable, required: The country, taken from `completeAddress`.
        - `lat` number, nullable, required: Latitude in degrees. Null when we have no coordinates, and then `lng` is null too.
        - `lng` number, nullable, required: Longitude in degrees. Null exactly when `lat` is.
        - `plusCode` string, nullable, required: Google's Plus Code for the spot, a short code that works like an address.
      - `ratings` object, required: The star rating and its reviews.
        - `rating` number, nullable, required: The average star rating, 0 to 5. Null when nobody has rated it.
        - `reviewCount` integer, nullable, required: How many reviews it has. Null when Maps didn't say.
        - `reviewsPerRating` object, nullable, required: How many reviews gave each star count. Stars Google left out are missing here too.
      - `hours` object, required: When it opens and how busy it gets.
        - `timezone` string, nullable, required: The place's time zone, such as America/Sao_Paulo.
        - `openHours` object, nullable, required: Day name to the hours it is open, both in the language of the last search that collected the place. Null when Maps shows no hours.
        - `popularTimes` object, nullable, required: Day name to hour ("0" to "23") to how busy it usually is, from 0 to 100. Null when Maps shows none.
      - `commerce` object, required: Prices, amenities, payments, and links to order or book.
        - `priceRange` string, nullable, required: The price band Maps shows, such as "$$". Null when it shows none.
        - `about` object[], required: What the place says about itself on Maps, in groups such as accessibility or payments.
          - `id` string, required
          - `name` string, required
          - `options` object[], required: What Maps lists in this group, such as a wheelchair-accessible entrance.
            - `name` string, required
            - `enabled` boolean, required: True when the place has it, false when Maps says it doesn't.
            - `values` string[]
        - `creditCardsAccepted` string[], required: The cards it takes, as Maps lists them. Empty when Maps doesn't say.
        - `menu` object, nullable, required: A link to its menu. Null when it has none.
          - `link` string, uri, required: The link to open.
          - `source` string, required: Who offers it: "Google", the business's own site, a delivery app.
        - `orderOnline` object[], required: Where to order from it online. Empty when Maps lists none.
          - `link` string, uri, required: The link to open.
          - `source` string, required: Who offers it: "Google", the business's own site, a delivery app.
        - `reservations` object[], required: Where to book with it. Empty when Maps lists none.
          - `link` string, uri, required: The link to open.
          - `source` string, required: Who offers it: "Google", the business's own site, a delivery app.
      - `meta` object, required: When we collected the place, and whether it cost you a credit.
        - `scrapedAt` string, date-time, required: When we last collected this place.
        - `firstSeenAt` string, date-time, required: When we first collected this place.
        - `cached` boolean, required: True when this came from what we already had, which costs nothing.
    - `coverage` 'fresh' | 'partial' | 'new', required: How much of the area we had searched when you asked. `fresh`: all of it recently. `new`: none of it. `partial`: some.
    - `placesKnown` integer, required: Places we already had saved here when you asked. They cost nothing.
    - `collection` object, nullable, required: Collecting the rest, when what we held could not fill `limit`. It stops at `limit` places. Read it again, or follow its stream, to see the new ones arrive. Null when nothing needed collecting, or when `collectionError` says why we could not start.
      - `id` string, uuid, required: Our id for the collection. get_collection, list_collection_places, cancel_collection and create_export take it.
      - `kind` 'search' | 'collection', required: `collection` when you asked for it, `search` when a search started it to fill its `limit`.
      - `status` 'queued' | 'running' | 'succeeded' | 'partial' | 'failed' | 'cancelled', required: `queued` while it waits its turn, `running` while it searches, then `succeeded`, `partial`, `failed` or `cancelled`.
      - `stopReason` 'quota' | 'daily_cap' | 'cancelled' | 'engine' | 'empty', nullable, required: Why it stopped short, or found nothing. A credit limit shows here as soon as it is hit, while the search already in flight finishes. Null when it searched the whole area or reached `maxPlaces`.
      - `query` object, required: What you asked for, exactly as it ran, with `maxPlaces` filled in from your plan when you left it out.
        - `keywords` string[], required: What it searched for. One search per term.
        - `geo` union, required: The area, as you gave it: a Brazilian city as location, a lat/lng point with radiusM, a bbox, or a cityIbge with radiusKm.
          - object
            - `location` string, required
          - object
            - `lat` number, required
            - `lng` number, required
            - `radiusM` number, required
          - object
          - object
            - `cityIbge` integer, required
            - `radiusKm` number, required
        - `lang` string, required: The language it searched in. Two letters, such as pt or en.
        - `minRating` number, nullable, required: Only places rated at least this were kept, from 1 to 5. Null when every place was.
        - `maxPlaces` integer, required: It stops after this many places: what you asked for, or the most your plan collects at once.
        - `enrich` string[], required: ["emails"] when it also looks for email addresses, which costs one more credit for each place that has one. Empty when it doesn't.
      - `area` union, required: Where it searched, ready to draw: a lat/lng centre with radiusM in metres, or a bbox as [west, south, east, north]. A city is the circle we searched around its centre. Null for an old collection whose city we can no longer place.
        - object
          - `lat` number, required
          - `lng` number, required
          - `radiusM` integer, required
        - object
      - `squaresTotal` integer, required: How many searches the area takes: one per square of the area, for each term.
      - `squaresCovered` integer, required: How many of those we searched recently enough to reuse. Their places cost nothing.
      - `jobsTotal` integer, required: Batches of searching. Each one covers several squares around one point.
      - `jobsDone` integer, required: How many of those batches have finished.
      - `counts` object, required: What it has delivered so far, and what that cost.
        - `placesDelivered` integer, required: Places handed over so far: always `placesFresh` plus `placesCached`.
        - `placesFresh` integer, required: Places we collected for you just now, either new to us or too old to reuse. Each costs one credit.
        - `placesCached` integer, required: Places we already had saved. They cost nothing.
        - `creditsCharged` integer, required: Credits taken: one per fresh place, plus one per place we found an email for.
        - `creditsRefunded` integer, required: Credits given back. A collection that delivers nothing refunds all of them.
      - `error` object, nullable, required: Why it failed, shaped like any other error. Null unless `status` is `failed`.
        - `code` 'VALIDATION_ERROR' | 'UNAUTHORIZED' | 'INVALID_TOKEN' | 'PLAN_GATE' | 'AUP_REQUIRED' | 'FORBIDDEN' | 'NOT_FOUND' | 'PLACE_NOT_FOUND' | 'COLLECTION_NOT_FOUND' | 'EXPORT_NOT_FOUND' | 'CONFLICT' | 'PAYLOAD_TOO_LARGE' | 'GEO_UNRESOLVED' | 'UPSTREAM_BLOCKED' | 'RATE_LIMIT_EXCEEDED' | 'QUOTA_EXCEEDED' | 'INTERNAL_ERROR' | 'ENGINE_UNAVAILABLE' | 'SERVICE_UNAVAILABLE' | 'BILLING_UNAVAILABLE', required: What went wrong, as a word a program can check.
        - `message` string, required: What went wrong, as a sentence.
        - `messageKey` 'serverErrors.validation' | 'serverErrors.unauthorized' | 'serverErrors.keyInactive' | 'serverErrors.signInRequired' | 'serverErrors.planGateEmails' | 'serverErrors.planGateWebhooks' | 'serverErrors.planGateContact' | 'serverErrors.aupRequired' | 'serverErrors.forbidden' | 'serverErrors.accountSuspended' | 'serverErrors.accountDeletionRequested' | 'serverErrors.accountDeleted' | 'serverErrors.lastStaff' | 'serverErrors.notFound' | 'serverErrors.placeNotFound' | 'serverErrors.placeShortLink' | 'serverErrors.placeLinkNotOne' | 'serverErrors.placeRefUnreadable' | 'serverErrors.collectionNotFound' | 'serverErrors.collectionEnded' | 'serverErrors.collectionPlacesDeleted' | 'serverErrors.exportNotFound' | 'serverErrors.exportNothingYet' | 'serverErrors.exportNothingFound' | 'serverErrors.conflict' | 'serverErrors.payloadTooLarge' | 'serverErrors.geoUnresolved' | 'serverErrors.cityAmbiguous' | 'serverErrors.cityDidYouMean' | 'serverErrors.cityUnknown' | 'serverErrors.cityIbgeUnknown' | 'serverErrors.areaTooLarge' | 'serverErrors.radiusTooLarge' | 'serverErrors.noSearchTerms' | 'serverErrors.tooManyTerms' | 'serverErrors.maxPlacesOverPlan' | 'serverErrors.xlsxTooLarge' | 'serverErrors.keyLimit' | 'serverErrors.subscriptionOpen' | 'serverErrors.checkoutOpen' | 'serverErrors.webhookNotHttps' | 'serverErrors.webhookCredentials' | 'serverErrors.webhookPrivateAddress' | 'serverErrors.webhookNoAddress' | 'serverErrors.webhookDuplicate' | 'serverErrors.webhookUnreachable' | 'serverErrors.webhookTimeout' | 'serverErrors.webhookStatus' | 'serverErrors.webhookRedirect' | 'serverErrors.webhookStopped' | 'serverErrors.webhookOff' | 'serverErrors.upstreamBlocked' | 'serverErrors.rateLimit' | 'serverErrors.quotaMonth' | 'serverErrors.quotaDay' | 'serverErrors.concurrency' | 'serverErrors.internal' | 'serverErrors.engineUnavailable' | 'serverErrors.collectionEngineFailed' | 'serverErrors.serviceUnavailable' | 'serverErrors.billingUnavailable' | 'serverErrors.billingCurrencyUnavailable': `message` as a key, to show it in your reader's language, with `params` filling its blanks.
        - `params` object: The values that fill `messageKey`'s blanks, such as a limit.
        - `details` unknown
      - `queuedAt` string, date-time, required: When we accepted it. ISO 8601 with an offset, as every date here is.
      - `startedAt` string, date-time, nullable, required: When the first search started. Null while it waits its turn.
      - `finishedAt` string, date-time, nullable, required: When it ended. Null while it runs.
      - `cancelRequestedAt` string, date-time, nullable, required: Set the moment you cancel. The search already in flight still finishes.
      - `resultsExpireAt` string, date-time, required: After this, the counts stay but its places can no longer be listed. Export them first.
    - `collectionError` object, nullable, required: Why we could not start collecting the rest: out of credits for today or this month, or already running as many collections as your plan allows. The places we already held still come back.
      - `code` 'VALIDATION_ERROR' | 'UNAUTHORIZED' | 'INVALID_TOKEN' | 'PLAN_GATE' | 'AUP_REQUIRED' | 'FORBIDDEN' | 'NOT_FOUND' | 'PLACE_NOT_FOUND' | 'COLLECTION_NOT_FOUND' | 'EXPORT_NOT_FOUND' | 'CONFLICT' | 'PAYLOAD_TOO_LARGE' | 'GEO_UNRESOLVED' | 'UPSTREAM_BLOCKED' | 'RATE_LIMIT_EXCEEDED' | 'QUOTA_EXCEEDED' | 'INTERNAL_ERROR' | 'ENGINE_UNAVAILABLE' | 'SERVICE_UNAVAILABLE' | 'BILLING_UNAVAILABLE', required: What went wrong, as a word a program can check.
      - `message` string, required: What went wrong, as a sentence.
      - `messageKey` 'serverErrors.validation' | 'serverErrors.unauthorized' | 'serverErrors.keyInactive' | 'serverErrors.signInRequired' | 'serverErrors.planGateEmails' | 'serverErrors.planGateWebhooks' | 'serverErrors.planGateContact' | 'serverErrors.aupRequired' | 'serverErrors.forbidden' | 'serverErrors.accountSuspended' | 'serverErrors.accountDeletionRequested' | 'serverErrors.accountDeleted' | 'serverErrors.lastStaff' | 'serverErrors.notFound' | 'serverErrors.placeNotFound' | 'serverErrors.placeShortLink' | 'serverErrors.placeLinkNotOne' | 'serverErrors.placeRefUnreadable' | 'serverErrors.collectionNotFound' | 'serverErrors.collectionEnded' | 'serverErrors.collectionPlacesDeleted' | 'serverErrors.exportNotFound' | 'serverErrors.exportNothingYet' | 'serverErrors.exportNothingFound' | 'serverErrors.conflict' | 'serverErrors.payloadTooLarge' | 'serverErrors.geoUnresolved' | 'serverErrors.cityAmbiguous' | 'serverErrors.cityDidYouMean' | 'serverErrors.cityUnknown' | 'serverErrors.cityIbgeUnknown' | 'serverErrors.areaTooLarge' | 'serverErrors.radiusTooLarge' | 'serverErrors.noSearchTerms' | 'serverErrors.tooManyTerms' | 'serverErrors.maxPlacesOverPlan' | 'serverErrors.xlsxTooLarge' | 'serverErrors.keyLimit' | 'serverErrors.subscriptionOpen' | 'serverErrors.checkoutOpen' | 'serverErrors.webhookNotHttps' | 'serverErrors.webhookCredentials' | 'serverErrors.webhookPrivateAddress' | 'serverErrors.webhookNoAddress' | 'serverErrors.webhookDuplicate' | 'serverErrors.webhookUnreachable' | 'serverErrors.webhookTimeout' | 'serverErrors.webhookStatus' | 'serverErrors.webhookRedirect' | 'serverErrors.webhookStopped' | 'serverErrors.webhookOff' | 'serverErrors.upstreamBlocked' | 'serverErrors.rateLimit' | 'serverErrors.quotaMonth' | 'serverErrors.quotaDay' | 'serverErrors.concurrency' | 'serverErrors.internal' | 'serverErrors.engineUnavailable' | 'serverErrors.collectionEngineFailed' | 'serverErrors.serviceUnavailable' | 'serverErrors.billingUnavailable' | 'serverErrors.billingCurrencyUnavailable': `message` as a key, to show it in your reader's language, with `params` filling its blanks.
      - `params` object: The values that fill `messageKey`'s blanks, such as a limit.
      - `details` unknown
  - `meta` union
    - object
      - `total` integer, required: Every item that matches, not just this page.
      - `limit` integer, required: The most items a page holds.
      - `offset` integer, required: How many items came before this page.
      - `hasMore` boolean, required: True when another page follows.
    - object
      - `cost` integer, required: Credits this call took.
      - `creditsRemaining` integer, required: Credits left after this call, this month's and bought ones together.
      - `requestId` string, required: This call's id. Send it to support if something went wrong.
      - `cached` boolean: True when this response used only saved results.

## Other responses

- `default`: Any other failure.

---

[API](https://skmtc.dev/gmaps/apis/gmaps-dev.md) · [All operations](https://skmtc.dev/gmaps/apis/gmaps-dev/llms.txt) · [OpenAPI document](https://skmtc.dev/gmaps/apis/gmaps-dev/revisions/0b3459aaf802?raw)
