---
title: "Primeiros passos com o SDK Python da NFE.io"
description: "Instale o pacote nfe-io, crie um NfeClient, emita sua primeira NFS-e com create_and_wait e baixe o PDF."
source_url: https://nfe.io/docs/desenvolvedores/bibliotecas/python/primeiros-passos/
product: bibliotecas
last_updated: 2026-10-14
tags: ["python", "how-to", "nfse", "emissao"]
integration: ["sdk"]
---

# Primeiros passos

Este guia cobre a instalação, a criação do cliente e a emissão da sua primeira nota fiscal de
serviço (NFS-e), do envio até o PDF.

## 1. Instale o pacote

```bash
pip install nfe-io
# ou
uv add nfe-io
```

:::note Requisitos
Python **3.10** ou superior. O SDK não tem dependências de runtime: usa só a biblioteca padrão. O
pacote se chama `nfe-io` no PyPI e é importado como `nfeio`.
:::

## 2. Configure as chaves

A NFE.io usa duas chaves, cada uma para um grupo de APIs:

| Parâmetro | Variável de ambiente | Usada em |
|---|---|---|
| `api_key` | `NFE_API_KEY` | NFS-e, empresas, certificados, webhooks |
| `data_api_key` | `NFE_DATA_API_KEY` | consultas de CNPJ, CPF e CEP |

```bash
export NFE_API_KEY="sua-chave"
export NFE_DATA_API_KEY="sua-chave-de-dados"
```

O SDK nunca usa uma chave no lugar da outra. Se você tem uma chave só, use o mesmo valor nas duas.
Detalhes em [Configuração](./configuration.md).

## 3. Crie um cliente

```python
from nfeio import NfeClient

client = NfeClient()  # lê NFE_API_KEY e NFE_DATA_API_KEY
```

Também dá para passar as chaves direto: `NfeClient(api_key="...", data_api_key="...")`. O argumento
explícito vence a variável de ambiente. O cliente é thread-safe: crie um por configuração e
reutilize.

## 4. Emita uma NFS-e

```python
from decimal import Decimal

from nfeio import NfeClient, ServiceInvoice
from nfeio.types import ServiceInvoiceCreateParams

client = NfeClient()
company_id = "ID_DA_EMPRESA"

params: ServiceInvoiceCreateParams = {
    "cityServiceCode": "10677",
    "description": "Consultoria em tecnologia",
    "servicesAmount": Decimal("150.00"),
    "borrower": {
        "type": "NaturalPerson",
        "name": "Maria Silva",
        "federalTaxNumber": "52998224725",
        "email": "maria@example.com",
    },
}

invoice: ServiceInvoice = client.service_invoices.create_and_wait(
    company_id, params, external_id="pedido-1001"
)
print(invoice.id, invoice.flow_status)  # ... Issued
print(invoice["number"])                # qualquer campo do JSON, pelo nome original
print(repr(invoice.services_amount))    # Decimal(...), sem erro de arredondamento
```

Três coisas para notar:

- **O corpo usa as chaves da API** (camelCase), exatamente como na
  [referência da NFS-e](/desenvolvedores/rest-api/nota-fiscal-de-servico-v1): copie um exemplo da
  documentação e ele funciona. Os argumentos dos métodos seguem o padrão Python (snake_case).
- **`external_id` é a sua chave de deduplicação.** Use sempre, com um valor que identifique a venda
  no seu sistema. É ele que permite reconciliar uma emissão com resultado incerto sem duplicar a
  nota (veja [Tratamento de erros](./errors.md)).
- **`create_and_wait` espera a prefeitura.** A emissão é assíncrona: a API responde `202` e a nota
  passa por estados intermediários até `Issued`. O método consulta a nota até um estado final
  (120 s por padrão). Os detalhes estão em [Síncrono, assíncrono e polling](./async-and-polling.md).

:::warning Produção ou teste
Não existe host de homologação nem chave de teste. O ambiente (produção ou teste) é configurado
na empresa, na inscrição municipal. Para testar, use uma empresa com ambiente de testes.
:::

## 5. Baixe o PDF e o XML

```python
from pathlib import Path

from nfeio import NfeClient

client = NfeClient()
company_id = "ID_DA_EMPRESA"
invoice_id = "ID_DA_NOTA"

pdf: bytes = client.service_invoices.download_pdf(company_id, invoice_id)
Path("nota.pdf").write_bytes(pdf)

xml: bytes = client.service_invoices.download_xml(company_id, invoice_id)
Path("nota.xml").write_bytes(xml)
```

Mais em [Downloads](./downloads.md).

## 6. Leia a resposta

Toda resposta é um `NfeObject`, um `Mapping` imutável sobre o JSON recebido:

- `obj["campo"]` dá acesso a **qualquer** campo, inclusive os que o SDK ainda não conhece.
  Objetos aninhados também são `NfeObject`: `invoice["borrower"]["address"]["city"]["code"]`.
- Algumas **propriedades tipadas** corrigem ou tipam o que vem no fio: ids, status, datas
  (`datetime` com fuso), valores (`Decimal`) e documentos (CNPJ/CPF como `str` com zeros à
  esquerda). Exemplos: `invoice.flow_status`, `invoice.services_amount`, `invoice.issued_on`,
  `invoice.borrower.federal_tax_number`.
- `obj.to_dict()` devolve uma cópia em dicionários comuns, serializável com `json.dumps`.
- `obj.last_response.request_id` traz o `x-request-id` da resposta. Informe esse valor ao suporte.

:::tip Valores monetários
No acesso por chave, valores aparecem como vieram no JSON (`float`). Nas propriedades tipadas, como
`Decimal`. Nas requisições, `int`, `float` e `Decimal` são aceitos, e `Decimal("100.10")` é enviado
sem perda como `100.10`.
:::

## Próximos passos

- [Configuração](./configuration.md): timeouts, retry, TLS e opções por chamada.
- [Tratamento de erros](./errors.md): a regra de nunca reenviar uma emissão às cegas.
- [Webhooks](./webhooks.md): receba a conclusão da nota por push, sem polling.
- [Notas de serviço](./recursos/service-invoices.md): todos os métodos de `service_invoices`.
