---
title: "Downloads de PDF e XML no SDK Python da NFE.io"
description: "Baixe o PDF, o XML e o XML de cancelamento da NFS-e como bytes. O SDK segue o redirecionamento para a URL temporária sem reenviar a chave de API."
source_url: https://nfe.io/docs/desenvolvedores/bibliotecas/python/downloads/
product: bibliotecas
last_updated: 2026-10-14
tags: ["python", "referencia", "nfse", "xml"]
integration: ["sdk"]
---

# Downloads

Os downloads da NFS-e devolvem `bytes`, prontos para gravar em disco, anexar num e-mail ou enviar
para um bucket.

| Método | Conteúdo |
|---|---|
| `service_invoices.download_pdf(company_id, invoice_id)` | PDF da nota (DANFSE). |
| `service_invoices.download_xml(company_id, invoice_id)` | XML da nota, sem alteração. |
| `service_invoices.download_cancellation_xml(company_id, invoice_id)` | XML do cancelamento. |

```python
from pathlib import Path

from nfeio import NfeClient

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

Path("nota.pdf").write_bytes(client.service_invoices.download_pdf(company_id, invoice_id))
Path("nota.xml").write_bytes(client.service_invoices.download_xml(company_id, invoice_id))
```

Baixe depois que a nota chegar a `Issued` (ou `Cancelled`, para o XML de cancelamento). Com
`create_and_wait`, isso já está garantido quando o método retorna.

## Redirecionamento sem a chave

A API não devolve o arquivo direto: ela responde `302` para uma URL temporária e pré-assinada, em
outro host. O SDK segue esse redirecionamento por você, com três cuidados:

- a requisição para a URL temporária sai **sem** o cabeçalho `Authorization`. A URL já carrega a
  própria autorização, e a sua chave nunca é enviada a um host para onde foi redirecionada;
- só redirecionamentos `https` são seguidos, até três saltos;
- só os downloads seguem redirecionamento. Em qualquer outra chamada, um `3xx` vira
  `UnexpectedResponseError`.

O limite de tamanho de resposta (`max_response_bytes`, 10 MiB por padrão) vale também para os
arquivos.

## XML

O XML volta intocado. O da NFS-e costuma começar direto em `<Nfse>`, sem o prólogo `<?xml ...?>`. Se
for interpretar o XML, use um parser protegido contra entidades maliciosas, como o
[`defusedxml`](https://pypi.org/project/defusedxml/) (o SDK não depende dele):

```python
from defusedxml.ElementTree import fromstring

from nfeio import NfeClient

client = NfeClient()

xml = client.service_invoices.download_xml("ID_DA_EMPRESA", "ID_DA_NOTA")
raiz = fromstring(xml)
print(raiz.tag)
```

## XML de cancelamento

Só alguns provedores geram o XML de cancelamento (o Ambiente Nacional, por exemplo). Nos outros, a
API responde `404`, e o método levanta `NotFoundError`:

```python
from nfeio import NfeClient, NotFoundError

client = NfeClient()

try:
    xml_cancelamento = client.service_invoices.download_cancellation_xml(
        "ID_DA_EMPRESA", "ID_DA_NOTA"
    )
except NotFoundError as erro:
    print("sem XML de cancelamento para este provedor:", erro.message)
```

## No cliente assíncrono

Os mesmos métodos, com `await`:

```python
import asyncio
from pathlib import Path

from nfeio import AsyncNfeClient


async def main() -> None:
    async with AsyncNfeClient() as client:
        pdf = await client.service_invoices.download_pdf("ID_DA_EMPRESA", "ID_DA_NOTA")
        Path("nota.pdf").write_bytes(pdf)


asyncio.run(main())
```

## Reenvio por e-mail

Para reenviar a nota por e-mail ao tomador, sem baixar nada:

```python
from nfeio import NfeClient

client = NfeClient()

client.service_invoices.send_email("ID_DA_EMPRESA", "ID_DA_NOTA")
```
