Pular para o conteúdo

Lugares · POST /v1/places/search

Busca

Diga o que você procura e onde, e receba os lugares. A busca responde na hora com os lugares que já salvamos e coleta o resto quando eles não bastam.

Faça uma busca

Uma busca precisa de duas coisas: o que você procura, em keywords, e onde, em geo. O resto tem valor padrão. Esta pede até 50 padarias em Curitiba com nota 4 ou mais:

curl -X POST https://api.gmaps.dev/v1/places/search \
  -H "Authorization: Bearer $GMAPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "keywords": ["padaria"],
    "geo": { "location": "Curitiba, PR" },
    "minRating": 4,
    "limit": 50
  }'
CampoPadrãoPara que serve
keywordsObrigatórioO que procurar, como ["padaria"]. Cada termo é buscado separadamente. Maiúsculas, acentos e espaços sobrando não fazem diferença.
geoObrigatórioOnde procurar: exatamente um dos quatro formatos abaixo.
lang"pt"O idioma da busca, em duas letras, como en. Os lugares que coletamos para você vêm nesse idioma. Cada idioma é buscado separadamente, então uma região já buscada em pt continua new em en.
minRatingnullTraz só lugares com nota igual ou maior que esta, de 1 a 5. Lugares que ninguém avaliou também ficam de fora. Com null, vêm todos.
limit20Quantos lugares você quer, de 1 a 120. É também o máximo que a busca pode custar, em créditos.
wait0Por quantos segundos a resposta espera os lugares novos chegarem, até 60. Com 0, a resposta vem na hora.

Um campo que não conhecemos é recusado, então nenhum erro de digitação passa batido.

Diga onde

geo aceita exatamente um destes formatos. Mandar dois, ou misturar os campos deles, é recusado.

FormatoO que cobre
{ "location": "Curitiba, PR" }Uma cidade brasileira e a sigla do estado. Curitiba/PR e Curitiba - PR também funcionam, assim como um nome que só uma cidade tem. Buscamos a uma distância fixa em volta do centro da cidade.
{ "lat": -25.4296, "lng": -49.2713, "radiusM": 1500 }Um ponto e até onde procurar em volta dele, em metros. Funciona em qualquer lugar do mundo.
{ "bbox": [-49.31, -25.46, -49.23, -25.39] }Um retângulo, na ordem oeste, sul, leste e norte, em graus. Oeste precisa ser menor que leste, e sul menor que norte. Uma região que cruza a linha dos 180° vai como dois retângulos.
{ "cityIbge": 4106902, "radiusKm": 8 }Uma cidade brasileira pelo código IBGE de sete dígitos (o de Curitiba é 4106902) e até onde procurar a partir do centro, em quilômetros.

location cobre sempre a mesma distância, que pode não chegar até a borda de uma cidade grande. Para escolher o tamanho, mande cityIbge com radiusKm ou um ponto com radiusM.

location só encontra cidades brasileiras. Quando não dá para saber qual cidade você quis dizer, a busca é recusada com 422 GEO_UNRESOLVED:

  • Várias cidades têm esse nome: acrescente o estado, como Bom Jesus, PI. details.candidates lista as que encontramos.
  • Nenhuma cidade tem esse nome: confira como está escrito. Se algum nome chega perto, a mensagem pergunta se era ele, e details.candidates mostra esse nome.
  • O lugar fica fora do Brasil: mande um ponto ou um retângulo.

Um código IBGE que não é de uma cidade brasileira é recusado do mesmo jeito.

Regiões grandes demais para uma busca

Buscamos cada região em quadrados pequenos, uma vez para cada termo, e Cobertura explica como. Alguns pedidos são grandes demais para uma busca só. Eles são recusados com 400 VALIDATION_ERROR antes de qualquer coisa começar:

  • um raio acima do limite para uma região;
  • uma região e termos que somam mais quadrados do que um pedido aceita;
  • mais termos do que um pedido aceita.

A mensagem diz qual é o limite e o que você mandou. Use uma região menor ou menos termos, ou divida o pedido em várias buscas.

Leia a resposta

Esta é a resposta para uma região que já tínhamos buscado em parte, reduzida a um lugar e a poucos campos:

{
  "data": {
    "places": [
      {
        "id": "5b0c8e0a-2f4d-4c1e-9a57-3d2b8f6e1c90",
        "identity": {
          "cid": "12345678901234567890",
          "name": "Padaria Estrela do Centro",
          "category": "Padaria",
          "mapsUrl": "https://maps.google.com/?cid=12345678901234567890"
        },
        "ratings": { "rating": 4.6, "reviewCount": 212 },
        "meta": { "cached": true }
      }
    ],
    "coverage": "partial",
    "placesKnown": 14,
    "collection": {
      "id": "8c1f5e2a-7b3d-4f60-9e14-2a6d0b9c7f31",
      "kind": "search",
      "status": "queued",
      "squaresTotal": 21,
      "squaresCovered": 9,
      "counts": {
        "placesDelivered": 14,
        "placesFresh": 0,
        "placesCached": 14,
        "creditsCharged": 0,
        "creditsRefunded": 0
      }
    },
    "collectionError": null
  }
}
  • places: até limit lugares, cada um no formato descrito em Lugares. Com uma coleta, são os lugares que ela tem até agora, e os que já tínhamos salvado vêm primeiro. Sem coleta, vêm primeiro os que têm mais avaliações.
  • coverage: quanto da região já tínhamos buscado recentemente. fresh é tudo, partial é uma parte e new é nada.
  • placesKnown: quantos lugares já tínhamos salvado na parte buscada, contando só os que passam no minRating. Todos eles saem de graça.
  • collection: a coleta que está buscando o resto, ou null quando não precisou coletar nada. Coletas explica tudo sobre ela.
  • collectionError: por que a coleta não pôde começar, ou null.
  • meta.cached, em cada lugar: true quando já tínhamos o lugar salvo, então ele não custou nada.

Quando ainda não salvamos o bastante

Uma busca só começa uma coleta quando as duas coisas acontecem: parte da região não foi buscada recentemente, e temos menos de limit lugares salvos ali. Se buscamos a região toda recentemente, você recebe o que encontramos naquela vez, e nada começa.

A coleta entrega no máximo limit lugares, e os que já tínhamos salvado entram nessa conta de graça. Cada lugar novo custa 1 crédito quando chega, então uma busca nunca custa mais que limit créditos. Começar a coleta é de graça: toda busca responde com x-request-cost: 0, e o counts.creditsCharged da coleta mostra o que ela cobrou desde então.

Se você mandar a mesma busca enquanto a coleta dela roda, recebe essa mesma coleta de volta. Nenhuma coleta nova começa, e você não paga nada a mais.

Para receber os lugares novos, você pode:

  • fazer a mesma busca de novo mais tarde. Os lugares novos voltam junto com os outros, e agora de graça.
  • ler a coleta em GET /v1/collections/{id} ou acompanhar o progresso ao vivo dela.
  • mandar wait, como mostra a próxima seção.

Espere os lugares novos

Com wait, a resposta espera até a coleta terminar ou os segundos acabarem, o que vier primeiro. Depois responde com os lugares que a coleta tem até ali. Quando não há nada para coletar, a resposta vem na hora.

No SDK, uma chamada com wait ganha a espera inteira e mais um tempo para a resposta chegar, mesmo que o timeout do cliente seja menor:

curl -X POST https://api.gmaps.dev/v1/places/search \
  -H "Authorization: Bearer $GMAPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "keywords": ["padaria"],
    "geo": { "location": "Curitiba, PR" },
    "wait": 60
  }'

Para esperar mais de 60 segundos, acompanhe o progresso ao vivo da coleta.

Quando a coleta não pode começar

Se seus créditos acabaram, ou se você já tem tantas coletas rodando quanto o plano permite, a busca responde mesmo assim com os lugares que já salvamos. collection vem null, e collectionError diz o motivo. Este é o fim de uma resposta assim:

{
  "collection": null,
  "collectionError": {
    "code": "QUOTA_EXCEEDED",
    "message": "You reached your daily limit of 250 credits. Retry after midnight UTC. Buying credits does not raise this limit.",
    "messageKey": "serverErrors.quotaDay",
    "params": { "limit": 250 },
    "details": {
      "scope": "day",
      "limit": 250,
      "resetAt": "2026-09-25T00:00:00.000Z",
      "retryAfterSecs": 3600
    }
  }
}

O código é sempre QUOTA_EXCEEDED, e o messageKey diz qual dos três motivos foi:

  • serverErrors.quotaDay: você chegou ao limite diário. Ele zera à meia-noite UTC, e créditos comprados não aumentam esse limite.
  • serverErrors.quotaMonth: os créditos do mês e os de pacotes acabaram. Os créditos do plano voltam no dia 1º, à meia-noite UTC, ou compre um pacote de créditos agora.
  • serverErrors.concurrency: você já tem tantas coletas rodando quanto o plano permite. Tente de novo quando uma terminar.

A busca em si dá certo do mesmo jeito. Os números de cada plano estão em Limites.

Quando uma busca é recusada

Uma busca recusada não começa nada e não custa nada. A resposta diz o motivo:

StatusCódigoMotivoO que fazer
400VALIDATION_ERRORFalta um campo, um valor está fora do intervalo ou o campo não existe, o geo tem mais de um formato, ou a região é grande demais.Leia message e details, corrija o campo e mande de novo.
422GEO_UNRESOLVEDNão dá para saber qual cidade brasileira o location quer dizer, ou o código IBGE não é de uma cidade.Acrescente o estado, corrija o código ou mande um ponto ou um retângulo.
429RATE_LIMIT_EXCEEDEDChamadas demais neste minuto.Espere os segundos indicados em retry-after e mande de novo.

Uma chave errada ou que falta é recusada como explica Autenticação. Ficar sem créditos nunca faz uma busca ser recusada. Erros lista todos os códigos.