GeoLoc API

Documentação da API

A API GeoLoc recebe CEP + número e devolve latitude, longitude, endereço completo e o nível de precisão. Com vários endereços, também devolve a ordem de visita e os links do Google Maps. Todas as respostas são JSON em UTF-8.

URL base https://api.qtux.com.br/geocep

O mesmo serviço responde também em https://api.orderfly.com.br/geocep.

RotaPara quê
GET /healthConfere se a API está no ar (não pede chave).
GET /localizarUm endereço, ou uma lista curta na própria URL.
POST /localizarAté 1.000 endereços em JSON, com rota opcional.
POST /rotaReplaneja a rota a partir da posição atual (GPS do app).

Autenticação

Toda chamada (exceto /health) leva a chave no cabeçalho X-Api-Key. Sem a chave, ou com uma chave errada, a resposta é 401.

Cabeçalho
X-Api-Key: SUA_CHAVE

Guarde a chave no servidor. Não coloque a chave em código JavaScript de página nem dentro do aplicativo publicado: quem abrir o código leva a chave. O app chama o seu servidor, e o seu servidor chama a API.

Verificar se está no ar

GET /health
Resposta
{"lOk": true}

Localizar um endereço

GET /localizar?sCep=13216340&sNum=161
ParâmetroTipoDescrição
sCeptextoCEP com ou sem traço: 13216-340 ou 13216340. Obrigatório.
sNumtextoNúmero da porta. 120A, 120-B e 120 fundos usam o 120; vazio, S/N ou KM viram "sem número".
sLogrtextoOpcional. Nome da rua, só para desempatar CEP que cobre várias ruas. Aceita abreviações: R. Dr. Bento Goncalves.
curl -H "X-Api-Key: SUA_CHAVE" \
     "https://api.qtux.com.br/geocep/localizar?sCep=13216340&sNum=161"
{
  "lOk": true, "iQtd": 1, "iAchou": 1,
  "aRet": [{
    "iSeq": 0,
    "sCep": "13216340", "sNum": "161",
    "lOk": true,
    "nLat": -23.17516, "nLon": -46.87214,
    "sPrec": "numero",
    "sLogr": "RUA HERMENEGILDO MARTINELI",
    "sLocal": "JARDIM DA FONTE",
    "iCodMun": 3525904, "sMun": "Jundiaí", "sUf": "SP",
    "lAmbig": false,
    "sEnd": "RUA HERMENEGILDO MARTINELI, 161, JARDIM DA FONTE, Jundiaí - SP, 13216-340",
    "sMapa": "https://www.google.com/maps/search/?api=1&query=-23.17516,-46.87214",
    "iOrdem": 1
  }]
}

Vários endereços na URL

Para testar no navegador ou mandar poucos endereços sem POST. Cada endereço é cep|numero (ou cep|numero|rua), separados por ; ou ,.

GET /localizar?sLista=13216340|161;13213073|177;13211000|575

Lista no corpo (recomendado)

POST /localizar Content-Type: application/json

Até 1.000 endereços por chamada. A resposta mantém a mesma ordem do envio (iSeq = posição na lista enviada).

Corpo
{
  "aEnd": [
    {"sCep": "13216-340", "sNum": "161"},
    {"sCep": "13213073",  "sNum": "177"},
    {"sCep": "90010150",  "sNum": "1200", "sLogr": "opcional"}
  ]
}

A lista também pode ir direto no corpo, sem o aEnd: [{"sCep": "...", "sNum": "..."}].

Ordem de visita e ida e volta

No /localizar, o primeiro endereço da lista é sempre a saída. Duas opções, no corpo do POST ou na URL (&lOtim=1&lVolta=1):

OpçãoPadrãoO que faz
lOtimfalseReordena as demais paradas pelo menor percurso total. Até 9 paradas testa todas as ordens; acima disso usa otimização (em média 0,25% acima da melhor ordem possível).
lVoltafalseVolta para a saída no fim (ida e volta). A otimização já conta o trecho de volta.
{
  "lOtim": true, "lVolta": true,
  "aEnd": [
    {"sCep": "13216340", "sNum": "161"},   // base: saida e chegada
    {"sCep": "13202020", "sNum": "235"},
    {"sCep": "13203000", "sNum": "221"},
    {"sCep": "13206010", "sNum": "2641"},
    {"sCep": "13208050", "sNum": "318"},
    {"sCep": "13209000", "sNum": "873"},
    {"sCep": "13211000", "sNum": "575"},
    {"sCep": "13213073", "sNum": "177"},
    {"sCep": "13215003", "sNum": "193"}
  ]
}
{
  "lOk": true, "iQtd": 9, "iAchou": 9,
  "lOtim": true, "lVolta": true,
  "sCusto": "rua",
  "nKm": 31.8, "nMin": 58.4,
  "aOrdem": [0, 7, 6, 5, 4, 3, 2, 1, 8, 0],
  "aMapa": ["https://www.google.com/maps/dir/?api=1&travelmode=driving&origin=...&waypoints=..."],
  "aRet": [ /* um item por endereco enviado, com iOrdem */ ]
}

aOrdem lista as posições da lista enviada (iSeq) na ordem de visita; com lVolta a saída aparece de novo no fim. Endereço não encontrado fica fora da rota. Os valores de nKm e nMin acima são ilustrativos.

Pelas ruas ou em linha reta: sCusto: "rua" significa que a ordem e os totais usam o tempo de carro pelas ruas (mão única, rios, rodovias), com nMin. Se o serviço de rotas estiver fora do ar, a API não falha: responde sCusto: "reta", com a distância em linha reta e sem nMin.

Replanejar a partir do GPS

POST /rota

Feita para o app de visitas: a cada parada concluída, o app manda a posição atual e o que falta, e recebe a nova ordem e o link das próximas paradas. A API não guarda estado.

CampoTipoDescrição
oOrig{nLat, nLon}Posição atual. Obrigatório. (0, 0) de GPS sem sinal é recusado.
oFim{nLat, nLon}Ponto final fixo, por exemplo a base. Opcional.
aParlistaParadas: com nLat/nLon (já localizadas) ou com sCep/sNum (a API localiza). sId é livre e volta igual. Até 1.000.
iParLink1 a 10Paradas por link do Maps. Padrão 4: o navegador do celular aceita só 3 paradas intermediárias + destino.
lOtimtrueReordena pelo menor percurso. false mantém a ordem enviada.
lComOrigfalseSem origem no link: o Google usa a posição do aparelho na hora do toque e já abre a navegação.
{
  "oOrig": {"nLat": -23.18989, "nLon": -46.88723},
  "oFim":  {"nLat": -23.17516, "nLon": -46.87214},
  "aPar": [
    {"sId": "CLI-102", "nLat": -23.22103, "nLon": -46.87383, "sPrec": "numero", "sEnd": "..."},
    {"sId": "CLI-205", "sCep": "13203000", "sNum": "221"}
  ],
  "iParLink": 4
}
{
  "lOk": true, "iQtd": 2, "iAchou": 2,
  "sCusto": "rua", "nKm": 9.4, "nMin": 21.0,
  "aOrdem": [0, 1],
  "sMapa": "https://www.google.com/maps/dir/?api=1&travelmode=driving&dir_action=navigate&...",
  "aMapa": ["..."],
  "aPar": [
    {"iSeq": 0, "sId": "CLI-102", "lOk": true, "iOrdem": 1, ...},
    {"iSeq": 1, "sId": "CLI-205", "lOk": true, "iOrdem": 2, ...}
  ]
}

No /rota, iOrdem = 1 é a próxima parada a fazer (no /localizar, 1 é a saída). sMapa é o link das próximas paradas; aMapa traz a rota inteira em links encadeados.

Fluxo recomendado no app

01

Montar o dia

Envie todas as paradas com sCep/sNum. Guarde no app o nLat, nLon, sPrec e sEnd que voltarem.

02

A cada visita feita

Mande o GPS atual em oOrig e só as paradas que faltam, já com as coordenadas (não precisa localizar de novo).

03

Seguir o link

Abra o sMapa: o Google já navega até as próximas paradas, na ordem calculada.

Campos da resposta

No topo

CampoTipoDescrição
lOkboolA chamada foi processada.
iQtd / iAchouintEndereços enviados / encontrados.
sCustotextorua (tempo de carro pelas ruas) ou reta (linha reta).
nKmnúmeroDistância total da rota. Em linha reta, o caminho real é maior.
nMinnúmeroTempo de carro estimado, só com sCusto: "rua".
aOrdemlistaPosições (iSeq) na ordem de visita.
aMapalistaLinks do Google Maps com a rota, já na ordem.
aRet / aParlistaUm item por endereço enviado, na mesma ordem (aPar no /rota).

Em cada endereço

CampoTipoDescrição
iSeqintPosição na lista enviada (começa em 0).
lOkboolfalse = CEP inválido ou inexistente; sErro explica.
nLat / nLonnúmeroCoordenada em graus decimais (WGS84), 6 casas.
sPrectextoDe onde veio a coordenada (ver Precisão).
iNumRefintSó com sPrec: "vizinho": o número usado como referência.
sLogr / sLocaltextoLogradouro e bairro/localidade.
iCodMun / sMun / sUfint / textoCódigo IBGE do município, nome e UF.
sEndtextoEndereço escrito: rua, número, bairro, cidade - UF, CEP.
sMapatextoLink do Google Maps com o alfinete no ponto.
iOrdemintPosição na rota calculada.
lAmbig / aOpcbool / listaCEP com várias ruas possíveis (ver abaixo).

Precisão (sPrec)

Do melhor para o pior. A API para no primeiro nível que encontrar.

ValorSignificado
numeroPonto do próprio número predial.
interpoladoCalculado entre os dois vizinhos do mesmo lado da rua (mesma paridade).
vizinhoSó há número de um lado: usa o mais próximo (iNumRef).
logradouroSem número válido: centro da rua.
cepCentro do CEP (CEP genérico com várias ruas e sem sLogr).
cep_baseCEP fora do Censo: ponto da base de CEPs complementar (qualidade menor).

Em Brasília (DF) quase não há número predial (endereços por quadra e bloco): lá a resposta costuma ser logradouro ou cep.

CEP com várias ruas

Em cidades pequenas, um CEP pode cobrir várias ruas. Se o número existir em mais de uma delas, a API devolve o centro do CEP com lAmbig: true e as ruas candidatas em aOpc. Reenvie com sLogr para desempatar.

Trecho da resposta
{
  "lOk": true, "sPrec": "cep", "lAmbig": true,
  "aOpc": [
    {"sLogr": "RUA DAS FLORES", "sLocal": "CENTRO", "nLat": -27.1, "nLon": -52.6},
    {"sLogr": "AVENIDA BRASIL", "sLocal": "CENTRO", "nLat": -27.1, "nLon": -52.6}
  ]
}

Links do Google Maps

  • Formato oficial (api=1): origem, até 9 paradas intermediárias e destino. Rotas maiores são divididas em vários links encadeados: cada um começa na última parada do anterior.
  • O Maps calcula o caminho entre paradas seguidas, mas não muda a ordem: a ordem é a da API.
  • Pontos com precisão fraca (logradouro, cep, cep_base) vão no link como endereço escrito, para o Google achar o número; os demais vão por coordenada.
  • Um endereço só vira um alfinete (busca), não uma rota.

Erros e limites

CódigoQuando
200Processado. Endereços não encontrados vêm com lOk: false e sErro no próprio item.
400Corpo inválido, lista vazia, oOrig/oFim inválidos.
401Chave ausente ou errada.
413Mais de 1.000 endereços ou corpo acima de 1 MB.
500Falha interna na consulta. Tente de novo; o detalhe fica só no registro do servidor.

Erros sempre no formato {"lOk": false, "sErro": "..."}. Mensagens por item: CEP invalido (8 digitos), CEP nao encontrado.

Exemplos completos

Localizar dois endereços e montar a rota com volta para a base.

curl -s https://api.qtux.com.br/geocep/localizar \
     -H "X-Api-Key: SUA_CHAVE" \
     -H "Content-Type: application/json" \
     -d '{"lOtim": true, "lVolta": true,
          "aEnd": [{"sCep": "13216340", "sNum": "161"},
                   {"sCep": "13213073", "sNum": "177"},
                   {"sCep": "13211000", "sNum": "575"}]}'
<?php
// A chave fica no servidor (variavel de ambiente ou arquivo de configuracao)
$sChave = getenv('GEOLOC_CHAVE');
$aCorpo = [
   'lOtim'  => true,
   'lVolta' => true,
   'aEnd'   => [
      ['sCep' => '13216340', 'sNum' => '161'],
      ['sCep' => '13213073', 'sNum' => '177'],
      ['sCep' => '13211000', 'sNum' => '575'],
   ],
];
$hCurl = curl_init('https://api.qtux.com.br/geocep/localizar');
curl_setopt_array($hCurl, [
   CURLOPT_POST           => true,
   CURLOPT_POSTFIELDS     => json_encode($aCorpo),
   CURLOPT_HTTPHEADER     => ['Content-Type: application/json', 'X-Api-Key: ' . $sChave],
   CURLOPT_RETURNTRANSFER => true,
   CURLOPT_TIMEOUT        => 60,
]);
$aResp = json_decode((string)curl_exec($hCurl), true);
foreach ($aResp['aRet'] ?? [] as $aEnd) {
   echo $aEnd['iOrdem'] ?? '-', ' ', $aEnd['sEnd'] ?? $aEnd['sErro'], "\n";
}
echo $aResp['aMapa'][0] ?? '', "\n";
// No servidor (Node 18+): a chave nunca vai para o navegador
const oResp = await fetch('https://api.qtux.com.br/geocep/localizar', {
   method: 'POST',
   headers: { 'Content-Type': 'application/json', 'X-Api-Key': process.env.GEOLOC_CHAVE },
   body: JSON.stringify({
      lOtim: true, lVolta: true,
      aEnd: [
         { sCep: '13216340', sNum: '161' },
         { sCep: '13213073', sNum: '177' },
         { sCep: '13211000', sNum: '575' }
      ]
   })
});
const oJson = await oResp.json();
oJson.aRet.forEach(o => console.log(o.iOrdem, o.sEnd, o.nLat, o.nLon));
console.log(oJson.aMapa[0]);
import os, requests

oResp = requests.post(
    'https://api.qtux.com.br/geocep/localizar',
    headers={'X-Api-Key': os.environ['GEOLOC_CHAVE']},
    json={'lOtim': True, 'lVolta': True,
          'aEnd': [{'sCep': '13216340', 'sNum': '161'},
                   {'sCep': '13213073', 'sNum': '177'},
                   {'sCep': '13211000', 'sNum': '575'}]},
    timeout=60)
oJson = oResp.json()
for oEnd in oJson['aRet']:
    print(oEnd.get('iOrdem'), oEnd.get('sEnd') or oEnd.get('sErro'))
print(oJson['aMapa'][0])

Boas práticas

Mande em lote

Um POST com 300 endereços é muito mais rápido que 300 chamadas de um endereço.

Guarde a coordenada

Endereço de cliente muda pouco: salve nLat, nLon e sPrec no seu cadastro e localize de novo só quando o endereço mudar.

Olhe o sPrec

Para cep e cep_base, peça confirmação ao usuário ou complete o cadastro com o nome da rua.