FiscalAPI
Guias

Como consultar as certidões de um fornecedor por API?

Script pronto em Python: CND Federal, CNDT e CRF do FGTS de uma lista de CNPJs, com a situação, a validade e o PDF de cada certidão.

Atualizado em

Três chamadas por CNPJ: /api/consultar-cnd-federal, /api/consultar-cndt e /api/consultar-crf-fgts. O script desta página lê uma lista de CNPJs, grava uma planilha com a situação e a validade de cada certidão e salva os PDFs numa pasta.

  • Custo: 1 consulta por certidão, 3 por CNPJ: de R$ 0,075 a R$ 2,01 por fornecedor, conforme o plano (tabela).
  • Sem PDF, mais barato: /api/certidoes-todas devolve as três situações em 1 consulta.
  • Para testar: a API pede plano ou recarga, a partir de R$ 19,99/mês. No painel, sem programar, são 10 consultas grátis por mês.
  • Sem certificado digital: basta o CNPJ.
  • Comece: crie a conta e a chave de API.

O que cada certidão prova

CertidãoÓrgãoProvaValidade
CND FederalReceita Federal e PGFNTributos federais, contribuições previdenciárias e Dívida Ativa da União180 dias
CNDTTSTDébitos trabalhistas180 dias
CRF do FGTSCaixaRegularidade do empregador no FGTS30 dias

A CND estadual (ICMS) tem rota própria, por UF: Consultar CND.

Antes de começar

  • Python 3.9 ou mais novo. O SDK oficial não tem dependências.
  • A chave de API na variável de ambiente FISCALAPI_API_KEY.
  • Um arquivo fornecedores.csv com a coluna cnpj. Use o CNPJ da matriz: a CND Federal é emitida para a matriz e vale para as filiais.
pip install fiscalapi-br
export FISCALAPI_API_KEY="fapi_sua_chave_aqui"

O script

Salve como certidoes.py:

"""CND Federal, CNDT e CRF do FGTS de uma lista de CNPJs, com os PDFs."""
import base64
import csv
import os
import sys
import time

from fiscalapi_br import (
    APIConnectionError,
    AuthenticationError,
    BadRequestError,
    FiscalAPI,
    InsufficientBalanceError,
    RateLimitError,
    ServerError,
)

ENTRADA = "fornecedores.csv"   # coluna "cnpj"
SAIDA = "certidoes.csv"        # uma linha por certidão; é também o checkpoint
PASTA_PDF = "pdfs"
COLUNAS = ["cnpj", "certidao", "status", "validade_iso", "numero", "pdf", "erro"]

# Segundos entre chamadas, pelo limite por minuto do plano.
PAUSA = {"lite": 12.0, "starter": 3.0, "pro": 1.0, "enterprise": 0.5}
PLANO = "starter"


def limpar(valor: str) -> str:
    return "".join(c for c in valor if c.isalnum()).upper()


def ja_feitas() -> set[tuple[str, str]]:
    if not os.path.exists(SAIDA):
        return set()
    with open(SAIDA, encoding="utf-8", newline="") as f:
        return {(linha["cnpj"], linha["certidao"]) for linha in csv.DictReader(f)}


def gravar(linha: dict) -> None:
    novo = not os.path.exists(SAIDA)
    with open(SAIDA, "a", encoding="utf-8", newline="") as f:
        w = csv.DictWriter(f, fieldnames=COLUNAS)
        if novo:
            w.writeheader()
        w.writerow(linha)


def consultar(chamada, cnpj: str) -> dict:
    """Chama a rota e espera se a certidão ainda estiver sendo emitida."""
    for _ in range(4):
        resp = chamada(cnpj=cnpj)
        if resp["source_status"].get("error_code") != "EM_PROCESSAMENTO":
            return resp
        # O tempo sugerido vem no header Retry-After, que o SDK não expõe: 30 s cobre.
        time.sleep(30)
    return resp


def main() -> None:
    api = FiscalAPI()  # lê FISCALAPI_API_KEY; repete sozinho em 429 e 5xx
    certidoes = {
        "cnd_federal": api.consultar_cnd_federal,
        "cndt": api.consultar_cndt,
        "crf_fgts": api.consultar_crf_fgts,
    }
    os.makedirs(PASTA_PDF, exist_ok=True)
    with open(ENTRADA, encoding="utf-8-sig", newline="") as f:   # utf-8-sig: aceita CSV salvo pelo Excel
        cnpjs = list(dict.fromkeys(limpar(linha["cnpj"]) for linha in csv.DictReader(f)))
    feitas = ja_feitas()

    for cnpj in filter(None, cnpjs):
        for nome, chamada in certidoes.items():
            if (cnpj, nome) in feitas:
                continue
            inicio = time.monotonic()
            linha = {"cnpj": cnpj, "certidao": nome}
            try:
                resp = consultar(chamada, cnpj)
            except InsufficientBalanceError as e:
                print(f"Parei no {cnpj}: {e.message}. Rode de novo depois de recarregar.")
                sys.exit(2)
            except AuthenticationError as e:
                print(f"Chave recusada: {e.message}")
                sys.exit(3)
            except BadRequestError as e:
                gravar({**linha, "erro": e.code})          # pedido recusado: não repete
                continue
            except RateLimitError as e:
                time.sleep(e.retry_after or 60)             # fica para a próxima rodada
                continue
            except (ServerError, APIConnectionError):
                continue                                    # fica para a próxima rodada

            codigo = resp["source_status"].get("error_code") or ""
            r = resp.get("result")
            if codigo in ("CNPJ_INVALIDO", "CPF_INVALIDO"):
                # Vem em HTTP 200, sem exceção. Não é cobrado e não adianta repetir.
                gravar({**linha, "erro": codigo})
            elif not r or r["status"] == "erro":
                # O órgão não respondeu ou recusou o pedido. Não é cobrado e não é gravado:
                # entra de novo na próxima rodada. A mensagem diz o motivo.
                print(cnpj, nome, codigo or (r or {}).get("status_raw", ""))
            else:
                if r.get("pdf_base64"):
                    linha["pdf"] = os.path.join(PASTA_PDF, f"{cnpj}_{nome}.pdf")
                    with open(linha["pdf"], "wb") as f:
                        f.write(base64.b64decode(r["pdf_base64"]))
                gravar({**linha, "status": r["status"], "validade_iso": r.get("validade_iso", ""),
                        "numero": r.get("protocolo", "")})

            espera = PAUSA[PLANO] - (time.monotonic() - inicio)
            if espera > 0:
                time.sleep(espera)

    print("Fim. O que não entrou em certidoes.csv é consultado se você rodar de novo.")


if __name__ == "__main__":
    main()

Rode com python certidoes.py. Ajuste PLANO para o seu plano.

Como ler o resultado

O campo status de cada certidão:

  • negativa: regular, sem pendências. No CRF, empregador regular perante o FGTS.
  • positiva_com_efeitos_de_negativa: há débitos suspensos ou garantidos. Vale como negativa.
  • positiva: há pendências. No CRF, não existe certificado a emitir, e a validade vem vazia.
  • nao_contribuinte: só na CND Federal, quando o CNPJ não está cadastrado na Receita.

validade_iso vem em AAAA-MM-DD, pronto para ordenar e filtrar. numero é o número da certidão ou do certificado, para conferir no site do órgão.

A coluna erro guarda só o que não adianta repetir: CNPJ_INVALIDO, quando o documento não tem o formato de um CNPJ. Esse caso chega em HTTP 200, sem exceção, e não é cobrado. Corrija o CNPJ em fornecedores.csv, apague a linha em certidoes.csv e rode de novo.

status: "erro" não entra na planilha. Ele quer dizer que o órgão não respondeu naquele momento ou recusou o pedido, e nas três certidões costuma ser passageiro. O script mostra a mensagem na tela, não cobra e consulta de novo na próxima rodada. Se a mensagem da CND Federal se repetir dizendo que o CNPJ é de filial, troque pelo CNPJ da matriz: a certidão sai para a matriz.

Quando consultar de novo

  • CRF do FGTS: vale 30 dias. Quem acompanha fornecedor consulta todo mês.
  • CND Federal e CNDT: valem 180 dias. Filtre validade_iso e refaça as que vencem nas próximas semanas.
  • CND Federal: a API devolve a certidão que ainda vale; se ela não existe ou vence em até 3 dias, emite uma nova na mesma chamada, o que pode levar até cerca de 60 segundos.
  • Renovar a CND Federal antes do vencimento: refazer a consulta devolve a mesma certidão enquanto ela valer por mais de 3 dias. Para sair com uma nova, de 180 dias, chame api.consultar_cnd_federal(cnpj=cnpj, emitir_nova=True).

Para refazer só uma parte, apague as linhas dela em certidoes.csv e rode de novo.

Só a situação, numa chamada

Se você não precisa do PDF, /api/certidoes-todas consulta as três certidões em paralelo e conta como 1 consulta:

resp = api.consultar_certidoes_todas(cnpj="12345678000195", skip_estaduais=True)
for c in resp["certidoes_federais"]:
    if c["result"]:
        print(c["result"]["tipo"], c["result"]["status"], c["result"]["validade_iso"])

Sem skip_estaduais, a mesma chamada inclui a CND estadual nos 27 estados e pode levar mais de um minuto. Detalhes em Consultar Todas as Certidões.

Quanto custa

PlanoUma certidãoAs três, com PDFPor minuto
LiteR$ 0,67R$ 2,015
StarterR$ 0,10R$ 0,3020
ProR$ 0,065R$ 0,19560
EnterpriseR$ 0,025R$ 0,075120

As três só com a situação, pela rota certidoes-todas, custam o mesmo que uma certidão. "Por minuto" é o número de consultas por minuto do plano.

Resposta com erro HTTP ou com status: "erro" não é cobrada. Veja Planos e limites e as comparações de APIs de CND Federal, CNDT e CRF do FGTS.

Perguntas frequentes

Existe API para tirar a CND Federal, a CNDT e o CRF do FGTS?

Sim. A FiscalAPI tem uma rota para cada certidão e uma que consulta as três de uma vez. Todas pedem só o CNPJ e devolvem a situação, a validade e o número em JSON; as rotas individuais trazem também o PDF.

Como automatizar as certidões de vários fornecedores?

Com uma lista de CNPJs e uma chamada por certidão. O script desta página grava a situação e a validade numa planilha e salva os PDFs numa pasta, e pode rodar todo mês.

Precisa de certificado digital ou procuração?

Não. As três certidões são emitidas só com o CNPJ.

Dá para consultar por CPF?

A CNDT e o CRF aceitam CPF. A CND Federal, por enquanto, só CNPJ.

A API devolve o PDF da certidão?

Sim, em base64, no campo pdf_base64 de cada rota individual. A rota que consulta as três de uma vez não traz os PDFs.

Planos e preços

Esta pagina foi util?

On this page