Pular para o conteúdo
FiscalAPI
API & Integração

API para Baixar XML de NF-e por Chave de Acesso (com Certificado Digital)

Equipe FiscalAPI 6 min de leitura
APINF-eXMLIntegraçãoTutorial

Para baixar o XML completo de uma NF-e por chave de acesso via API, você faz um `POST` para `https://api.fiscalapi.com.br/api/consultar-nfe-xml` enviando a chave de 44 dígitos, o certificado A1 do participante (`.pfx` em base64) e a senha — a FiscalAPI consulta a SEFAZ na hora e devolve o XML. Uma chave por requisição, sem manter o certificado armazenado. Para baixar várias notas, dispare uma chamada por chave (mostramos um loop pronto abaixo).

Este guia é para devs: mostra o contrato da API, exemplos reais em cURL, Node.js e Python (incluindo como ler o .pfx e codificá-lo em base64), a tabela de campos da resposta e um loop pronto para baixar várias notas (uma chamada por chave).

Por que você precisa do certificado#

O XML completo da NF-e (com itens, NCM, CFOP, impostos, valores) é um documento fiscal. A SEFAZ só o entrega para um participante da nota: emitente, destinatário, transportador ou autorizado a download. Não existe "chave → XML sem certificado" — qualquer serviço que prometa isso está te entregando, no máximo, dados de resumo (status, protocolo, descrição truncada).

Por isso o endpoint exige o certificado A1 (.pfx) do participante. A FiscalAPI usa o certificado na hora da consulta e o descarta em seguida: zero custódia, nada é gravado em disco ou banco.

Sobre notas antigas: quando a SEFAZ não disponibiliza mais a nota pela consulta por chave (resposta cStat 632, típico de notas mais antigas), a FiscalAPI tenta automaticamente o portal da NF-e com o mesmo certificado. O item só volta com status fora_prazo quando nem o portal consegue liberar o XML.

O contrato da API#

Endpoint

POST https://api.fiscalapi.com.br/api/consultar-nfe-xml

Autenticação via header:

X-API-Key: fapi_sua_chave_aqui

Corpo (JSON)

json
{
  "chave": "43260689677595000128550030006074891411409127",
  "certificado": "<conteúdo do .pfx em base64>",
  "senha": "senha-do-certificado"
}

A chave pode vir com espaços ou pontos — a API normaliza para os 44 dígitos. É uma chave por requisição; para baixar várias, repita a chamada reaproveitando o mesmo certificado (veja o loop nos exemplos).

cURL: gerar o base64 e enviar#

O .pfx é binário, então ele vai no JSON codificado em base64. Em uma linha:

bash
PFX_B64=$(base64 -i certificado.pfx | tr -d '\n')

curl -X POST https://api.fiscalapi.com.br/api/consultar-nfe-xml \
  -H "X-API-Key: fapi_sua_chave_aqui" \
  -H "Content-Type: application/json" \
  -d "{
    \"chave\": \"43260689677595000128550030006074891411409127\",
    \"certificado\": \"$PFX_B64\",
    \"senha\": \"senha-do-certificado\"
  }"

No macOS use base64 -i arquivo.pfx; no Linux, base64 -w 0 arquivo.pfx já remove as quebras de linha.

Node.js: ler o .pfx, consultar e salvar o XML#

javascript
import fs from "node:fs";

const API_KEY = process.env.FISCALAPI_KEY; // "fapi_..."
const certificado = fs.readFileSync("certificado.pfx").toString("base64");
const senha = process.env.PFX_SENHA;

async function consultarChave(chave) {
  const resp = await fetch("https://api.fiscalapi.com.br/api/consultar-nfe-xml", {
    method: "POST",
    headers: {
      "X-API-Key": API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ chave, certificado, senha }),
  });
  const data = await resp.json();
  return data.resultados[0]; // envelope com um item (a chave consultada)
}

// Para baixar várias notas, faça uma chamada por chave (aqui, em sequência).
const chaves = ["43260689677595000128550030006074891411409127"];

for (const chave of chaves) {
  const item = await consultarChave(chave);
  if (item.status === "ok") {
    fs.writeFileSync(`${item.chave}.xml`, item.xml);
    console.log(`OK   ${item.chave} -> ${item.chave}.xml`);
  } else {
    console.warn(`SKIP ${item.chave} (${item.status}: ${item.mensagem})`);
  }
}

Python: base64, requests e gravação em arquivo#

python
import base64
import os

import requests

API_KEY = os.environ["FISCALAPI_KEY"]  # "fapi_..."

with open("certificado.pfx", "rb") as f:
    certificado_b64 = base64.b64encode(f.read()).decode()

senha = os.environ["PFX_SENHA"]


def consultar_chave(chave: str) -> dict:
    resp = requests.post(
        "https://api.fiscalapi.com.br/api/consultar-nfe-xml",
        headers={"X-API-Key": API_KEY},
        json={"chave": chave, "certificado": certificado_b64, "senha": senha},
        timeout=120,
    )
    resp.raise_for_status()
    return resp.json()["resultados"][0]  # envelope com um item


# Para baixar várias notas, faça uma chamada por chave (aqui, em sequência).
chaves = [
    "43260689677595000128550030006074891411409127",
]

for chave in chaves:
    item = consultar_chave(chave)
    if item["status"] == "ok":
        with open(f"{item['chave']}.xml", "w", encoding="utf-8") as out:
            out.write(item["xml"])
        print(f"OK   {item['chave']}")
    else:
        print(f"SKIP {item['chave']} ({item['status']}: {item['mensagem']})")

A resposta#

A API responde com um envelope e um array resultados com um item (a chave consultada):

json
{
  "request_id": "550e8400-e29b-41d4-a716-446655440000",
  "total": 1,
  "com_dado": 1,
  "creditos_cobrados": 1,
  "resultados": [
    {
      "chave": "43260689677595000128550030006074891411409127",
      "status": "ok",
      "cstat": "138",
      "mensagem": "Documento localizado.",
      "from_cache": false,
      "xml": "<nfeProc versao=\"4.00\" ...>...</nfeProc>"
    }
  ]
}

Campos da resposta#

CampoNívelDescrição
request_idenvelopeIdentificador da requisição (use em logs/suporte).
totalenvelopeSempre 1 (uma chave por requisição).
com_dadoenvelope1 se a chave retornou XML (status: ok), senão 0.
creditos_cobradosenvelopeCréditos consumidos (igual a com_dado).
resultados[]envelopeLista com um item (a chave consultada).
chaveitemChave de 44 dígitos normalizada.
statusitemResultado da chave (ver tabela abaixo).
cstatitemCódigo de status retornado pela SEFAZ (ex.: 138, 632).
mensagemitemMensagem legível associada ao resultado.
from_cacheitemtrue se servido de cache.
xmlitemXML completo da NF-e (presente apenas quando status: ok).

Status por chave#

statusSignificaTem xml?Cobra crédito?
okXML completo retornadoSimSim
somente_resumoSEFAZ só liberou dados de resumoNãoNão
nao_autorizadoCertificado não é participante da notaNãoNão
nao_encontradoChave inexistente na base da SEFAZNãoNão
fora_prazocStat 632 — nota antiga que nem a consulta por chave nem o portal liberaramNãoNão
chave_invalidaChave fora do formato de 44 dígitosNãoNão
erroFalha na consulta (rejeição/indisponibilidade)NãoNão

Regra de cobrança: você só paga 1 crédito por chave que retorna XML (status: ok). Chaves sem retorno não geram cobrança.

Boas práticas#

  • Trate o resultado pelo `status`. Não assuma que toda chave volta ok; leia resultados[0] e ramifique por status, como nos exemplos acima.
  • Para volume, uma chamada por chave. Reaproveite o mesmo cert/senha e controle a concorrência (ex.: 3 a 5 chamadas em paralelo) para baixar muitas notas sem sobrecarregar.
  • Respeite o limite de consultas da SEFAZ. Consultas repetidas demais da mesma chave podem retornar cStat 656 (consumo indevido). Não faça polling agressivo.
  • Guarde o `request_id` nos seus logs — ele agiliza qualquer investigação de suporte.

FAQ#

Dá para baixar o XML só com a chave, sem certificado?#

Não. O XML completo é entregue pela SEFAZ apenas a participantes da nota (emitente, destinatário, transportador ou autorizado), e a identificação é feita pelo certificado A1. Sem certificado, o máximo disponível é o resumo.

Por que algumas chaves voltam fora_prazo?#

A SEFAZ deixa de disponibilizar notas mais antigas pela consulta por chave (resposta cStat 632). Nesses casos a FiscalAPI tenta automaticamente o portal da NF-e com o mesmo certificado; o status fora_prazo só aparece quando nem o portal consegue liberar o XML.

O certificado fica armazenado em algum lugar?#

Não. A política é de zero custódia: o .pfx é usado no momento da consulta à SEFAZ e descartado em seguida. Nada é gravado em disco ou banco de dados.

Quantos créditos uma consulta consome?#

Um crédito quando a chave retorna XML (status: ok). Como é uma chave por requisição, ao baixar várias notas você paga 1 crédito por chave que retornou XML; chaves sem retorno não geram cobrança.

Posso enviar a chave com pontos ou espaços?#

Sim. A API normaliza a entrada para os 44 dígitos antes de consultar, então valores copiados de relatórios ou planilhas funcionam sem pré-tratamento.


Quer testar? Veja a documentação completa do endpoint, conheça o produto NF-e por Chave ou, se preferir o fluxo manual, veja como baixar o XML pela chave no painel.

Pronto para integrar? Crie sua conta e gere uma API key para fazer a primeira consulta em minutos.


Leia também#

Compartilhar: WhatsApp LinkedIn Twitter

Consulte dados fiscais via API

Consulte Inscrições Estaduais, CNPJs e Certidões Negativas via API REST. Dados direto da SEFAZ e Receita Federal. Planos a partir de R$ 19,99/mês.

Criar conta →