---
title: "Como emitir NF-e com Python: certificado, assinatura e envio à SEFAZ"
url: "https://python.dev.br/blog/emitir-nfe-python-nota-fiscal-eletronica/"
markdown_url: "https://python.dev.br/blog/emitir-nfe-python-nota-fiscal-eletronica.MD"
description: "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."
date: "2026-10-04"
author: "Equipe Python Dev BR"
---

# 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.


<p><strong>Resposta rápida:</strong> para <strong>emitir NF-e com Python</strong> você precisa de quatro coisas: montar o XML no leiaute 4.00, assiná-lo com um <strong>certificado digital A1 (ICP-Brasil)</strong> usando XML Signature, validá-lo contra o XSD oficial e transmiti-lo aos <strong>web services da SEFAZ</strong> (nfeAutorizacao e nfeRetAutorizacao) via SOAP. Bibliotecas como <code>nfelib</code>, <code>signxml</code> e <code>zeep</code> cobrem o caminho manual; o <code>PyNFe</code> 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.</p>

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](/blog/python-automacao-notas-fiscais-xml-nfe/) 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:

1. **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.
2. **Assinatura digital** do nó `infNFe` com o certificado do emissor, usando XML Signature (RSA-SHA256).
3. **Validação contra o XSD** oficial do pacote de schemas publicado pela SEFAZ.
4. **Transmissão do lote** ao web service `nfeAutorizacao` do estado (ou do ambiente nacional/SVRS).
5. **Consulta do recibo** em `nfeRetAutorizacao` até receber o protocolo de autorização (cStat 100) ou a rejeição.
6. **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:

```bash
# 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](/blog/python-secrets-tokens-senhas-seguros/) 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):

```xml
<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:

```python
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:

```python
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`:

```python
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 o `NFe`.
- 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](/blog/httpx-timeouts-retries-python/)):

```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](/blog/python-automacao-notas-fiscais-xml-nfe/) 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()`:

1. **Fila de emissão** — o pedido entra, a nota é montada e transmitida de forma assíncrona; a [agendamento de tarefas com APScheduler](/blog/apscheduler-agendar-tarefas-python/) ou um worker Celery evita travar a request do usuário.
2. **Máquina de estados** — montada, assinada, transmitida, autorizada/rejeitada, cancelada. Cada transição registrada com timestamp.
3. **Retry com cuidado** — erros 5xx podem ser reenviados; rejeição de conteúdo exige correção. Distinga os dois no código.
4. **Log estruturado** — recibo, cStat, xMotivo e chave de acesso em [log pesquisável](/blog/logging-em-python/); sem log não existe diagnóstico quando a contabilidade pergunta onde a nota parou.
5. **Guarda do XML** — o `nfeProc` autorizado é documento fiscal: arquive com backup e imutabilidade, e facilite a busca por chave de acesso.
6. **Tratamento de erros explícito** — veja [tratamento de erros em Python](/blog/tratamento-de-erros-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](/blog/python-e-excel-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](/blog/integracao-pagamentos-pix-python/), [validar CNPJs e CEPs](/blog/validar-cpf-cnpj-cep-python-brasilapi-viacep/) e [gerar relatórios em Excel](/blog/python-e-excel-openpyxl/) — 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](/carreira/analista-automacao-python/) e as [vagas Python abertas](/vagas/).
