Consultar CNPJ no Google Sheets (e CEP e placa) com Apps Script

Capa do artigo Consultar CNPJ no Google Sheets (e CEP e placa) com Apps Script

Consultar CNPJ no Google Sheets, assim como CEP ou placa, é o tipo de tarefa que todo time operacional acaba fazendo na mão: abre o site da Receita, copia a razão social, cola na célula, repete 300 vezes. Com o Google Apps Script e a APIBrasil, isso vira uma fórmula. Você digita =CONSULTA_CNPJ(B2) e a linha se preenche com razão social, situação cadastral, município e CNAE.

O script inteiro cabe em um arquivo, não precisa de servidor e usa só recursos nativos do Sheets: UrlFetchApp para chamar a API, PropertiesService para guardar o token fora da planilha e CacheService para não pagar duas vezes pela mesma consulta.

A seguir, você cria o projeto, grava o token com segurança, escreve as três funções personalizadas (CEP, CNPJ e placa) e termina com cache, cotas e tratamento de erros para a planilha aguentar uso real.

Quando a planilha é a melhor ferramenta

Nem toda integração merece um sistema. Se a demanda é enriquecer uma lista de clientes uma vez por semana, validar os fornecedores de uma licitação ou conferir os veículos de uma frota pequena, a planilha já é o lugar onde o trabalho acontece. Levar a consulta para dentro dela elimina a troca de ferramenta e a cópia manual, que é onde os erros aparecem.

A planilha também é o formato natural de relatório operacional. Times de atendimento, por exemplo, montam um relatório de atendimento cruzando dados do CRM com informações cadastrais do cliente; ter o CNPJ resolvido na própria planilha evita exportar, processar e importar de volta.

O limite aparece quando o volume cresce. Milhares de consultas por dia, necessidade de rodar sem ninguém com a planilha aberta ou integração com outros sistemas pedem um serviço próprio, como o descrito em consultar CNPJ com a API da APIBrasil em Python. Até lá, o Apps Script resolve com muito menos esforço.

Criando o script e guardando o token

Com a planilha aberta, vá em Extensões, Apps Script. O editor abre com um arquivo Código.gs vazio; é nele que as funções abaixo entram. Antes de escrever código, guarde o token: no menu lateral do editor, abra Configurações do projeto, role até Propriedades do script e adicione uma propriedade chamada APIBRASIL_TOKEN com o Bearer Token gerado no painel da APIBrasil. Se ainda não tem um, o guia de primeiros passos com Bearer Token e homologação mostra o caminho.

Guardar o token nas propriedades, e não em uma célula ou no código, tem dois efeitos: ele não aparece para quem só lê a planilha e não vai junto quando alguém copia o script. A função base, usada por todas as outras, lê essa propriedade e trata o padrão de resposta da APIBrasil, em que o erro pode chegar com HTTP 200:

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

/** Chamada genérica à APIBrasil. Lança erro com a mensagem devolvida pela API. */
function chamarApiBrasil_(caminho, corpo) {
  const token = PropertiesService.getScriptProperties().getProperty("APIBRASIL_TOKEN");
  if (!token) throw new Error("Defina APIBRASIL_TOKEN em Configurações do projeto > Propriedades do script");

  const resposta = UrlFetchApp.fetch(BASE_URL + caminho, {
    method: "post",
    contentType: "application/json",
    headers: { Authorization: "Bearer " + token },
    payload: JSON.stringify(corpo),
    muteHttpExceptions: true, // 4xx/5xx não viram exceção; tratamos abaixo
  });

  const codigo = resposta.getResponseCode();
  let json = {};
  try { json = JSON.parse(resposta.getContentText()); } catch (e) {}

  // A API pode responder 200 com erro no corpo: sempre confira "error"
  if (codigo >= 400 || json.error) {
    throw new Error(json.message || ("HTTP " + codigo));
  }
  return json; // { error: false, message, data, balance, tax, ... }
}

/** Guarda o resultado por 6 horas (máximo do CacheService) para não repetir consultas cobradas. */
function comCache_(chave, fn) {
  const cache = CacheService.getScriptCache();
  const salvo = cache.get(chave);
  if (salvo) return JSON.parse(salvo);
  const valor = fn();
  cache.put(chave, JSON.stringify(valor), 21600);
  return valor;
}

O sufixo de sublinhado nos nomes (chamarApiBrasil_) é uma convenção do Apps Script: funções terminadas em _ não aparecem como fórmula na planilha, o que evita que alguém chame a função base direto de uma célula.

Função =CONSULTA_CEP()

A consulta de CEP usa o endpoint POST /consulta/cep/credits com tipo igual a cep. Na resposta, os dados ficam em data.cep: logradouro e estado são strings, enquanto cidade e bairro são objetos com campos próprios (a cidade traz também o código IBGE). A função devolve uma linha com cinco colunas, que o Sheets espalha automaticamente para a direita da célula onde a fórmula foi digitada.

/**
 * Consulta um CEP na APIBrasil e devolve logradouro, bairro, cidade, UF e código IBGE.
 * @param {string} cep CEP com ou sem hífen (ex.: "30130-010").
 * @param {boolean} [homolog] true para testar sem consumir créditos.
 * @return {string[][]} Uma linha com 5 colunas.
 * @customfunction
 */
function CONSULTA_CEP(cep, homolog) {
  const limpo = String(cep).replace(/\D/g, "");
  if (limpo.length !== 8) return "CEP inválido";

  return comCache_("cep:" + limpo + (homolog ? ":h" : ""), function () {
    const r = chamarApiBrasil_("/consulta/cep/credits", { tipo: "cep", cep: limpo, homolog: homolog === true });
    const d = r.data.cep; // confira o nome exato dos campos na documentação
    return [[
      d.logradouro || "",
      (d.bairro && d.bairro.bairro) || "",
      (d.cidade && d.cidade.cidade) || "",
      d.estado || "",
      (d.cidade && d.cidade.cidade_ibge) || "",
    ]];
  });
}

O comentário com @customfunction é o que faz a fórmula aparecer no autocompletar da planilha, com a descrição dos parâmetros. Para testar sem gastar crédito, use =CONSULTA_CEP(A2; VERDADEIRO): o segundo argumento manda homolog como true e a API responde de uma base fixa. Em uma planilha com configuração regional dos Estados Unidos, o separador de argumentos é vírgula em vez de ponto e vírgula.

Infográfico com as funções personalizadas do Google Sheets via Apps Script e APIBrasil: CONSULTA_CEP, CONSULTA_CNPJ, CONSULTA_PLACA, token nas propriedades do script e cache para poupar créditos

Consultar CNPJ no Google Sheets com =CONSULTA_CNPJ()

Aqui está o coração da planilha. O endpoint é POST /dados/cnpj/credits com tipo igual a cnpj, e a resposta traz o cadastro em data.dados: situação cadastral, nome fantasia, CNAE fiscal, endereço em campos soltos e município como objeto. Razão social, porte e capital social ficam no objeto empresa, e a opção pelo Simples ou MEI no objeto simples. Como a estrutura pode mudar entre versões, o código abaixo lê cada campo de forma defensiva.

Um detalhe que economiza dinheiro: validar os dígitos verificadores antes de chamar a API. Um CNPJ digitado errado é uma consulta cobrada que volta vazia. O algoritmo é curto e está explicado em detalhes no guia para validar CPF e CNPJ em JavaScript, PHP e Python; a versão abaixo cobre o CNPJ numérico.

/**
 * Consulta um CNPJ na APIBrasil. Sem "campo", devolve uma linha com razão social,
 * nome fantasia, situação, município, UF e CNAE principal.
 * @param {string} cnpj CNPJ com ou sem máscara.
 * @param {string} [campo] Um campo só: razao_social, nome_fantasia, situacao, municipio, uf, cnae, porte ou capital_social.
 * @param {boolean} [homolog] true para testar sem consumir créditos.
 * @return {string|string[][]}
 * @customfunction
 */
function CONSULTA_CNPJ(cnpj, campo, homolog) {
  const limpo = String(cnpj).replace(/\D/g, "");
  if (!validarCnpj_(limpo)) return "CNPJ inválido";

  const resumo = comCache_("cnpj:" + limpo + (homolog ? ":h" : ""), function () {
    const r = chamarApiBrasil_("/dados/cnpj/credits", { tipo: "cnpj", cnpj: limpo, homolog: homolog === true });
    const d = r.data.dados || r.data.cnpj || {}; // confira o caminho exato na documentação
    const empresa = d.empresa || {};
    return {
      razao_social: empresa.razao_social || "",
      nome_fantasia: d.nome_fantasia || "",
      situacao: d.situacao_cadastral || "",
      municipio: (d.municipio && d.municipio.descricao) || "",
      uf: d.uf || "",
      cnae: d.cnae_fiscal || "",
      porte: empresa.porte_empresa || "",
      capital_social: empresa.capital_social || "",
    };
  });

  if (campo) {
    const valor = resumo[String(campo).toLowerCase()];
    return valor === undefined ? "campo desconhecido" : valor;
  }
  return [[resumo.razao_social, resumo.nome_fantasia, resumo.situacao, resumo.municipio, resumo.uf, resumo.cnae]];
}

/** Valida os dígitos verificadores de um CNPJ numérico com 14 dígitos. */
function validarCnpj_(cnpj) {
  if (!/^\d{14}$/.test(cnpj) || /^(\d)\1{13}$/.test(cnpj)) return false;
  const calc = function (base, pesos) {
    let soma = 0;
    for (let i = 0; i < base.length; i++) soma += Number(base[i]) * pesos[i];
    const resto = soma % 11;
    return resto < 2 ? 0 : 11 - resto;
  };
  const d1 = calc(cnpj.slice(0, 12), [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
  const d2 = calc(cnpj.slice(0, 12) + d1, [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
  return cnpj.slice(12) === String(d1) + String(d2);
}

O parâmetro opcional campo deixa a fórmula flexível: =CONSULTA_CNPJ(B2; “situacao”) devolve só a situação cadastral, útil para uma coluna de filtro, enquanto =CONSULTA_CNPJ(B2) preenche seis colunas de uma vez. Como as duas chamadas usam a mesma chave de cache, a segunda não gera nova cobrança.

Função =CONSULTA_PLACA()

Para veículos, a APIBrasil oferece várias consultas no mesmo endpoint, POST /consulta/veiculos/credits, diferenciadas pelo campo tipo. A versão abaixo usa agregados-basica, que devolve fabricante, modelo, anos de fabricação e modelo, cor, combustível e UF de jurisdição. Para valor de mercado, o tipo fipe-chassi retorna uma lista resultados com marca, modelo, código FIPE e valor; confira os nomes de campo na página de cada consulta.

/**
 * Consulta uma placa (padrão antigo ou Mercosul) na APIBrasil e devolve
 * fabricante, modelo, ano modelo, cor e UF.
 * @param {string} placa Placa, ex.: "ABC1D23" ou "ABC-1234".
 * @param {boolean} [homolog] true para testar sem consumir créditos.
 * @return {string[][]} Uma linha com 5 colunas.
 * @customfunction
 */
function CONSULTA_PLACA(placa, homolog) {
  const limpa = String(placa).toUpperCase().replace(/[^A-Z0-9]/g, "");
  if (!/^[A-Z]{3}\d[A-Z0-9]\d{2}$/.test(limpa)) return "Placa inválida";

  return comCache_("placa:" + limpa + (homolog ? ":h" : ""), function () {
    const r = chamarApiBrasil_("/consulta/veiculos/credits", { tipo: "agregados-basica", placa: limpa, homolog: homolog === true });
    const d = r.data || {}; // nomes dos campos conforme a documentação da consulta
    return [[d.fabricante || "", d.modelo || "", d.ano_modelo || "", d.cor || "", d.uf_jurisdicao || ""]];
  });
}

A expressão regular aceita os dois formatos de placa em uso no Brasil e rejeita o resto antes de gastar crédito. Se a sua frota tem placas de outros países, relaxe a validação e deixe a API responder.

Cache, cotas e erros

Funções personalizadas são recalculadas quando os argumentos mudam e podem ser reexecutadas quando a planilha é reaberta. Sem cache, isso significa pagar de novo pela mesma consulta. O CacheService guarda o resultado por até seis horas; depois disso, a próxima recalculação chama a API novamente. Para dados que não mudam (o CEP de um endereço, a razão social de um fornecedor), copie as colunas e cole como valores assim que terminar, congelando o resultado.

O Apps Script também impõe cotas: cada função personalizada tem tempo máximo de execução, e há um limite diário de chamadas UrlFetchApp por conta. Os números atuais estão na página de cotas do Google; o que importa na prática é não arrastar uma fórmula por mil linhas de uma vez. Para lotes grandes, um menu que processa a seleção e grava valores direto é mais previsível:

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("APIBrasil")
    .addItem("Consultar CNPJs da seleção", "consultarCnpjsSelecionados")
    .addToUi();
}

/** Lê a coluna selecionada (CNPJs) e grava o resultado nas 6 colunas à direita. */
function consultarCnpjsSelecionados() {
  const intervalo = SpreadsheetApp.getActiveRange();
  const linhas = intervalo.getValues();
  const saida = linhas.map(function (linha) {
    try {
      const r = CONSULTA_CNPJ(linha[0]);
      return Array.isArray(r) ? r[0] : [r, "", "", "", "", ""];
    } catch (e) {
      return ["ERRO: " + e.message, "", "", "", "", ""]; // saldo, token, CNPJ inexistente...
    }
  });
  intervalo.offset(0, 1, linhas.length, 6).setValues(saida);
}

Na fórmula, um erro da API aparece como #ERROR! com a mensagem ao passar o mouse; no menu, ele vai para a célula como texto, o que facilita filtrar o que falhou e rodar de novo só essas linhas. Em qualquer dos dois caminhos, três erros são os mais comuns: token ausente ou inválido (confira a propriedade do script), saldo insuficiente (HTTP 402, recarregue no painel) e documento inexistente (a API responde error igual a true com a mensagem).

Por fim, trate o acesso à planilha como acesso ao token: quem pode editar o script consegue ler as propriedades. Em planilhas compartilhadas com muita gente, mantenha o script em uma cópia restrita e distribua apenas os valores, ou publique as funções como biblioteca com o token na biblioteca.

Perguntas frequentes

A função recalcula e cobra de novo toda vez que abro a planilha?

Com o cache de seis horas, reaberturas no mesmo período não geram nova chamada. Depois disso, uma recalculação consulta a API outra vez. Para dados estáveis, copie e cole como valores depois da consulta; a fórmula cumpriu o papel dela.

O token fica visível para quem tem acesso à planilha?

Quem apenas visualiza ou edita células não vê as propriedades do script. Quem tem permissão de editar o projeto Apps Script consegue abrir as configurações e ler o token. Restrinja a edição do script e revogue o token no painel se suspeitar de vazamento.

Por que a célula fica em “Carregando…” ou mostra #ERROR!?

“Carregando…” por mais de alguns segundos costuma indicar muitas fórmulas disparadas ao mesmo tempo ou a API demorando; aguarde ou reduza o lote. #ERROR! traz a mensagem do erro ao passar o mouse: token ausente, saldo insuficiente ou documento inválido são os casos típicos.

Dá para fazer o mesmo no Excel?

Não com este script, que é exclusivo do Google Sheets. No Excel, o caminho equivalente é Office Scripts ou Power Query chamando os mesmos endpoints com o mesmo header Authorization. A lógica de validação, cache e tratamento de error é a mesma.

Como consultar centenas de CNPJs de uma vez sem travar a planilha?

Para consultar CNPJ no Google Sheets em lote, use o menu APIBrasil em vez de arrastar a fórmula: ela processa a seleção em sequência, grava valores fixos e registra erros por linha. Para milhares de registros recorrentes, migre para um script fora da planilha, com fila e controle de saldo.

Próximos passos

Com as três funções no projeto, a planilha passa a consultar CNPJ, CEP e placa com a mesma naturalidade de um PROCV. Comece com homolog ativo para validar os mapeamentos, troque para consultas reais em um lote pequeno e acompanhe o campo balance nas respostas. Quando o volume justificar, o mesmo token e os mesmos endpoints servem para a versão em código, sem reescrever a lógica.

Loading

Deixe um comentário

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