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
}'| Campo | Padrão | Para que serve |
|---|---|---|
keywords | Obrigatório | O que procurar, como ["padaria"]. Cada termo é buscado separadamente. Maiúsculas, acentos e espaços sobrando não fazem diferença. |
geo | Obrigatório | Onde 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. |
minRating | null | Traz 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. |
limit | 20 | Quantos lugares você quer, de 1 a 120. É também o máximo que a busca pode custar, em créditos. |
wait | 0 | Por 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.
| Formato | O 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.candidateslista 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.candidatesmostra 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élimitlugares, 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 enewé nada.placesKnown: quantos lugares já tínhamos salvado na parte buscada, contando só os que passam nominRating. Todos eles saem de graça.collection: a coleta que está buscando o resto, ounullquando não precisou coletar nada. Coletas explica tudo sobre ela.collectionError: por que a coleta não pôde começar, ounull.meta.cached, em cada lugar:truequando 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:
| Status | Código | Motivo | O que fazer |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Falta 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. |
| 422 | GEO_UNRESOLVED | Nã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. |
| 429 | RATE_LIMIT_EXCEEDED | Chamadas 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.