Como emitir NF-e com Python: certificado, assinatura e envio à SEFAZ
Emitir e consultar NF-e com Python: entenda certificado A1, assinatura XML com signxml, validação XSD, envio aos web services da SEFAZ, homologação e quando usar uma API.
Resposta rápida: para emitir NF-e com Python você precisa de quatro coisas: montar o XML no leiaute 4.00, assiná-lo com um certificado digital A1 (ICP-Brasil) usando XML Signature, validá-lo contra o XSD oficial e transmiti-lo aos web services da SEFAZ (nfeAutorizacao e nfeRetAutorizacao) via SOAP. Bibliotecas como nfelib, signxml e zeep cobrem o caminho manual; o PyNFe agrupa as etapas; APIs comerciais como NFE.io e Focus NFe emitem por você. Sem certificado válido, não existe emissão própria — só via API contratada.
Emitir nota fiscal é diferente de apenas ler notas recebidas. Se o seu objetivo é consolidar, conferir ou extrair dados de XML de NF-e que a empresa já recebeu, o guia de automação de XML de notas fiscais cobre esse lado. Aqui o assunto é o fluxo de emissão e consulta: transformar um pedido do seu sistema em uma nota autorizada, com responsabilidade sobre assinatura, transmissão e arquivamento.
Este guia é técnico e não substitui o contador ou o ERP. Regras fiscais de CFOP, ICMS, PIS/COFINS e regime tributário variam por empresa e por estado — a parte fiscal do XML deve nascer validada. O papel do Python é automatizar e integrar com segurança o que a legislação já define.
Como funciona a emissão de NF-e
A NF-e (modelo 55) segue um fluxo nacional, independentemente do estado emissor:
- Montagem do XML no leiaute 4.00, com a chave de acesso de 44 dígitos calculada a partir de UF, data, CNPJ, série, número e código numérico.
- Assinatura digital do nó
infNFecom o certificado do emissor, usando XML Signature (RSA-SHA256). - Validação contra o XSD oficial do pacote de schemas publicado pela SEFAZ.
- Transmissão do lote ao web service
nfeAutorizacaodo estado (ou do ambiente nacional/SVRS). - Consulta do recibo em
nfeRetAutorizacaoaté receber o protocolo de autorização (cStat 100) ou a rejeição. - Arquivamento do
nfeProc(NFe assinada + protocolo) e, se necessário, geração do DANFE para impressão.
| Etapa | Quem valida | Erro comum |
|---|---|---|
| Montar o XML | XSD + regras de negócio | CFOP ou NCM errados, tributos inconsistentes |
| Assinar | SEFAZ na recepção | Assinar o nó errado ou alterar o XML depois de assinar |
| Transmitir | Web service da SEFAZ | Lote fora do tamanho ou do formato esperado |
| Consultar | nfeRetAutorizacao | Não tratar rejeição e reenviar o mesmo número |
| Arquivar | Fiscal/auditoria | Perder o XML autorizado (a guarda é obrigatória) |
Tudo isso roda primeiro em homologação (tpAmb=2), o ambiente de testes da SEFAZ, e só depois em produção (tpAmb=1). Nunca aponte o código para produção antes de validar em homologação.
NF-e, NFC-e e NFS-e: não confunda os documentos
Antes de escrever código, confirme qual documento o processo precisa emitir:
| Documento | Modelo | Quem autoriza | Uso típico |
|---|---|---|---|
| NF-e | 55 | SEFAZ estadual (ou ambiente nacional) | Venda entre empresas, circulação de mercadorias |
| NFC-e | 65 | SEFAZ estadual | Varejo, consumidor final, cupom eletrônico |
| NFS-e | — | Prefeitura municipal | Prestação de serviços |
A NF-e e a NFC-e compartilham boa parte do leiaute e dos web services. A NFS-e é outro mundo: cada prefeitura expõe integração própria (quando expõe), algumas seguem o padrão Abrasf, e há padrões como ABRASF e gim/DSF que exigem bibliotecas ou adaptações específicas. Se a tarefa é emitir nota de serviço, comece identificando o padrão da cidade antes de escolher biblioteca.
O que você precisa antes de programar
- Certificado digital A1 (arquivo
.pfx) ou A3 (token/smartcard) vinculado ao CNPJ emissor, emitido por autoridade ICP-Brasil. A1 é o mais prático para automação porque pode ser lido de arquivo. - Inscrição estadual e código de regime tributário válidos, definidos com a contabilidade.
- Schemas XSD do leiaute vigente (pacote PL_009 da SEFAZ) para validar localmente o XML antes de gastar uma chamada de web service.
- URLs dos web services do estado emissor, separadas para homologação e produção.
Convertendo o certificado A1 para PEM
O .pfx embute chave privada e certificado. Para usar em Python, exporte cada parte com o OpenSSL:
# chave privada (sem senha de arquivo; proteja o acesso ao disco e à senha de origem)
openssl pkcs12 -in certificado.pfx -nocerts -nodes -out chave_privada.pem
# certificado (parte pública)
openssl pkcs12 -in certificado.pfx -clcerts -nokeys -out certificado.pem
Guarde a senha do .pfx em variável de ambiente ou gerenciador de segredos — nunca no código. O artigo sobre segredos e tokens em Python mostra como estruturar isso sem vazar credenciais no repositório.
Montando o XML da nota
O coração da NF-e é o nó infNFe. Um exemplo mínimo (campos obrigatórios de uma operação realista, com tributos simplificados):
<infNFe Id="NFe35261012345678000190550010000011291234567890" versao="4.00">
<ide>
<cUF>35</cUF>
<cNF>12345678</cNF>
<natOp>VENDA DE MERCADORIA</natOp>
<mod>55</mod>
<serie>1</serie>
<nNF>112</nNF>
<dhEmi>2026-10-04T09:00:00-03:00</dhEmi>
<tpNF>1</tpNF>
<idDest>1</idDest>
<cMunFG>3550308</cMunFG>
<tpImp>1</tpImp>
<tpEmis>1</tpEmis>
<cDV>0</cDV>
<tpAmb>2</tpAmb>
<finNFe>1</finNFe>
<indFinal>0</indFinal>
<indPres>9</indPres>
<procEmi>0</procEmi>
</ide>
<emit>
<CNPJ>12345678000190</CNPJ>
<xNome>EMPRESA DEMONSTRACAO LTDA</xNome>
<enderEmit>
<xLgr>Avenida Paulista</xLgr>
<nro>1000</nro>
<xMun>Sao Paulo</xMun>
<UF>SP</UF>
</enderEmit>
<IE>123456789012</IE>
<CRT>3</CRT>
</emit>
<dest>
<CNPJ>98765432000100</CNPJ>
<xNome>CLIENTE ATACADO S.A.</xNome>
<enderDest>
<xMun>Campinas</xMun>
<UF>SP</UF>
</enderDest>
<indIE>1</indIE>
</dest>
<det nItem="1">
<prod>
<cProd>SKU-001</cProd>
<cEAN>SEM GTIN</cEAN>
<xProd>NOTEBOOK 14 POLEGADAS</xProd>
<NCM>84713012</NCM>
<CFOP>5101</CFOP>
<uCom>UN</uCom>
<qCom>2.0000</qCom>
<vUnCom>3500.00</vUnCom>
<vProd>7000.00</vProd>
<indTot>1</indTot>
</prod>
<imposto>
<ICMS>
<ICMS00>
<orig>0</orig>
<CST>00</CST>
<modBC>3</modBC>
<vBC>7000.00</vBC>
<pICMS>18.00</pICMS>
<vICMS>1260.00</vICMS>
</ICMS00>
</ICMS>
<PIS>
<PISAliq>
<CST>01</CST>
<vBC>7000.00</vBC>
<pPIS>1.65</pPIS>
<vPIS>115.50</vPIS>
</PISAliq>
</PIS>
<COFINS>
<COFINSAliq>
<CST>01</CST>
<vBC>7000.00</vBC>
<pCOFINS>7.60</pCOFINS>
<vCOFINS>532.00</vCOFINS>
</COFINSAliq>
</COFINS>
</imposto>
</det>
<total>
<ICMSTot>
<vBC>7000.00</vBC>
<vICMS>1260.00</vICMS>
<vProd>7000.00</vProd>
<vNF>7000.00</vNF>
</ICMSTot>
</total>
</infNFe>
Esse esqueleto serve para entender a anatomia; uma nota real tem dezenas de campos condicionais. Não monte essa string na mão. Use uma biblioteca que parte dos schemas oficiais, como a nfelib, que gera classes Python a partir do XSD do leiaute 4.00:
from nfelib.nfe.bindings.v4_0.nfe import Nfe
# a nfelib expõe classes tipadas para cada nó do leiaute;
# preencher é trabalho de mapear seu pedido para os campos do schema
A vantagem de gerar a partir do XSD é estrutural: enquanto o campo existir no leiaute, ele existe no objeto; quando a SEFAZ publica uma revisão, a biblioteca é regenerada a partir do schema novo.
Validando contra o XSD
Antes de assinar, valide o XML localmente com o lxml — é barato e evita rejeições óbvias:
from lxml import etree
schema = etree.XMLSchema(etree.parse("schemes/PL_009/nfe_v4.00.xsd"))
documento = etree.parse("nota.xml")
if not schema.validate(documento):
for erro in schema.error_log:
print(erro)
Erros de schema detectados em milissegundos na sua máquina custam minutos (e um número de nota) no web service.
Assinando o XML com signxml
A SEFAZ exige que o nó infNFe seja assinado com XML Signature enveloped, usando RSA-SHA256 e canonicalização exclusiva, com a referência apontando para o Id da nota. Em Python, a biblioteca de referência é a signxml:
from lxml import etree
from signxml import XMLSigner, methods
arquivo = etree.parse("nota.xml")
inf_nfe = arquivo.find(".//infNFe")
with open("chave_privada.pem", "rb") as f:
chave = f.read()
with open("certificado.pem", "rb") as f:
certificado = f.read()
signer = XMLSigner(
method=methods.enveloped,
signature_algorithm="rsa-sha256",
c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#",
)
assinado = signer.sign(
inf_nfe,
key=chave,
cert=certificado,
reference_uri=f"#{inf_nfe.get('Id')}",
)
Regras que evitam as rejeições mais comuns:
- Assine o
infNFe, não o documento inteiro nem oNFe. - Nada pode ser alterado no XML depois da assinatura — nem espaços. Qualquer edição invalida o digest.
- O certificado precisa estar dentro da validade e pertencer ao CNPJ do
emit. - Regerar a assinatura é sempre seguro; “consertar” o XML assinado, nunca.
Para conferir o resultado no lado de cá, valide a assinatura com XMLVerifier antes de transmitir.
Transmitindo à SEFAZ
A comunicação usa SOAP com o certificado (TLS cliente + mensagem). Os web services variam por estado e por ambiente; os dois métodos centrais da emissão são:
nfeAutorizacao— envia o lote (1 a 50 notas) e devolve um recibo;nfeRetAutorizacao— consulta o recibo até obter o protocolo.
O fluxo com zeep tem este formato (esqueleto; consulte o WSDL do seu estado e trate timeouts como em qualquer integração HTTP — veja timeouts e retries em Python):
from zeep import Client
from zeep.wsse.signature import BinarySignature
from lxml import etree
wsdl = "https://homologacao.nfe.fazenda.sp.gov.br/ws/nfeautorizacao4.asmx?WSDL"
client = Client(wsdl, wsse=BinarySignature("certificado.pem", "chave_privada.pem"))
lote = etree.tostring(nota_assinada)
resposta = client.service.nfeAutorizacao(
nfeDadosMsg={"enviNFe": lote},
)
print(resposta.cStat, resposta.xMotivo, resposta.infRec.nRec)
A resposta de autorização chega em cStat/xMotivo. Os códigos importantes para o seu código tratar:
| cStat | Significado | O que o código faz |
|---|---|---|
| 100 | Autorizada | Arquiva o nfeProc e gera o DANFE |
| 225 | Rejeição: falha no schema | Volta ao XSD, corrige e regera |
| 2xx (outros) | Rejeições de conteúdo | Corrige o XML e reenvia a mesma nota |
| 5xx/6xx | Erros de infraestrutura | Estratégia de retry com espera |
| 104 | Lote processado | Lê o resultado por nota dentro do lote |
Nunca incremente o número da nota ao rejeitar: rejeitada, a nota não consumiu numeração. O cancelamento (evento 110111) e a carta de correção também são eventos transmitidos por web service, com os mesmos cuidados de assinatura.
Consultando notas de terceiros
Para baixar notas emitidas contra o seu CNPJ (entradas), use a consulta de distribuição DFe por NSU ou chave de acesso. É o mesmo par certificado + SOAP, e o resultado alimenta pipelines de conferência — a matéria de automação de XML de NF-e mostra como extrair e validar esses arquivos em lote.
Fazer in-house ou usar uma API de emissão?
O caminho manual dá controle total, mas a manutenção é real: schemas mudam, estados divergem em detalhes de SOAP, certificados expiram. A decisão costuma ser:
| Critério | Emissão própria (nfelib + signxml + zeep / PyNFe) | API comercial (NFE.io, Focus NFe, eNotas, Tecnospeed) |
|---|---|---|
| Custo fixo | Sem mensalidade | Mensalidade por plano/volume |
| Esforço inicial | Alto (schemas, assinatura, WSDL) | Baixo (REST + JSON) |
| Certificado | Você gerencia A1 | O provedor assina |
| Manutenção de leiaute | Sua responsabilidade | Do provedor |
| Integração NFS-e | Separada, prefeitura a prefeitura | Muitas prefeituras cobertas |
| Auditoria do fluxo | Total, no seu log | Depende dos webhooks/reports |
Regra prática: volume baixo ou muitas prefeituras de NFS-e → API comercial tende a pagar-se rápido. Volume alto, requisitos fortes de dados internos ou produto que já é fiscal → emissão própria, validada em homologação, com monitoramento. Muitas empresas usam as duas: API para NFS-e municipal e fluxo próprio para NF-e.
Se optar por biblioteca pronta, avalie o PyNFe, que agrupa assinatura, transmissão e consulta a vários estados. Antes de adotar qualquer biblioteca comunitária, confirme a data do último commit e a compatibilidade com o leiaute vigente — o custo de uma rejeição em produção aparece na fila da contabilidade.
Arquitetura mínima de um emissor confiável
Um emissor de NF-e em produção precisa de mais do que uma função emitir():
- Fila de emissão — o pedido entra, a nota é montada e transmitida de forma assíncrona; a agendamento de tarefas com APScheduler ou um worker Celery evita travar a request do usuário.
- Máquina de estados — montada, assinada, transmitida, autorizada/rejeitada, cancelada. Cada transição registrada com timestamp.
- Retry com cuidado — erros 5xx podem ser reenviados; rejeição de conteúdo exige correção. Distinga os dois no código.
- Log estruturado — recibo, cStat, xMotivo e chave de acesso em log pesquisável; sem log não existe diagnóstico quando a contabilidade pergunta onde a nota parou.
- Guarda do XML — o
nfeProcautorizado é documento fiscal: arquive com backup e imutabilidade, e facilite a busca por chave de acesso. - Tratamento de erros explícito — veja tratamento de erros em Python para modelar exceções de certificado, schema e web service separadamente.
Perguntas frequentes
Quanto custa emitir NF-e com Python?
O software não tem custo de licença — as bibliotecas são open source. Os custos reais são o certificado digital (anual), o tempo de desenvolvimento/homologação e, se usar, a mensalidade de uma API comercial. Não existe taxa por nota cobrada pela SEFAZ pela emissão em si.
Preciso de ERP para emitir NF-e?
Não obrigatoriamente, mas o XML precisa de dados fiscais corretos (CFOP, NCM, tributos) que normalmente vêm do sistema de gestão e da contabilidade. Python pode ser o integrador entre seu sistema e a SEFAZ, mas a responsabilidade fiscal dos campos continua sendo da empresa emitente.
O DANFE pode ser gerado com Python?
O DANFE é a representação gráfica (impressa/PDF) da NF-e autorizada. Ele pode ser gerado em Python — por exemplo com ReportLab, a partir do nfeProc — e precisa seguir o leiaute oficial, incluindo o QR Code nas notas modelo 55. Para volumes altos, provedores de emissão entregam o DANFE pronto.
Homologação aceita XML de qualquer CNPJ?
Não. O ambiente de homologação tem regras próprias (CNPJ de teste, valores fixos em alguns estados) e não exige certificado de produção em todos os cenários, mas aceita apenas o formato definido no manual. Emissões em homologação não têm valor fiscal — servem só para validar o software.
Posso emitir NF-e a partir de uma planilha Excel?
Sim, esse é um caso comum de automação: ler pedidos de um Excel com OpenPyXL, validar os campos obrigatórios, montar o XML e transmitir. A planilha é a entrada; a validação fiscal antes da montagem é o que impede rejeições em cascata.
Conclusão
Emitir NF-e com Python é viável e bem documentado: XML tipado com nfelib, assinatura com signxml, SOAP com zeep — ou uma camada única com PyNFe, ou uma API comercial quando o custo de manutenção não se justifica. O trabalho de verdade não está no código de assinatura, e sim na validação prévia, na máquina de estados da emissão e no arquivamento do que foi autorizado.
Comece pelo ambiente de homologação do seu estado, com uma nota de teste, e cresça a partir do log das rejeições. Para os fluxos adjacentes — ler notas recebidas, receber pagamentos via Pix, validar CNPJs e CEPs e gerar relatórios em Excel — os outros guias do site cobrem cada peça. E se o seu interesse em automação fiscal é profissional, veja a carreira de analista de automação com Python e as vagas Python abertas.