API para Baixar XML de NF-e por Chave de Acesso (com Certificado Digital)
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 (respostacStat 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 statusfora_prazoquando 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)
{
"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:
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#
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#
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):
{
"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#
| Campo | Nível | Descrição |
|---|---|---|
| request_id | envelope | Identificador da requisição (use em logs/suporte). |
| total | envelope | Sempre 1 (uma chave por requisição). |
| com_dado | envelope | 1 se a chave retornou XML (status: ok), senão 0. |
| creditos_cobrados | envelope | Créditos consumidos (igual a com_dado). |
| resultados[] | envelope | Lista com um item (a chave consultada). |
| chave | item | Chave de 44 dígitos normalizada. |
| status | item | Resultado da chave (ver tabela abaixo). |
| cstat | item | Código de status retornado pela SEFAZ (ex.: 138, 632). |
| mensagem | item | Mensagem legível associada ao resultado. |
| from_cache | item | true se servido de cache. |
| xml | item | XML completo da NF-e (presente apenas quando status: ok). |
Status por chave#
| status | Significa | Tem xml? | Cobra crédito? |
|---|---|---|---|
| ok | XML completo retornado | Sim | Sim |
| somente_resumo | SEFAZ só liberou dados de resumo | Não | Não |
| nao_autorizado | Certificado não é participante da nota | Não | Não |
| nao_encontrado | Chave inexistente na base da SEFAZ | Não | Não |
| fora_prazo | cStat 632 — nota antiga que nem a consulta por chave nem o portal liberaram | Não | Não |
| chave_invalida | Chave fora do formato de 44 dígitos | Não | Não |
| erro | Falha na consulta (rejeição/indisponibilidade) | Não | Nã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; leiaresultados[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#
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 →