Pular para o conteúdo
CODECYCLEPDF Services
Como funcionaPreçoPrivacidadeCódigo abertoFAQ
EntrarCriar conta
  1. Início
  2. Guias
  3. HTML para PDF em Python

Gerar PDF a partir de HTML em Python

Guia · atualizado em 12/09/2026 · 8 min de leitura

Quer gerar PDF a partir de HTML em Python? Relatórios, notas, certificados e contratos ficam muito mais fáceis de manter quando o layout é um template HTML com CSS. Este guia compara as bibliotecas mais usadas e mostra o código completo com requests, FastAPI e Django, usando a API do PDF Services.

  1. As opções em Python
  2. Passo a passo com requests
  3. Exemplo com FastAPI
  4. Exemplo com Django
  5. CSS para PDF
  6. Tratamento de erros
  7. Custos e privacidade

As opções em Python

OpçãoComo funcionaPontos de atenção
WeasyPrintRenderiza HTML e CSS de impressão em Python, sem navegador.Ótimo suporte a CSS de impressão, mas depende de bibliotecas nativas (Pango), que complicam a instalação no Windows e em imagens mínimas.
xhtml2pdfConversor em Python puro.Suporte limitado a CSS moderno: layouts mais elaborados costumam precisar de ajustes.
pdfkit (wkhtmltopdf)Wrapper do executável wkhtmltopdf.O wkhtmltopdf foi arquivado em 2023 e usa um WebKit antigo. Evite em projetos novos.
PlaywrightControla um Chromium headless e imprime a página.Precisa instalar e manter o navegador; custo alto de memória em servidores e funções.
API HTTP (PDF Services)Você envia o HTML e recebe o PDF.Depende de rede e é cobrado por uso, sem dependências nativas no servidor.

O PDF Services converte com o iText pdfHTML, um motor maduro de conversão de HTML e CSS para PDF, e todo o código do serviço é aberto sob a licença AGPL v3. Você não precisa instalar o iText, Java ou .NET: a conversão acontece na API.

Se você já usa WeasyPrint e ele atende, ótimo. A API faz mais sentido quando você não quer instalar bibliotecas do sistema, quando publica em ambientes serverless (AWS Lambda, Cloud Functions) ou quando prefere não ocupar o seu servidor com a renderização.

Passo a passo com requests

1. Crie a conta e a chave

Cadastre a empresa, confirme o e-mail e crie uma chave em Chaves de API no painel. Guarde-a em variável de ambiente ou em um gerenciador de segredos, nunca no repositório.

2. Envie o HTML e salve o PDF

Instale o requests (pip install requests) e rode:

gerar_pdf.py
import os

import requests

api_key = os.environ["PDFSERVICES_API_KEY"]

html = """<!doctype html>
<html lang="pt-BR">
<head>
  <meta charset="utf-8">
  <style>
    @page { size: A4; margin: 2cm; }
    body { font-family: sans-serif; }
  </style>
</head>
<body><h1>Olá, PDF!</h1></body>
</html>"""

resposta = requests.post(
    "https://api.pdfservices.sistema.site/v1/html2pdf/html",
    headers={"X-Api-Key": api_key, "Content-Type": "text/html; charset=utf-8"},
    data=html.encode("utf-8"),
    timeout=60,
)

if not resposta.ok:
    erro = resposta.json()
    raise RuntimeError(f"Falha {resposta.status_code}: {erro['error']} - {erro['message']}")

with open("documento.pdf", "wb") as arquivo:
    arquivo.write(resposta.content)

print("Cobrado: R$", resposta.headers["X-Charged-Amount"])

A resposta traz o PDF em application/pdf e os headers X-Charged-Amount (valor cobrado), X-Balance (saldo restante) e X-Transaction-Id. Sempre defina um timeout nas chamadas HTTP.

Codifique o HTML em UTF-8 ao enviar o corpo (html.encode("utf-8")) e declare o charset no Content-Type: assim acentos e cedilhas chegam intactos ao PDF.

Exemplo com FastAPI e Jinja2

Com FastAPI, use o httpx assíncrono para não bloquear o event loop, e o Jinja2 com autoescape para montar o HTML a partir de um template:

main.py (FastAPI)
import os

import httpx
from fastapi import FastAPI, HTTPException
from fastapi.responses import Response
from jinja2 import Environment, FileSystemLoader, select_autoescape

app = FastAPI()
templates = Environment(loader=FileSystemLoader("templates"), autoescape=select_autoescape())
API_URL = "https://api.pdfservices.sistema.site/v1/html2pdf/json"


@app.get("/faturas/{numero}/pdf")
async def fatura_pdf(numero: str):
    # O autoescape do Jinja2 impede que dados do usuário virem HTML no documento.
    html = templates.get_template("fatura.html").render(numero=numero)

    async with httpx.AsyncClient(timeout=60) as client:
        resposta = await client.post(
            API_URL,
            json={"html": html},
            headers={"X-Api-Key": os.environ["PDFSERVICES_API_KEY"]},
        )

    if resposta.status_code != 200:
        raise HTTPException(status_code=502, detail=f"Falha ao gerar o PDF ({resposta.status_code}).")

    return Response(
        content=resposta.content,
        media_type="application/pdf",
        headers={"Content-Disposition": 'inline; filename="fatura.pdf"'},
    )

Exemplo com Django

No Django, renderize o template normalmente com render_to_string, já com autoescape, e devolva o PDF em um HttpResponse:

views.py (Django)
import os

import requests
from django.http import HttpResponse
from django.template.loader import render_to_string


def fatura_pdf(request, numero):
    html = render_to_string("fatura.html", {"numero": numero})

    resposta = requests.post(
        "https://api.pdfservices.sistema.site/v1/html2pdf/json",
        json={"html": html},
        headers={"X-Api-Key": os.environ["PDFSERVICES_API_KEY"]},
        timeout=60,
    )
    resposta.raise_for_status()

    return HttpResponse(resposta.content, content_type="application/pdf")

Você pode reaproveitar os templates e o CSS que já usa nas páginas do sistema. Basta um bloco <style> com as regras de @page.

CSS para PDF: tamanho da página, cabeçalho, rodapé e quebras

O motor de conversão entende CSS de impressão (paged media). É com ele que você controla o documento, sem nada específico de Python:

estilo.css (dentro de uma tag <style>)
@page {
  size: A4;
  margin: 2.5cm 2cm;
  @top-center { content: "Relatório mensal"; }
  @bottom-right { content: "Página " counter(page) " de " counter(pages); }
}

.nova-pagina { page-break-before: always; }

/* Repete o cabeçalho da tabela em todas as páginas */
thead { display: table-header-group; }
  • Tamanho e margens: @page { size: A4; }, A4 landscape, Letter ou medidas como 21cm 29.7cm.
  • Cabeçalho e rodapé: as caixas de margem (@top-center, @bottom-right etc.) com counter(page) e counter(pages) geram o "Página 1 de 3".
  • Quebras: page-break-before, page-break-after e page-break-inside: avoid para não cortar blocos ao meio.
  • Imagens: embutidas como data: URI ou por URL https pública, em JPG ou PNG.
  • Fontes: as padrão já cobrem acentos; para a sua fonte, use @font-face com data: URI. Fontes por URL, como as do Google Fonts, não são baixadas.
  • JavaScript não é executado: monte o HTML final no servidor. Para gráficos, gere uma imagem PNG antes.

Tratamento de erros

Quando algo dá errado, a API responde com um JSON {"error": "...", "message": "..."}. Os casos que valem tratar no código:

HTTPCódigoO que fazer
401invalid_api_keyConfira a chave configurada; ela pode ter sido excluída no painel.
402insufficient_balanceSaldo insuficiente: adicione créditos. Não adianta repetir a chamada.
413payload_too_largeO corpo passou de 1 MB. Reduza imagens embutidas ou use URLs https.
422conversion_failedO HTML não pôde ser convertido. Revise o documento; a falha não é cobrada.
503busyMuitas chamadas simultâneas na mesma conta: repita com espera crescente.

Com requests, um HTTPAdapter com Retry(total=3, backoff_factor=1, status_forcelist=[503], allowed_methods=["POST"]) resolve as novas tentativas. Repita só no status 503 e em falhas de rede: repetir 401, 402 ou 422 não muda o resultado.

Custos e privacidade

O PDF Services cobra R$ 0,001 por conversão bem-sucedida, com créditos pré-pagos e sem mensalidade: mil PDFs custam R$ 1,00. Conversões com erro não são cobradas.

O HTML enviado e o PDF gerado são processados só em memória e nunca são armazenados nem registrados em logs. Ficam registrados apenas metadados da transação, como data, tamanho e valor cobrado. Detalhes na Política de Privacidade. Todo o código do serviço é aberto, sob a licença AGPL v3, e pode ser auditado no GitHub.

Gere seu primeiro PDF agora

Crie a conta da sua empresa, gere uma chave no painel e rode o exemplo acima. Pelo painel também dá para testar a conversão antes de escrever uma linha de código.

Criar conta Já tenho conta · Entrar

Outros guias

  • Gerar PDF a partir de HTML em C#
  • Gerar PDF a partir de HTML em Node.js
  • Referência rápida da API, com exemplos em PHP e cURL

HTML para PDF em C# · HTML para PDF em Node.js · HTML para PDF em Python

Termos de Uso · Política de Privacidade · Código-fonte (GitHub) · © CODECYCLE · Software livre sob AGPL v3