Boas práticas para integrar a APIBrasil em produção: erros, saldo, retry e homologação

Boas práticas para integrar a APIBrasil em produção: erros, saldo, retry e homologação

Fazer a primeira chamada na APIBrasil leva cinco minutos. Deixar a integração robusta em produção exige conhecer alguns comportamentos que fogem do padrão REST e que, se ignorados, fazem o código funcionar no caminho feliz e falhar em silêncio no resto.

A própria documentação da APIBrasil, na aba Instruções para IA de cada endpoint em doc.apibrasil.io, lista essas armadilhas. Neste guia reunimos todas elas e montamos um cliente TypeScript reutilizável para qualquer API do catálogo.

Armadilha 1: o erro chega no corpo, com HTTP 200

A resposta da APIBrasil traz um campo error (booleano) e uma message. Uma consulta pode falhar e mesmo assim devolver HTTP 200. Se o seu código checa só response.ok, ele vai tratar a falha como sucesso e tentar ler um data que não existe.

Regra: valide em duas etapas, primeiro o status HTTP e depois error === false. As duas precisam passar.

Armadilha 2: saldo e custo são texto em formato brasileiro

Os campos balance, tax e extra_charges.total chegam como string, e o formato varia entre APIs. A documentação cita valores como "154,380", "497,84", "0,000" e "0.19": vírgula em umas, ponto em outras, duas ou três casas decimais.

Em JavaScript, parseFloat("154,380") devolve 154 e perde os centavos sem erro nenhum, e Number("154,380") devolve NaN. Use um conversor próprio:

export function valorBR(v: string | number | null | undefined): number {
  if (v === null || v === undefined || v === "") return 0;
  if (typeof v === "number") return v;
  const s = v.trim();
  // Com vírgula: vírgula é o decimal e ponto é milhar ("1.234,56").
  if (s.includes(",")) return Number(s.replace(/\./g, "").replace(",", "."));
  return Number(s); // "0.19"
}
Testes do conversor de valores monetários em formato brasileiro retornados pela APIBrasil
O mesmo campo pode vir com vírgula ou ponto. <code>parseFloat</code> perde os centavos sem avisar.

Armadilha 3: cada chamada custa dinheiro

Retry cego multiplica a conta. A regra da documentação é clara:

  • Nunca repita automaticamente um 4xx. Parâmetro errado repetido continua errado e continua cobrando.
  • Repita só tempo esgotado e 5xx, com recuo exponencial e um teto pequeno de tentativas.

Armadilha 4: HTTP 402 é saldo insuficiente

O 402 é o único status que exige ação humana: recarregar a conta em app.apibrasil.io/recargas. Trate como erro terminal, com mensagem própria, e não junto com os outros 4xx. É um ótimo gatilho para um alerta no Slack ou por e-mail.

Armadilha 5: extra_charges é cobrado à parte

Quando o corpo pede serviços adicionais, a resposta traz cada um com o preço e um total que não está incluído no valor base da consulta. Se você mostra o custo para o usuário ou repassa ao cliente, some os dois.

O cliente TypeScript completo

Com as cinco regras em mente, este é um cliente genérico que serve para qualquer API do catálogo:

import { valorBR } from "./valores";

const BASE = "https://gateway.apibrasil.io/api/v2/";

export class SaldoInsuficiente extends Error {}
export class ErroAPIBrasil extends Error {
  constructor(msg: string, public http?: number) { super(msg); }
}

export interface RespostaAPIBrasil<T> {
  error: boolean;
  message: string;
  balance: string;
  tax: string;
  valor_consulta: number;
  api_limit_for: "credit" | "homolog" | string;
  homolog?: boolean;
  data: T;
  extra_charges?: { total?: string };
}

const HOMOLOG = process.env.APIBRASIL_HOMOLOG !== "false";
const espera = (ms: number) => new Promise((r) => setTimeout(r, ms));

export async function apibrasil<T>(
  rota: string,
  corpo: Record<string, unknown>,
  { tentativas = 3, timeoutMs = 120_000 } = {}
): Promise<RespostaAPIBrasil<T> & { custoTotal: number; saldo: number }> {
  const payload = { homolog: HOMOLOG, ...corpo };

  for (let t = 1; t <= tentativas; t++) {
    const inicio = Date.now();
    let res: Response;
    try {
      res = await fetch(BASE + rota, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.APIBRASIL_TOKEN}`,
        },
        body: JSON.stringify(payload),
        signal: AbortSignal.timeout(timeoutMs),
      });
    } catch (e) {
      // Timeout ou falha de rede: pode repetir.
      if (t === tentativas) throw e;
      await espera(2 ** (t - 1) * 1000);
      continue;
    }

    if (res.status === 402) throw new SaldoInsuficiente("Saldo insuficiente: recarregue a conta");
    if (res.status >= 500) {
      if (t === tentativas) throw new ErroAPIBrasil(`HTTP ${res.status}`, res.status);
      console.warn(JSON.stringify({ api: rota, http: res.status, tentativa: t }));
      await espera(2 ** (t - 1) * 1000);
      continue;
    }
    if (res.status >= 400) {
      // 4xx: não repita, corrija a chamada.
      throw new ErroAPIBrasil(`HTTP ${res.status}: ${await res.text()}`, res.status);
    }

    const body = (await res.json()) as RespostaAPIBrasil<T>;
    if (body.error) throw new ErroAPIBrasil(body.message || "Erro na consulta", res.status);

    const custoTotal = valorBR(body.tax) + valorBR(body.extra_charges?.total);
    const saldo = valorBR(body.balance);
    console.info(JSON.stringify({ api: rota, http: res.status, error: false, tax: custoTotal, balance: saldo, ms: Date.now() - inicio }));
    return { ...body, custoTotal, saldo };
  }
  throw new ErroAPIBrasil("Não foi possível concluir a chamada");
}

E o uso fica simples, para qualquer API:

const r = await apibrasil<{ cnpj: { nome_fantasia: string } }>("dados/cnpj/credits", {
  tipo: "cnpj",
  cnpj: "44959669000180",
});
console.log(r.data.cnpj.nome_fantasia, "custou", r.custoTotal);
Log estruturado das chamadas à APIBrasil com custo, saldo e tentativas de retry
Log estruturado com rota, status, custo e saldo: base para alertas de consumo.

Homologação: ligada por padrão fora de produção

O campo homolog: true faz a APIBrasil responder com dados válidos, api_limit_for: "homolog" e sem tarifação. O cliente acima já lê isso de APIBRASIL_HOMOLOG, com homologação ligada por padrão. Assim, uma suíte de testes esquecida rodando na CI não gasta saldo real.

AmbienteAPIBRASIL_HOMOLOG
Desenvolvimento localtrue (padrão)
CI / testes automatizadostrue
Homologação do clientetrue
Produçãofalse

Mais práticas que fazem diferença

  • Token só no back-end. Nunca chame a APIBrasil direto do navegador ou do app: o Bearer Token ficaria exposto. Veja como proteger tokens com variáveis de ambiente.
  • Cache inteligente. CNPJ, CEP e FIPE mudam pouco. Um cache de horas ou dias corta boa parte do custo.
  • Timeout generoso. A documentação usa --max-time 120 nos exemplos. Algumas consultas dependem de fontes externas e podem demorar.
  • Validação local antes da chamada. Dígitos do CPF e do CNPJ, formato de placa e CEP: tudo isso dá para conferir sem gastar.
  • Alerta de saldo. Com o saldo já convertido em número, dispare um alerta quando cair abaixo de um limite.
  • Consultas assíncronas para volume. O catálogo tem uma categoria de Consultas Assíncronas, e as APIs de mensageria aceitam o sufixo /queue para enfileirar envios em lote.

Use as Instruções para IA a seu favor

Cada endpoint em doc.apibrasil.io tem a aba Instruções para IA, com a especificação completa do endpoint, as armadilhas e os requisitos de implementação. Cole esse texto no seu assistente de código (Claude, Cursor, Copilot) e peça o cliente na linguagem do seu projeto: ele já vem com as regras deste artigo. Se preferir que o assistente chame as APIs diretamente, veja o MCP da APIBrasil.

Checklist antes de ir para produção

  1. Validação em duas etapas: status HTTP e campo error.
  2. balance, tax e extra_charges.total convertidos com função própria.
  3. Retry só em timeout e 5xx, com recuo exponencial e teto.
  4. 402 tratado à parte, com alerta.
  5. homolog configurável e ligado por padrão fora de produção.
  6. Token em variável de ambiente, só no back-end.
  7. Log estruturado com custo e saldo.

Próximos passos

Aplique o cliente nos tutoriais da série: consulta de CNPJ com Python, placa com valor FIPE em Node.js e verificação por SMS OTP.

Loading

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *