ZapCarZapCar
Voltar para a API
RESTJSON · UTF-8API v1America/São_Paulo

Documentação da API

Integre a consulta veicular ZapCar no seu sistema — do zero à primeira consulta. Autenticação por chave, cobrança por saldo, respostas em JSON e PDF.

Base URLhttps://api.zapcarconsulta.com.br

Novidades da API

Últimas mudanças na API. A maioria é aditiva; o item marcado Atenção pode exigir ajuste de quem já integra.

11/09/2026

  • Novo — a logo da sua empresa nos PDFs (white-label). Quem cadastra a logo no portal (Configurações › Personalização dos PDFs) passa a receber, no GET /v1/consultas/:id/pdf, o relatório com a identidade da própria empresa. Vale para consulta, consulta-completa, gravame, renajud e para os relatórios desenhados pela ZapCar (debitos, debitos-estadual, decodificacao-chassi, decodificacao-motor, score-credito). Quando o documento é o emitido pela base, a logo é aplicada na cópia servida, no topo da 1ª página, e o conteúdo é deslocado para baixo (nada é encolhido); o arquivo gravado não muda. Documentos oficiais (crlv, ATPV-e, código de segurança) seguem intactos. A logo é sempre a do dono da chave de API. Nada mudou no endpoint, no content-type nem no JSON de dados — mas se você extrai texto do PDF por posição, revise: a 1ª página ganha uma faixa no topo e a paginação pode mudar. Sem logo cadastrada, o PDF continua exatamente como era.

09/09/2026

  • Consulta Simples — nova base estadual; o PDF passa a ser desenhado pela ZapCar no mesmo layout. O serviço consulta passou a ser atendido por outra base estadual. Nenhum campo mudou de nome ou tipo no JSON de dados; o tri-estado (vazio significa não informado, nunca “nada consta”) e o bloco nao_verificado continuam iguais. Duas mudanças aditivas: comunicacaodevendas volta a vir preenchido (a base nova informa o comunicado de venda) e exerciciolicenciamento volta a trazer o ano do último licenciamento; origem_de_emplacamento passa a vir vazio. O GET /v1/consultas/:id/pdf continua entregando um documento no mesmo layout de antes (mesmas seções: dados da consulta, quadro de avisos, resumo, informações do veículo, multas e débitos, restrições, proprietário), agora gerado pela ZapCar a partir dos dados, porque a base nova não emite documento próprio. Itens que a base não respondeu saem como NÃO INFORMADO, não como “nada consta”. Se você usa o JSON (recomendado), nada a fazer.
  • Consulta Simples — nova versão da base estadual (V2). O serviço consulta passou a ser atendido pela versão 2 da base estadual da mesma fonte (segunda troca de versão em dois dias; a anterior foi em 08/09). Nenhum campo mudou de nome ou tipo no JSON de dados; o tri-estado (vazio significa não informado, nunca “nada consta”) e o bloco nao_verificado continuam iguais. Campos condicionais como exerciciolicenciamento, origem_de_emplacamento e os valores de débito seguem vindo quando a base os devolve. O GET /v1/consultas/:id/pdf continua entregando o documento emitido pela base; como a versão mudou, o layout e a paginação do arquivo podem variar de novo — se você extrai texto do PDF por posição, revise. Se você usa o JSON (recomendado), nada a fazer. Um eventual retorno à versão anterior é feito do nosso lado, sem mudança no contrato.
  • Janela de polling — Atenção. A base estadual que atende a Consulta Simples e a Consulta Completa passou a levar até ~2 minutos numa placa consultada pela primeira vez no dia (medido em produção em 09/09; a mesma placa, repetida, volta em segundos). Quem desiste em 60s, como o exemplo antigo desta página fazia, abandona uma consulta que vai concluir e que já foi debitada. Recomendação nova: intervalo de 3–5s e teto de 5 minutos — ou, melhor, o webhook query.completed, que dispensa o polling. processando nunca é erro. Nada mudou no contrato da API: mesmos endpoints, mesmos campos.
  • Correção — falhas em série por “serviço indisponível”. Entre 08/09 (10:46) e 09/09 (09:30), consultas do serviço consulta falharam com erro_codigo: CIRCUIT_OPEN e retryable: true sem que a base fosse consultada — um defeito interno nosso na proteção contra fornecedor fora do ar, já corrigido. Todas foram estornadas automaticamente. Se você guardou essas placas como problemáticas, pode reprocessá-las.

08/09/2026

  • Consulta Simples — trocou a versão da base estadual. O serviço consulta passou a ser atendido por outra versão da base estadual da mesma fonte. Nenhum campo mudou de nome ou tipo no JSON de dados, e o tri-estado (vazio significa não informado, nunca “nada consta”) continua igual. Mudança aditiva: exerciciolicenciamento e origem_de_emplacamento, que vinham sempre vazios desde 28/08, voltam a vir preenchidos quando a base os devolve. O GET /v1/consultas/:id/pdf continua entregando o documento emitido pela base; como a versão da base mudou, o layout e a paginação do arquivo podem variar — se você extrai texto do PDF por posição, revise. Se você usa o JSON (recomendado), nada a fazer.

02/09/2026

  • Novo — erro_codigo na consulta que falhou. O GET /v1/consultas/:id com status: "erro" passa a trazer um código estável do motivo (PROVIDER_TIMEOUT, QUERY_NOT_FOUND, INVALID_PLATE…) e o campo erro passa a descrever o que aconteceu em vez da frase genérica. O retryable agora deriva desse código. A lista completa está em Consultar o resultado.
  • Novo — proteção contra pedido duplicado. Se você enviar o mesmo serviço para o mesmo identificador (placa, chassi, motor ou documento) enquanto a solicitação anterior ainda está em processamento, o POST /v1/consultas responde 200 com reaproveitada: true, o id da consulta existente e valor_cobrado: 0nada é cobrado de novo. Vale para retry de rede e para reenvio por engano. Continue usando Idempotency-Key quando puder: ela cobre também o caso em que a primeira consulta já terminou.
  • Comportamento — falha definitiva termina mais rápido. Quando a base responde “veículo não encontrado” ou o dado enviado é inválido, a consulta vai para erro na primeira tentativa, sem as retentativas internas que antes atrasavam o resultado em vários minutos. Falhas transitórias (base fora do ar, timeout) continuam sendo retentadas.
  • Comportamento — estorno automático. Toda consulta que termina em erro é estornada automaticamente; um processo de reconciliação confirma o estorno mesmo se houver falha momentânea no momento do erro. O campo erro informa quando o valor foi devolvido ao saldo.
  • Novo — FIPE dentro da Consulta Completa. Envie "incluir_fipe": true no POST /v1/consultas com servico: "consulta-completa" e o resultado traz dados.fipe, no mesmo formato do POST /v1/fipe — uma chamada só. O preço da FIPE é somado ao da Completa (veja opcionais.incluir_fipe.preco_adicional no GET /v1/servicos). Se a FIPE não estiver disponível para a placa, a Completa é entregue normalmente, dados.fipe vem null com dados.fipeErro, e só a parcela da FIPE é estornada.

29/08/2026

  • Atenção — mudou o layout dos PDFs que a ZapCar desenha. Em debitos, debitos-estadual, decodificacao-chassi, decodificacao-motor e score-credito, o GET /v1/consultas/:id/pdf passou a entregar o arquivo no mesmo padrão visual dos documentos emitidos pelas bases — seções em faixa, grade “rótulo: valor” e quadro de avisos —, para que todos os PDFs da plataforma tenham a mesma cara. Nada mudou no endpoint, no content-type, no fluxo nem no JSON de dados: muda o layout e a paginação. Se você extrai texto do PDF por posição, recorta páginas ou espera um número fixo de folhas, revise. Se você usa o JSON (recomendado), nada a fazer.
  • Gravame e RENAJUD — o mesmo vale para o relatório de reserva. Esses dois entregam o documento da base desde 28/08; o relatório ZapCar que aparece quando a base não emite documento também passou para o novo padrão visual. Nenhuma mudança de campo.
  • Score ZapCar — onde ele está. Lembrete, não mudança: o score de risco 0–100 da Consulta Completa vem no JSON (risco no GET /v1/consultas/:id) e no painel. Ele não está no PDF desde que a Completa passou a entregar o documento da base — se você o exibe para o seu usuário, leia do JSON.

28/08/2026

  • Atenção — o PDF mudou de documento. Em consulta, consulta-completa, gravame e renajud, o GET /v1/consultas/:id/pdf passou a entregar o documento emitido pela própria base consultada, e não mais o laudo desenhado pela ZapCar. O endpoint, o content-type e o fluxo são os mesmos — o que muda é o layout e a paginação do arquivo. Se você extrai texto do PDF por posição, recorta páginas ou espera um número fixo de folhas, revise. O JSON de dados não mudou por causa disso.
  • Atenção — serviço historico-proprietarios descontinuado. O slug saiu do catálogo e novas consultas são recusadas. O campo historico_proprietarios não é mais devolvido em consulta nenhuma, inclusive na Completa: nenhuma fonte do stack entrega a cadeia de donos anteriores. Todas devolvem apenas o proprietario atual. A relação de donos consta na certidão de prontuário do DETRAN de emplacamento.
  • Gravame — mais de um registro por veículo. dados.ocorrencias[] passa a trazer todos os registros de gravame do veículo, cada um com agente financeiro, documento, número do gravame, contrato e datas. Os campos de topo continuam descrevendo o registro principal, sem mudança de nome ou tipo — é aditivo. O documento oficial da base lista todos.
  • Gravame — novos campos do veículo. marcamodelo, cor, municipio, combustivel e motor passam a vir preenchidos quando a base os devolve. Antes não existiam nesse serviço.
  • RENAJUD — base sem resposta agora é ERRO, não “nada consta”. Quando a base de restrições judiciais não responde, a consulta falha (com estorno) em vez de devolver um veículo limpo. Se o seu código tratava resposta vazia como “sem bloqueio”, esse caso deixou de existir — passe a tratar o erro.
  • Novo — retryable na consulta que falhou. O GET /v1/consultas/:id com status: "erro" passa a trazer retryable: true quando a base caiu e a mesma consulta tende a funcionar minutos depois, false quando repetir só muda a fatura (placa inexistente, contrato, credencial). O campo é OMITIDO quando não classificamos — ausente significa “não sabemos”, e não false. Trate os três casos.
  • Gravame — codigofinanceira agora vem null, e o número do registro ganhou campo próprio. A base atual não devolve código de financeira — devolve o CNPJ dela, que já estava em documentofinanceira. O número do gravame no SNG, que antes ia empurrado para codigofinanceira por falta de lugar, passou a ter o campo numerogravame. Se você lia codigofinanceira esperando o número do registro, migre para numerogravame.
  • Atenção — documento do proprietário pode vir MASCARADO. Algumas bases devolvem o CPF/CNPJ do titular parcialmente ("***.490.411-**"). Antes nós removíamos a pontuação de tudo, e a máscara virava "490411" — seis dígitos que pareciam um documento e não eram. Agora a pontuação só sai quando restam 11 ou 14 dígitos; fora disso o valor volta como a base mandou. Valide o tamanho antes de usar: veiculo.proprietario.documento nem sempre é um documento completo, e tipoDocumento vem null quando não dá para afirmar.
  • Consulta Simples — o PDF também passou a ser o documento da base. Vale o mesmo aviso do item de cima sobre layout e paginação. Quando a base não emite documento, o relatório ZapCar continua sendo entregue — o endpoint responde igual nos dois casos.
  • Consulta Simples — duas reduções de cobertura. comunicacaodevendas passou a vir vazio (a fonte atual não tem o campo; vazio significa não informado, nunca “nada consta”). E datalicenciamento traz a data limite do licenciamento, não mais o ano do exercício — exerciciolicenciamento vem vazio.

27/08/2026

  • Atenção — dados.debitos da Consulta Completa deixou de sair zerado. O bloco sempre devolvia 0 em todos os itens; agora traz os valores reais apurados na consulta, mais estado por item (SEM_DEBITO, CONSTA, CONSTA_SEM_VALOR, NAO_INFORMADO) e total_parcial. Se o seu código assumia zero — somando esse bloco a outro, ou pulando a seção de débitos —, revise. total_parcial: true significa que existe débito sem valor informado: o total é um piso. E debitos: null é “não verificado”, não “não deve”.
  • Novo endpoint — imagens da Consulta Completa. GET /v1/consultas/:id/imagens/:tipo/:indice entrega as fotos de leilão, as imagens de vistoria/anúncio e o certificado do laudo CSV. Elas não viajam mais dentro do JSON (o retorno passava de 8 MB): fotosLeilao agora é uma lista de indice + url. Detalhes em Imagens da Consulta Completa.
  • Novo bloco dados.renainf. Infrações RENAINF cometidas fora da UF de emplacamento, com auto, enquadramento, órgão autuador, valor e situação de pagamento. Três estados: null = base não respondeu, total: 0 = respondeu sem infração, total: N = há infrações. Não some RENAINF aos débitos estaduais — são universos diferentes, e a soma inventa um total que não existe.
  • Novo bloco dados.csv — Certificado de Segurança Veicular. Sinal forte: o CSV é exigido depois de alteração ou reparo estrutural. O campo observacao traz a ressalva da fonte e deve ser repassada junto com o dado. A maior parte do conteúdo está dentro da imagem do certificado, em imagem_url.
  • Novos blocos dados.blocos e dados.offline — o terceiro estado, explícito. blocos diz, base por base, se ela respondeu e quantas ocorrências trouxe; offline: true avisa que a resposta veio de cache da fonte, não da base viva. Use os dois antes de escrever “nada consta” para o seu cliente.
  • Novo bloco dados.informacoesAvulsas. Pares descrição/valor do registro (status e vencimento do licenciamento, instituição alienadora, indicadores extrajudiciais). Vem a lista inteira, então campo novo aparece aqui em vez de sumir.
  • restricoes ganhou restricaoGeral e veiculoBaixado. restricaoGeral é um indicador amplo e não equivale a restrição administrativa — ele acende até por multa RENAINF. O texto real está em restricao1..4. Tratar os dois como sinônimo inventa impedimento de transferência.
  • Reforço de tri-estado. Mais campos de restricoes passam a devolver null quando a fonte não verificou o item, em vez de false. if (!restricoes.sinistro) trata “não verificado” como “sem sinistro” — teste os três estados.
  • O que a Completa não traz. anoUltimoLicenciamento pode vir null, e o bloco leilao (lote, pátio, data, comitente) costuma vir null — as fotos continuam chegando em fotosLeilao. O campo historicoProprietarios deixou de existir em 28/08/2026: a cadeia de donos anteriores não é mais entregue por nenhuma consulta — as fontes atuais devolvem apenas o proprietário atual.

26/08/2026

  • Consulta Completa — bloco veiculo mais completo. O bloco normalizado ganhou ficha_pesados (eixos, capacidade de carga, peso bruto total, capacidade de tração e tanque — preenchido só em caminhão, ônibus e implemento) e faturamento (tipo_documento, documento e uf de quem recebeu o veículo 0km). Aditivo: nenhum campo existente mudou de nome ou de tipo.
  • Leilão agora tem três fontes. veiculo.leilao ganhou o contador fotos. A foto do veículo em pátio é prova positiva por si só: quando fotos > 0, o veículo passou por leilão mesmo com ocorrencias: 0 — isso significa apenas que a base de ocorrências não trouxe lote e comitente. leilao: null continua sendo “nenhuma fonte verificou”.
  • Débito confirmado sem valor agora aparece. Quando a base do estado responde “existe débito, valor não informado”, o item entra em veiculo.debitos[] com valor_centavos: 0 e valor_informado: false. Antes ele era descartado e a resposta parecia dizer “sem débito” sobre um carro devendo. Nunca some valor_centavos sem olhar valor_informado: o total é um piso, não o devido.
  • Atenção — veiculo.sinistro pode vir null. O provedor devolve null nesse campo quando não verifica, e passamos a preservar isso em vez de converter para false. Se o seu código faz if (!veiculo.sinistro), ele passou a tratar “não verificado” como “sem sinistro”. Teste os três estados: true, false e null. O mesmo vale para recall.
  • CRLV — Mato Grosso do Sul (MS) agora atendido. A emissão do CRLV de MS entrou na API — exige só placa + uf, sem CPF nem RENAVAM. O estado tinha preço cadastrado mas nenhum emissor; agora tem. Confirme o preço em GET /v1/servicoscrlv.preco_por_uf.
  • Novo serviço — 2ª via do ATPV-e. Emissão da Autorização para Transferência de Propriedade de Veículo eletrônica. Slug atpve; exige a placa — sem uf, sem cpf, sem renavam. Entrega documento em PDF (não devolve JSON de dados). Cobertura nacional e preço único, diferente do crlv, que é por UF.
  • É emissão, não consulta. Leva cerca de 1 minuto. O status fica processando nesse intervalo — amplie o polling ou use o webhook query.completed em vez de desistir em 30s.
  • Consulta Completa — fotos de leilão. A Completa passa a trazer o bloco dados.fotosLeilao: imagens do veículo em pátio de leilão, quando existirem, e também no PDF do laudo. É aditivo — nenhum campo existente mudou. Três estados, iguais aos do bloco leilao: null = a base de imagens não respondeu (não verificado), [] = respondeu e não há foto, lista = fotos encontradas. null e [] significam coisas diferentes: ausência de foto não prova ausência de leilão.

06/08/2026

  • Novo serviço — Score de Crédito. Análise de crédito e localização por documento (CPF ou CNPJ, sem placa): dados da Receita Federal, score, renda presumida, e-mail/endereço presumidos e participação em empresas. Slug score-credito; entrega JSON (dados) e PDF.
  • Novo serviço — Débitos Estaduais. Débitos do veículo direto na base do estado (IPVA, licenciamento, DPVAT, multas e dívida ativa). Slug debitos-estadual; exige placa, uf e renavam — e, em algumas UFs, documento (CPF/CNPJ) ou chassi (ver debitos-estadual.campos_por_uf em GET /v1/servicos). Não confundir com debitos (código de barras, agregado).

12/08/2026

  • CRLV — Pernambuco (PE) agora atendido. A emissão do CRLV de PE já está disponível na API — exige só placa + uf. Confirme o preço e os campos em GET /v1/servicoscrlv.preco_por_uf.

04/08/2026

  • Novo endpoint — Tabela FIPE por placa. POST /v1/fipe devolve os valores FIPE do veículo na hora (endpoint síncrono: sem id, sem polling, sem PDF). Custa R$ 0,25 por consulta, com estorno automático se falhar ou não houver FIPE. Detalhes em POST /v1/fipe.

31/07/2026

  • Novos serviços — Decodificação de Chassi e de Motor. Decodificam um chassi ou número de motor (sem placa) em dados do veículo + sugestões da Tabela FIPE. Slugs decodificacao-chassi e decodificacao-motor; entregam JSON (dados) e PDF.
  • CRLV — mais estados atendidos. Consulte sempre a lista viva em GET /v1/servicoscrlv.preco_por_uf.
  • CRLV — 2ª via automática (melhoria transparente). Se a 1ª via falhar, tentamos um provedor alternativo automaticamente. Você não muda nada — só aumenta a taxa de sucesso.
  • Catálogo mais preciso. O requer de cada serviço em GET /v1/servicos agora reflete os campos certos (ex.: chassi/motor nas decodificações e os campos por UF no CRLV).
Atenção — quem emite CRLV: alguns estados passaram a exigir cpf (CPF/CNPJ) e/ou renavam além de placa e uf. Hoje: CPF + RENAVAM em BA, PA, SP, TO, MT · só CPF/CNPJ em MG · só RENAVAM em PR, PI · só placa + uf em RJ e demais. Emitindo sem os campos exigidos, a resposta é 400 CRLV_DADOS_OBRIGATORIOS. Antes de emitir, leia o requer da UF em crlv.preco_por_uf.

Introdução

A API ZapCar coloca a consulta veicular dentro do seu sistema. Você envia a placa (ou o chassi/motor na decodificação) e, quando o serviço pedir, a UF, o RENAVAM ou o CPF/CNPJ; nós consultamos as fontes oficiais e devolvemos o resultado em JSON e/ou PDF — o mesmo dado que aparece no painel.

É uma API REST comum: você faz chamadas HTTPS e recebe respostas em JSON (UTF-8). Não precisa instalar nada — qualquer linguagem que faça requisições HTTP (JavaScript, PHP, Python, etc.) já serve. O fuso das datas é America/São_Paulo.

Nove serviços devolvem dados em JSON — o mesmo conteúdo que alimenta o nosso PDF, para você montar o seu próprio relatório. A consulta simples e a completa vêm já estruturadas por seção (veículo, restrições, débitos, proprietário…); gravame, RENAJUD, débitos, débitos estaduais, as decodificações de chassi/motor e o Score de Crédito vêm no JSON do provedor. CRLV e código de segurança saem apenas como PDF (são documentos oficiais). Veja o modelo de cada resposta em Catálogo de serviços.

Endpoints de relance

Primeiros passos

Do zero à primeira consulta em 4 passos. Se você só quer confirmar que está tudo certo, faça os passos 1 e 2.

1

Gere sua chave

Crie sua conta, adicione saldo e vá em Integração → API no painel. Clique em Gerar nova chave e copie o valor zc_live_…. Ele aparece uma única vez — guarde num lugar seguro.
2

Faça a primeira chamada (teste rápido)

Peça a lista de serviços. Se responder 200, sua chave é válida e a API está no ar.
teste — deve responder 200
curl https://api.zapcarconsulta.com.br/v1/servicos \
  -H "Authorization: Bearer zc_live_sua_chave_aqui"
3

Crie uma consulta

Envie o serviço e a placa. A resposta traz um id e o status processando. Guarde esse id.
cria e devolve um id
curl -X POST https://api.zapcarconsulta.com.br/v1/consultas \
  -H "Authorization: Bearer zc_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"servico":"consulta","placa":"ABC1D23"}'
4

Veja o resultado

Consulte o id até o status virar concluido — aí vêm os dados e a pdf_url.
repita até concluido
curl https://api.zapcarconsulta.com.br/v1/consultas/COLE_O_ID \
  -H "Authorization: Bearer zc_live_sua_chave_aqui"
Só quer clicar em vez de digitar? Pule para Testar no Postman — tem uma coleção pronta para importar.

Autenticação

Toda chamada precisa da sua chave no cabeçalho Authorization, no formato Bearer. (Também aceitamos o cabeçalho X-API-Key.)

cabeçalho recomendado
Authorization: Bearer zc_live_sua_chave_aqui
alternativa
X-API-Key: zc_live_sua_chave_aqui

Sem chave válida, a resposta é 401. Trate a chave como uma senha: guarde no servidor (variável de ambiente), nunca no navegador, em apps ou em repositórios. Se desconfiar que vazou, revogue e gere outra — leva segundos.

Como funciona — assíncrono em 2 passos

A consulta depende de fontes externas e leva de alguns segundos a alguns minutos. Por isso você não recebe o resultado na mesma resposta: primeiro cria a consulta, depois busca o resultado.

1

Crie a consulta

POST em /v1/consultas com o serviço e a placa. O saldo é debitado na hora e você recebe um id com status "processando".

2

Busque o resultado

GET em /v1/consultas/{id}, repetindo a cada poucos segundos até o status virar "concluido" (aí vêm os dados e o pdf_url) ou "erro".

⚠️ A diferença entre criar e consultar (erro mais comum)

São endpoints parecidos, mas o método e a URL mudam. Trocar isso é o motivo nº 1 de receber 404 Rota não encontrada:

Você querMétodoURL
CriarPOST/v1/consultas (sem id)
Ver resultadoGET/v1/consultas/{id} (com id)
Baixar PDFGET/v1/consultas/{id}/pdf
POST leva o id? Não. POST /v1/consultas/{id} não existe → 404. Para ver um resultado use GET com o id; para criar use POST sem o id.

Acompanhar o resultado (polling)

Depois de criar a consulta, você "pergunta" pela resposta de tempos em tempos até ela ficar pronta. Isso se chama polling. Uma boa regra: consultar a cada 3–5 segundos, com teto de 5 minutos. Menos que isso abandona consulta que vai concluir — e que já foi debitada.

Em dia normal a maioria conclui em segundos. A base estadual (Consulta Simples e Completa), porém, chega a ~2 minutos numa placa consultada pela primeira vez no dia — a mesma placa, repetida, volta em segundos. O status passa por:

statusO que significaO que fazer
processandoAinda buscando na base consultada.Espere 3–5s e consulte de novo. Pode levar até ~2 min na base estadual.
concluidoPronto. Vêm "dados" (quando o serviço tem JSON) e "pdf_url".Use os dados / baixe o PDF.
erroNão foi possível concluir.O valor é estornado. Reenvie a consulta.
Não peça o pdf_url enquanto o status for processando — ele ainda não existe e a API responde 400 PDF_NOT_READY. Espere o concluido.
CRLV-e assíncrono (alguns estados). A emissão eletrônica do CRLV em CE, DF, ES, PB, RJ, RN, RS e SC é processada pelo DETRAN de forma assíncrona — pode levar de segundos a alguns minutos. Nesses casos o status fica processando por mais tempo: não trate isso como erro nem desista em 60s. Duas formas de acompanhar: (1) aumente a janela de polling; ou, melhor, (2) cadastre um webhook e reaja ao evento query.completed — aí você só chama GET /v1/consultas/:id quando o resultado já está pronto, sem polling longo. Se a emissão falhar, o valor é estornado e o status vira erro.

Veja um exemplo de polling pronto na seção Exemplos em código.

Cobrança e saldo

Cada consulta desconta do saldo da sua carteira o valor vigente do catálogo da API (veja em GET /v1/servicos). Os valores da API seguem o catálogo de integrações (GET /v1/servicos) e podem diferir das condições disponíveis no Portal do Cliente. Exemplo: a Tabela FIPE é gratuita no painel e cobrada por requisição na API. O débito acontece no POST, antes do processamento.

Sem saldo suficiente, a criação falha com 402 SALDO_INSUFICIENTE e nada é cobrado. Se a consulta for criada mas falhar no provedor, o valor é estornado automaticamente — você não paga por erro.

Você adiciona saldo pelo portal (recarga por Pix ou assinatura mensal, que recarrega sozinha). A API apenas consome — nunca gera cobrança nova.

Limites de uso

A API aceita por padrão até 120 requisições por minuto. Ao ultrapassar, as chamadas recebem 429 RATE_LIMITED — respeite o intervalo e reenvie. Precisa de mais volume? Fale com o suporte que ajustamos.

Listar serviços e preços

GET/v1/servicos

Retorna o catálogo de serviços com o preço vigente de cada um. Para o CRLV, o preço vem por UF. É a chamada mais simples — use-a para testar sua chave.

resposta 200
{
  "servicos": [
    { "servico": "consulta", "nome": "Consulta Simples", "requer": ["placa"], "preco": 5.99 },
    { "servico": "consulta-completa", "nome": "Consulta Completa", "requer": ["placa"], "preco": 38.99,
      "opcionais": { "incluir_fipe": { "preco_adicional": 0.25, "descricao": "Embute a Tabela FIPE da placa em dados.fipe." } } },
    { "servico": "gravame", "nome": "Gravame", "requer": ["placa"], "preco": 6.99 },
    { "servico": "renajud", "nome": "Renajud", "requer": ["placa"], "preco": 6.99 },
    { "servico": "debitos", "nome": "Débitos + Código de Barras", "requer": ["placa"], "preco": 19.99 },
    { "servico": "debitos-estadual", "nome": "Débitos Estaduais", "requer": ["placa","uf"], "preco": 2.99,
      "observacao": "Serviço por estado. Os campos exigidos variam por UF — ver campos_por_uf.", "campos_por_uf": [ "..." ] },
    { "servico": "decodificacao-chassi", "nome": "Decodificação de Chassi", "requer": ["chassi"], "preco": 2.99 },
    { "servico": "decodificacao-motor", "nome": "Decodificação de Motor", "requer": ["motor"], "preco": 2.99 },
    { "servico": "score-credito", "nome": "Score de Crédito", "requer": ["documento"], "preco": 12.99 },
    { "servico": "fipe", "nome": "Tabela FIPE", "requer": ["placa"], "preco": 0.25,
      "sincrono": true, "endpoint": "POST /v1/fipe",
      "observacao": "Retorna os valores FIPE do veículo na hora (sem PDF, sem polling)." },
    { "servico": "codigo-seguranca", "nome": "Código de Segurança", "requer": ["placa","renavam"], "preco": 19.99 },
    { "servico": "atpve", "nome": "2ª via do ATPV-e", "requer": ["placa"], "preco": 39.99 },
    { "servico": "crlv", "nome": "CRLV Digital", "requer": ["placa","uf"],
      "observacao": "Alguns estados exigem CPF/CNPJ e/ou RENAVAM — ver \"requer\" de cada UF.",
      "preco_por_uf": [
        { "uf": "AP", "preco": 29.99, "requer": ["placa","uf"] },
        { "uf": "BA", "preco": 29.99, "requer": ["placa","uf","cpf","renavam"] },
        { "uf": "CE", "preco": 44.99, "requer": ["placa","uf"] },
        { "uf": "GO", "preco": 29.99, "requer": ["placa","uf"] }
      ] }
  ],
  "desconto_api": null
}
Exemplo gerado com o catálogo e os preços vigentes no momento em que esta página foi renderizada. Consulte sempre GET /v1/servicos com a sua chave — preços personalizados e o Grupo de Desconto da API alteram o valor cobrado.

Consultar saldo

GET/v1/saldo

Retorna o saldo atual da sua carteira, em reais. Útil para monitorar e recarregar antes de acabar.

resposta 200
{ "saldo": 465.01 }

Criar uma consulta

POST/v1/consultas

Cria a consulta, debita o saldo e devolve o identificador para você acompanhar. O corpo (body) vai em JSON:

CampoTipoObrig.Descrição
servicostringSimSlug do serviço (ver Catálogo). Ex.: "consulta", "gravame", "crlv".
placastringDependePlaca (com ou sem hífen). Ex.: "ABC1D23". Obrigatória em todos, exceto decodificação de chassi/motor.
chassistringDependeObrigatório só em "decodificacao-chassi".
motorstringDependeObrigatório só em "decodificacao-motor".
ufstringDependeUF de 2 letras. Obrigatória no "crlv".
renavamstringDependeRENAVAM. Obrigatório no "codigo-seguranca" e em CRLV de algumas UFs.
cpfstringDependeCPF/CNPJ do proprietário. Obrigatório no CRLV de algumas UFs (ver Catálogo).
incluir_fipebooleanNãoSó em "consulta-completa": embute a Tabela FIPE da placa em dados.fipe (mesmo resultado do POST /v1/fipe). Cobra o preço da FIPE junto; se a FIPE falhar, só essa parcela é estornada e dados.fipe vem null com dados.fipeErro.
requisição
curl -X POST https://api.zapcarconsulta.com.br/v1/consultas \
  -H "Authorization: Bearer zc_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{ "servico": "consulta", "placa": "ABC1D23" }'
resposta 201
{
  "id": "6f1c2e2a-1b3d-4a9e-8f7c-2a1b3c4d5e6f",
  "servico": "consulta",
  "status": "processando",
  "valor_cobrado": 5.99,
  "saldo_restante": 465.01,
  "resultado_url": "/v1/consultas/6f1c2e2a-..."
}
resposta 402 — sem saldo
{ "erro": "Saldo insuficiente", "codigo": "SALDO_INSUFICIENTE", "saldo": 12.50 }
resposta 200 — solicitação idêntica já em andamento (nada cobrado)
{
  "id": "6f1c2e2a-1b3d-4a9e-8f7c-2a1b3c4d5e6f",
  "servico": "consulta",
  "status": "processando",
  "valor_cobrado": 0,
  "saldo_restante": 465.01,
  "resultado_url": "/v1/consultas/6f1c2e2a-...",
  "reaproveitada": true,
  "mensagem": "Essa solicitação já está sendo processada. Você não precisa realizar uma nova."
}
Uma consulta idêntica (mesmo serviço e mesmo identificador) enviada enquanto a anterior ainda está em processamento devolve a existente com reaproveitada: true — trate como sucesso e acompanhe o id retornado. Depois que a anterior termina, um novo POST cria (e cobra) uma consulta nova.

Consultar o resultado

GET/v1/consultas/{id}

Devolve o estado atual da consulta. Use o id que veio no passo anterior. Enquanto processa, o status é processando; quando conclui, inclui os dados (para os serviços com JSON) e a pdf_url.

Exemplo ilustrativo — resposta 200 — concluído
{
  "id": "6f1c2e2a-...",
  "servico": "consulta",
  "placa": "ABC1D23",
  "status": "concluido",
  "valor_cobrado": 5.99,
  "criado_em": "2026-07-23T09:14:02-03:00",
  "dados": { "dados_do_veiculo": { "...": "..." }, "restricoes_e_impedimentos": { "...": "..." } },
  "proprietario": { "nome": "JOAO DA SILVA", "documento": "12345678901" },
  "ultimo_licenciamento": 2024,
  "nao_verificado": ["restricao_judicial", "restricao_administrativa"],
  "pdf_url": "/v1/consultas/6f1c2e2a-.../pdf"
}
Exemplo ilustrativo — bloco veiculo normalizado (consulta, consulta-completa, gravame, renajud)
{
  "veiculo": {
    "placa": "ABC1D23", "renavam": "12345678901", "chassi": "9BWZZZ377VT004251",
    "marca": "VW", "modelo": "POLO SENSE", "ano_fabricacao": 2019, "ano_modelo": 2020,
    "cor": "BRANCA", "combustivel": "FLEX", "especie": "PASSAGEIRO", "categoria": "PARTICULAR",
    "municipio": "SALVADOR", "uf": "BA",

    "proprietario": { "nome": "JOAO DA SILVA", "documento": "12345678901" },
    "situacao": "EM CIRCULACAO", "baixado": false,
    "recall": false,
    "sinistro": true,
    "leilao": { "consta": true, "ocorrencias": 0, "fotos": 6 },

    "restricoes": [
      { "tipo": "FINANCEIRA", "ativa": false, "descricao": null },
      { "tipo": "RENAJUD", "ativa": false, "descricao": null }
    ],
    "debitos": [
      { "tipo": "MULTA", "descricao": "Multas", "valor_centavos": 97617, "valor_informado": true },
      { "tipo": "IPVA", "descricao": "IPVA", "valor_centavos": 0, "valor_informado": false }
    ],
    "debitos_total_centavos": 97617,

    "ficha_pesados": {
      "eixos": null, "capacidade_carga_kg": null, "peso_bruto_total_kg": null,
      "capacidade_maxima_tracao_kg": null, "capacidade_tanque_litros": null
    },
    "faturamento": { "tipo_documento": "CNPJ", "documento": "59104422002446", "uf": "SP" },

    "ultimo_licenciamento": 2024
  }
}
Trate null, false e ausência como coisas diferentes. sinistro e recall vêm nullquando a fonte não verificou o item — não é “sem sinistro”. leilao: null significa que nenhuma das três fontes respondeu; já leilao.fotos > 0 confirma a passagem por leilão mesmo com ocorrencias: 0. E em debitos[], um item com valor_informado: false é um débito REAL de valor desconhecido: debitos_total_centavos é o mínimo devido, não o total.
resposta 200 — ainda processando
{ "id": "6f1c2e2a-...", "status": "processando", ... }
resposta 200 — a consulta falhou (valor estornado)
{
  "id": "6f1c2e2a-...",
  "servico": "gravame",
  "placa": "ABC1D23",
  "status": "erro",
  "erro": "A base consultada está temporariamente indisponível. Isso costuma se resolver em alguns minutos — tente novamente. O valor foi devolvido ao seu saldo.",
  "erro_codigo": "PROVIDER_UNAVAILABLE",
  "retryable": true
}

erro_codigo diz por que a consulta falhou; retryable diz se vale repetir. Os dois podem estar ausentes em consultas anteriores a 02/09/2026.

erro_codigoretryableO que aconteceu
INVALID_PLATEfalsePlaca inválida. Também: INVALID_CHASSIS, INVALID_ENGINE, INVALID_RENAVAM, INVALID_DOCUMENT, MISSING_REQUIRED_FIELD, UF_UNAVAILABLE.
QUERY_NOT_FOUNDfalseA base consultou e não encontrou registro para os dados enviados.
PROVIDER_TIMEOUTtrueA base demorou além do limite. Repita em alguns minutos.
PROVIDER_UNAVAILABLEtrueA base está fora do ar ou instável. Repita em alguns minutos. Também: CIRCUIT_OPEN.
PROVIDER_INVALID_RESPONSEtrueA base respondeu de forma incompleta.
PROVIDER_ERRORfalseA base recusou a consulta. Também: PROVIDER_NO_DOCUMENT (documento não emitido), PROVIDER_AUTH_ERROR.
PROCESSING_INTERRUPTEDtrueO processamento foi interrompido do nosso lado. Também: QUEUE_UNAVAILABLE.
INTERNAL_ERRORfalseFalha interna da ZapCar, já reportada à equipe. Também: PDF_GENERATION_FAILED, ADAPTER_ERROR, CONFIG_ERROR.
Para CRLV e código de segurança não há campo dados — o resultado é só o PDF, em pdf_url.
Campo vazio nem sempre significa “nada consta”. Na Consulta Simples, o array nao_verificado lista as categorias que a base estadual não checou nesta consulta. Uma restrição que aparece nessa lista vem como "" em dados — e isso é ausência de verificação, não ausência de restrição. Trate os três estados separadamente: "NADA CONSTA" (verificado e limpo), texto descritivo (restrição ativa) e vazio + presente em nao_verificado (não informado). A base estadual atual verifica financiamento, RENAJUD, roubo/furto, veículo baixado e comunicação de venda; não verifica restricao_judicial, restricao_administrativa, restricao_tributaria e restricao_guincho, que só aparecem quando há ocorrência descrita em restricoes[] — espere essas quatro em nao_verificado na maioria das consultas.

Baixar o PDF

GET/v1/consultas/{id}/pdf

Retorna o documento — o mesmo do painel. Disponível quando o status é concluido; antes disso responde 400 PDF_NOT_READY. Na Consulta Simples o documento é gerado pela ZapCar no layout do laudo da base estadual, a partir do mesmo dados que a API devolve; itens que a base não respondeu saem como NÃO INFORMADO. Consultas feitas entre 28/08 e 09/09/2026 continuam entregando o PDF emitido pela base da época.

Confie no Content-Type, não na extensão. Quase todos os serviços devolvem application/pdf, mas alguns emissores de documento oficial entregam imagem — nesses casos a resposta sai como image/png ou image/jpeg, com a extensão certa no Content-Disposition. Salvar tudo como .pdf gera arquivo que não abre. Se o arquivo guardado estiver corrompido, a API responde 422 DOCUMENTO_CORROMPIDO em vez de entregar bytes quebrados.
baixar e salvar
curl -L https://api.zapcarconsulta.com.br/v1/consultas/COLE_O_ID/pdf \
  -H "Authorization: Bearer zc_live_sua_chave_aqui" \
  -o relatorio.pdf
Nos serviços com JSON (consulta, completa, gravame, RENAJUD, débitos e as duas decodificações), você não precisa do nosso PDF: use o campo dados e monte o seu próprio documento com a sua marca. O pdf_url fica ali como atalho para quem quiser o relatório pronto.

Imagens da Consulta Completa

GET/v1/consultas/{id}/imagens/{tipo}/{indice}

Baixa uma imagem da Consulta Completa: foto do veículo em pátio de leilão, imagem de vistoria/anúncio ou o certificado do laudo CSV. Só a Consulta Completa tem imagens; nos demais serviços a resposta é 404 IMAGEM_NOT_FOUND.

Você não monta essas URLs à mão: elas já vêm prontas dentro de dados, em fotosLeilao[].url, imagensOutras[].url e csv[].imagem_url / csv[].pdf_url.

ParâmetroValoresDescrição
tipoleilao · outra · csv-imagem · csv-pdfO que a imagem é. Só "leilao" é prova de passagem por leilão.
indiceinteiro ≥ 0Posição no array correspondente dentro de dados: fotosLeilao[i], imagensOutras[i], csv[i].
Por que a imagem não vem no JSON. Um retorno real da Consulta Completa passa de 8 MB quando as fotos viajam em base64 dentro da resposta. Elas ficam guardadas à parte e o JSON traz só a contagem e o link. A contagem já decide muita coisa: fotosLeilao com itens significa que o veículo passou por leilão, mesmo que leilao venha null — isso quer dizer apenas que a base de ocorrências não trouxe lote e comitente.
baixar a primeira foto de leilão
curl -L https://api.zapcarconsulta.com.br/v1/consultas/COLE_O_ID/imagens/leilao/0   -H "Authorization: Bearer zc_live_sua_chave_aqui"   -o leilao_0.png

A resposta é o binário, com o Content-Type real (image/png, image/jpeg ou application/pdf) — confie nele, não na extensão que você escolher.

Consultas concluídas antes de 27/08/2026 não têm imagens armazenadas e respondem 404 IMAGEM_NOT_FOUND. O PDF do laudo dessas consultas continua disponível normalmente.

Tabela FIPE por placa

POST/v1/fipe

Retorna os valores da Tabela FIPE do veículo a partir da placa. Ao contrário dos demais serviços, este endpoint é síncrono: a resposta vem na mesma chamada — sem id, sem polling e sem PDF.

Preço: R$ 0,25 por consulta, debitado do saldo. O valor é estornado automaticamente se a consulta falhar ou se o veículo não tiver FIPE — você só paga quando recebe os dados. O preço vigente também vem em GET /v1/servicos (serviço fipe).
CampoTipoObrig.Descrição
placastringSimPlaca do veículo (com ou sem hífen). Ex.: "ABC1D23".
requisição
curl -X POST https://api.zapcarconsulta.com.br/v1/fipe \
  -H "Authorization: Bearer zc_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{ "placa": "ABC1D23" }'
Exemplo ilustrativo — resposta 200
{
  "id": "6f1c2e2a-1b3d-4a9e-8f7c-2a1b3c4d5e6f",
  "servico": "fipe",
  "placa": "ABC1D23",
  "veiculo": { "marca": "VW", "modelo": "CROSSFOX", "ano_modelo": "2007" },
  "fipe": [
    {
      "codigo_fipe": "005225-6",
      "marca": "VW - Volkswagen",
      "modelo": "CROSSFOX 1.6 Mi Total Flex 8V",
      "ano_modelo": "2007",
      "combustivel": "Gasolina",
      "sigla_combustivel": "G",
      "valor": "R$ 26.799,00",
      "mes_referencia": "maio de 2022",
      "referencia_fipe": 285,
      "score": 101,
      "codigo_marca": "60",
      "codigo_modelo": "2398",
      "tipo_modelo": 1
    }
  ],
  "valor_cobrado": 0.25,
  "saldo_restante": 464.76
}

O campo fipe é uma lista: pode trazer mais de uma correspondência (o score indica a precisão — maior é melhor). Escolha a de maior score quando houver várias.

HTTPCódigoQuando acontece
402SALDO_INSUFICIENTESaldo menor que R$ 0,25. Nada é cobrado.
404FIPE_NAO_DISPONIVELPlaca válida, mas sem FIPE correspondente. Valor estornado.
502FIPE_ERROFalha ao consultar a fonte. Valor estornado.
503FIPE_INDISPONIVELServiço temporariamente fora. Valor estornado.

Webhooks (receber sem polling)

Em vez de perguntar de tempos em tempos se a consulta ficou pronta, você registra uma URL sua e a ZapCar avisa quando o evento acontece. É o caminho recomendado para o CRLV-e assíncrono e para volumes maiores — economiza requisições e chega antes.

Onde cadastrar. No painel, em Integração. Você informa a URL, escolhe os eventos e recebe um secret — que aparece uma única vez, na criação. Guarde-o: é com ele que você valida cada entrega. Dá para rotacionar o secret e disparar um evento de teste (webhook.test) sem gastar saldo.

Eventos disponíveis

EventoQuando dispara
query.completedA consulta chegou ao estado final com sucesso. Busque o resultado em GET /v1/consultas/:id.
query.failedA consulta falhou em definitivo (depois das tentativas). O valor é estornado.
vehicle.restriction.createdO monitoramento detectou restrição nova na placa (inclui roubo/furto).
vehicle.gravame.createdO monitoramento detectou gravame registrado.
vehicle.gravame.removedO monitoramento detectou baixa de gravame.
vehicle.debt.createdO monitoramento detectou débito novo.
vehicle.document.changedO monitoramento detectou mudança na situação documental.
webhook.testVocê mesmo disparou pelo painel, para conferir a integração.
Os eventos vehicle.* só existem para placas que você colocou no Monitoramento: eles nascem da comparação entre uma consulta e a anterior, não de uma vigilância em tempo real.

O que chega no seu endpoint

Um POST com corpo JSON e três cabeçalhos próprios:

CabeçalhoConteúdo
X-ZapCar-EventNome do evento (ex.: query.completed).
X-ZapCar-DeliveryId desta tentativa de entrega — útil no suporte.
X-ZapCar-SignatureAssinatura HMAC no formato t=<timestamp>,v1=<hmac>.
corpo do evento
{
  "id": "b2c3d4e5-6f70-...",       // id do EVENTO — use para idempotência
  "type": "query.completed",
  "created_at": "2026-08-19T12:00:00.000Z",
  "data": {
    "id": "a1b2c3d4-...",          // id da consulta (GET /v1/consultas/:id)
    "servico": "CONSULTA_COMPLETA",
    "status": "CONCLUIDO"
  }
}
O evento não traz o resultado da consulta. Ele avisa que ficou pronto; os dados continuam saindo por GET /v1/consultas/:id. É proposital: o que trafega até a sua URL fica mínimo, e o dado sensível só sai por chamada autenticada com a sua chave.

Validar a assinatura

Assine <timestamp>.<corpo bruto> com HMAC-SHA256 usando o seu secret e compare com o v1 do cabeçalho. Use o corpo exatamente como chegou: se você fizer parse e re-serializar o JSON antes de assinar, a assinatura não bate.

Node.js (Express)
const crypto = require("crypto");

// Corpo BRUTO (Buffer), não req.body já parseado — a assinatura é sobre os bytes.
app.post("/webhooks/zapcar", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-ZapCar-Signature") || "";
  const t  = /t=(\d+)/.exec(header)?.[1];
  const v1 = /v1=([a-f0-9]+)/.exec(header)?.[1];
  if (!t || !v1) return res.sendStatus(400);

  // Rejeita evento antigo: barra o replay de uma entrega capturada.
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return res.sendStatus(400);

  const esperado = crypto
    .createHmac("sha256", process.env.ZAPCAR_WEBHOOK_SECRET)
    .update(t + "." + req.body.toString("utf8"))
    .digest("hex");

  // Comparação em tempo constante.
  const a = Buffer.from(esperado), b = Buffer.from(v1);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return res.sendStatus(401);

  const evento = JSON.parse(req.body.toString("utf8"));

  // Responda 2xx RÁPIDO e processe depois (fila). Demorar aqui vira retry nosso.
  res.sendStatus(200);
  processarDepois(evento); // ignore se evento.id já foi processado
});

Reentrega e duplicatas

RegraComportamento
O que conta como sucessoQualquer resposta 2xx. Outro status — ou timeout — conta como falha.
Tempo para responderResponda em poucos segundos. Passando do limite, a entrega vira falha e será retentada.
TentativasAté 6, com backoff exponencial a partir de 10s (10s, 20s, 40s, 80s…).
Endpoint desativadoNão é retentado. A entrega fica marcada como falha no histórico.
DuplicatasUm retry reenvia o MESMO id de evento. Guarde os ids já processados e ignore repetidos.
HistóricoAs últimas entregas de cada endpoint aparecem no painel, com status, código HTTP e erro.
Trate receber o mesmo evento duas vezes como possibilidade real, não como exceção: se a sua URL responder 200 mas a resposta se perder na rede, nós retentamos. A proteção correta é idempotência pelo campo id do evento — não pela ordem de chegada, que também não é garantida.

Exemplos em código

O fluxo completo (criar → esperar → resultado) em algumas linguagens. Troque a chave e a placa.

cURL (terminal)

fluxo completo em curl
# 1. Cria a consulta (debita o saldo). Guarde o "id" da resposta.
curl -X POST https://api.zapcarconsulta.com.br/v1/consultas \
  -H "Authorization: Bearer zc_live_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d '{"servico":"consulta","placa":"ABC1D23"}'

# 2. Ver o resultado (troque COLE_O_ID). Repita até status = "concluido".
curl https://api.zapcarconsulta.com.br/v1/consultas/COLE_O_ID \
  -H "Authorization: Bearer zc_live_sua_chave_aqui"

# 3. Baixar o PDF (quando concluído).
curl -L https://api.zapcarconsulta.com.br/v1/consultas/COLE_O_ID/pdf \
  -H "Authorization: Bearer zc_live_sua_chave_aqui" \
  -o relatorio.pdf

JavaScript / Node.js

com polling automático
const BASE  = "https://api.zapcarconsulta.com.br";
const CHAVE = "zc_live_sua_chave_aqui";

async function consultarVeiculo(placa) {
  // 1. cria a consulta (debita o saldo)
  const criar = await fetch(BASE + "/v1/consultas", {
    method: "POST",
    headers: {
      "Authorization": "Bearer " + CHAVE,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ servico: "consulta", placa }),
  });
  if (criar.status === 402) throw new Error("Saldo insuficiente");
  const { id } = await criar.json();

  // 2. faz polling até concluir (a cada 5s, por até ~5 min).
  //    A base estadual leva até ~2 min numa placa fria: "processando" nunca é erro.
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const res  = await fetch(BASE + "/v1/consultas/" + id, {
      headers: { "Authorization": "Bearer " + CHAVE },
    });
    const c = await res.json();
    if (c.status === "concluido") return c;           // c.dados + c.pdf_url
    if (c.status === "erro") {
      // c.retryable: true = a base caiu, tente de novo. false = definitivo
      // (placa inexistente, contrato). Ausente = não classificamos.
      if (c.retryable) continue;                      // volta ao topo do laço
      throw new Error("Consulta falhou (estornada)");
    }
  }
  throw new Error("Tempo esgotado");
}

consultarVeiculo("ABC1D23").then((c) => console.log(c.dados));

PHP

com polling automático
<?php
$BASE  = "https://api.zapcarconsulta.com.br";
$CHAVE = "zc_live_sua_chave_aqui";
$H = ["Authorization: Bearer $CHAVE", "Content-Type: application/json"];

// 1. cria a consulta
$ch = curl_init("$BASE/v1/consultas");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $H,
  CURLOPT_POSTFIELDS => json_encode(["servico" => "consulta", "placa" => "ABC1D23"]),
]);
$id = json_decode(curl_exec($ch), true)["id"];
curl_close($ch);

// 2. polling até concluir
do {
  sleep(3);
  $ch = curl_init("$BASE/v1/consultas/$id");
  curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $H]);
  $c = json_decode(curl_exec($ch), true);
  curl_close($ch);
} while ($c["status"] === "processando");

print_r($c["dados"]);

Testar no Postman

Se preferir clicar em vez de programar, use o Postman. Importe a coleção pronta (gerada do catálogo, sempre com os serviços vigentes) ou a descrição OpenAPI 3.0 — e cole a sua chave na variável api_key. Ou monte as requisições à mão:

1

Configure a autenticação

Na requisição, aba AuthorizationBearer Token → cole sua chave zc_live_…. Isso vale para cada requisição.
2

Teste a conexão (GET)

Método GET, URL https://api.zapcarconsulta.com.br/v1/servicosSend. Se vier 200, está funcionando.
3

Crie a consulta (POST)

Troque para POST, URL https://api.zapcarconsulta.com.br/v1/consultas. Aba Bodyraw → escolha JSON → cole { "servico": "consulta", "placa": "ABC1D23" }Send. Copie o id.
4

Veja o resultado (GET)

Volte para GET, URL https://api.zapcarconsulta.com.br/v1/consultas/COLE_O_IDSend, repetindo até concluido.
O erro mais comum no Postman: deixar o método em POST mas colocar o id na URL. Para ver um resultado é sempre GET com id; para criar é POST sem id.

Códigos de erro

Erros seguem o formato { "erro": "...", "codigo": "..." } com o status HTTP correspondente.

HTTPCódigoRepetir?Quando acontece
400CHASSI_INVALIDOnãoChassi inválido.
400CONSULTA_WITHOUT_DATAnãoConsulta sem dados para gerar o documento.
400CRLV_DADOS_OBRIGATORIOSnãoFaltam campos exigidos pela UF (CPF/RENAVAM).
400DEBITOS_DADOS_OBRIGATORIOSnãoFaltam campos exigidos pela UF.
400DOCUMENTO_INVALIDOnãoCPF/CNPJ inválido.
400IDEMPOTENCY_KEY_INVALIDAnãoIdempotency-Key fora do formato (8–255 chars).
400MOTOR_INVALIDOnãoNúmero do motor inválido.
400PDF_NOT_READYsimPediu o PDF antes de a consulta concluir. Espere o status "concluido".
400PLACA_INVALIDAnãoPlaca em formato inválido.
422DOCUMENTO_CORROMPIDOnãoO arquivo guardado não é um documento válido (PDF/PNG/JPEG). Refaça a consulta.
400RENAVAM_INVALIDOnãoRENAVAM inválido.
400SERVICO_INVALIDOnãoO campo "servico" não corresponde a nenhum serviço.
400UF_INDISPONIVELnãoUF indisponível para o serviço.
400UF_INVALIDAnãoUF ausente ou diferente de 2 letras.
400VALIDATION_ERRORnãoCorpo da requisição inválido (vem com "detalhes" por campo).
401CUSTOMER_NOT_FOUNDnãoConta associada à chave não encontrada.
401INVALID_API_KEYnãoA chave é inválida, foi revogada ou não existe.
401MISSING_API_KEYnãoCabeçalho Authorization: Bearer <chave> ausente.
402SALDO_INSUFICIENTEnãoSaldo menor que o preço. A resposta traz o "saldo" atual.
404CONSULTA_NOT_FOUNDnãoO id não existe ou não pertence à sua conta.
404FIPE_NAO_DISPONIVELnãoSem FIPE para esta placa.
404PDF_NOT_FOUNDnãoPDF não disponível para esta consulta.
404ROTA_NAO_ENCONTRADAnãoMétodo ou URL não batem com nenhum endpoint.
409IDEMPOTENCY_IN_PROGRESSsimRequisição idêntica ainda em processamento; repita em instantes.
422IDEMPOTENCY_KEY_REUSEDnãoMesma Idempotency-Key com payload diferente.
422SERVICO_INDISPONIVELnãoServiço sem preço/config (ex.: UF não atendida no CRLV).
429RATE_LIMITEDsimMuitas requisições no intervalo. Aguarde e tente de novo.
500INTERNAL_ERRORsimErro interno inesperado.
502FIPE_ERROsimFalha transitória ao consultar a FIPE.
503CIRCUIT_OPENsimFornecedor temporariamente indisponível (circuito aberto).
503FIPE_INDISPONIVELsimProvedor FIPE temporariamente indisponível.
504PROVIDER_TIMEOUTsimTempo de resposta do fornecedor excedido.

Problemas comuns

404 "Rota não encontrada"

Método ou URL não batem. Quase sempre é: (a) usar POST com o id na URL — para ver resultado é GET; (b) usar GET em /v1/consultas sem id para criar — criar é POST; ou (c) a URL está digitada errada.

401 chave não fornecida / inválida

Faltou o cabeçalho Authorization: Bearer …, ou a chave está errada/revogada. No Postman, confira a aba Authorization em cada requisição.

400 ao baixar o PDF

É PDF_NOT_READY: você pediu o PDF antes de a consulta concluir. Consulte o resultado até o status virar concluido e só então baixe o PDF.

402 saldo insuficiente

Sua carteira não tem o valor do serviço. Recarregue no painel (Carteira) e tente de novo.

Resultado voltou vazio ou "não encontrado"

A consulta funcionou, mas a base consultada não encontrou o veículo. Confira se a placa é real e está correta — placa inventada não retorna dados.

Boas práticas

  • Guarde a chave no servidor (variável de ambiente). Nunca a exponha no navegador ou em apps.
  • Faça polling com intervalo de 3–5s e teto de ~5 minutos; não consulte em loop apertado nem desista em 60s.
  • Monitore o saldo com GET /v1/saldo e recarregue antes de acabar.
  • Guarde o id de cada consulta: use-o para rebaixar o PDF ou reconsultar o resultado depois.
  • Trate o status "erro" — o valor é estornado, mas convém reenviar a consulta.
  • Ao revogar uma chave, ela para na hora. Gere a nova antes de trocar em produção.

Pronto para começar?

Crie sua conta, gere a chave e faça a primeira consulta.

Criar minha chave