SDK Python
SDK Python oficial da engineAPI (EngineApiClient). Pacote engine-api-python, versão 1.0.0.
SDK Python
O SDK Python oficial está na versão 1.0.0 (Python 3.9+, dependências
httpx+pydantic). Confirme a disponibilidade do pacote no PyPI antes de instalar. Se
ainda não estiver publicado no seu ambiente, use a integração direta com httpx na
seção final desta página.
pip install engine-api-python
Configuração
from engineapi import EngineApiClient
client = EngineApiClient(
base_url="https://api.engineapi.com.br", # sem /v1, o client adiciona
api_key="ek_live_SEU_API_KEY",
timeout=30.0, # opcional, padrão 30s
)
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
base_url | str | Sim | URL base da API (sem /v1) |
api_key | str | Sim* | API Key server-to-server (ek_live_/ek_test_) |
token | str | Sim* | JWT de sessão de dashboard |
timeout | float | Não | Timeout em segundos (padrão 30) |
*Use api_key OU token. O client também suporta uso como context manager
(with EngineApiClient(...) as client: ...), fechando a conexão HTTP automaticamente.
Módulos Disponíveis
client.nfe # NFe: emissão, cancelamento, CC-e, download, status
client.companies # Empresas: ver aviso abaixo, contrato desatualizado
O pacote também expõe client.dfe; esse módulo está fora do contrato público hoje.
Não use em produção; será reativado quando a funcionalidade entrar na superfície
pública.
NFe: Emitir
emitir() recebe um dict repassado verbatim ao corpo da requisição: use os nomes
de campo reais do contrato: destinatario, items (não itens), pagamentos (lista,
não pagamento singular), cnpjCpf/nome/ie no destinatário.
nfe = client.nfe.emitir({
"naturezaOperacao": "VENDA DE MERCADORIA",
"destinatario": {
"cnpjCpf": "99888777000100",
"nome": "Cliente Exemplo SA",
"indicadorIE": 1,
"endereco": {
"logradouro": "Av Brasil", "numero": "500",
"bairro": "Centro", "codigoMunicipio": "3550308",
"municipio": "São Paulo", "uf": "SP", "cep": "01001000",
},
},
"items": [{
"codigo": "PROD001",
"descricao": "Camiseta Azul M", "ncm": "61091000",
"cfop": "5102", "unidade": "UN", "quantidade": 1,
"valorUnitario": 89.90,
"icms": {"origem": 0, "csosn": "400"},
}],
"pagamentos": [{"forma": "01", "valor": 89.90}],
})
print(f"NFe autorizada: {nfe['data']['accessKey']} ({nfe['data']['status']})")
issuerId é opcional no payload de NFe/NFCe: com um único emissor pode omitir; com
dois ou mais, informe o issuerId (UUID) do CNPJ que deve emitir; sem ele a API responde
400.
Cancelar, CC-e e downloads
# Cancelar: campo justificativa, mínimo 15 caracteres
client.nfe.cancelar(
"35260211222333000181550010000000011000000019",
"Erro nos dados do destinatário informado na venda",
)
# Carta de Correção
client.nfe.carta_correcao(
"35260211222333000181550010000000011000000019",
"Corrijo o endereço do destinatário para Av Brasil, 500",
)
# Downloads
pdf_bytes = client.nfe.download_pdf("35260211...")
xml_bytes = client.nfe.download_xml("35260211...")
Uso assíncrono
O pacote é síncrono (baseado em httpx.Client). Para FastAPI/asyncio, rode as chamadas
em thread separada (asyncio.to_thread) ou aguarde a variante async oficial:
import asyncio
from fastapi import FastAPI
app = FastAPI()
@app.post("/nfe")
async def emitir(dados: dict):
return await asyncio.to_thread(client.nfe.emitir, dados)
Companies: Empresas Emissoras
O client companies desta versão do pacote chama rotas erradas. criar(),
listar(), buscar() e upload_certificado() fazem requisições para /issuers/*, mas
a API real expõe o módulo em /companies; /issuers não existe e retorna 404. O
upload de certificado também está errado: o client manda {"certificado": ..., "senha": ...} em JSON, mas a rota real é multipart/form-data com o campo file.
Até essa versão ser corrigida, use httpx diretamente para o módulo de empresas
(exemplo abaixo).
import httpx
http = httpx.Client(base_url="https://api.engineapi.com.br/v1",
headers={"Authorization": f"Bearer {token}"})
# Cadastrar empresa
empresa = http.post("/companies", json={"cnpj": "11222333000181", "name": "Empresa Exemplo Ltda"})
# Upload de certificado: multipart, campo "file"
with open("certificado.pfx", "rb") as f:
http.post(
f"/companies/{issuer_id}/certificate",
files={"file": ("certificado.pfx", f, "application/x-pkcs12")},
data={"password": "senhaDoCertificado"},
)
Login e API Keys
# Login: retorna {access_token, partner} segundo a docstring do client, mas o
# servidor real devolve {access_token, user: {id,email,name,partnerId,role}}. Trate
# `user`, não `partner`, ao ler o retorno.
login = client.login("usuario@partner.com", "senha")
# Regenerar API Key de produção (exige subscription ACTIVE)
client.regenerate_api_key()
# Info da key atual
client.api_key_info()
Tratamento de erros
import httpx
try:
nfe = client.nfe.emitir(dados)
except httpx.HTTPStatusError as e:
body = e.response.json()
if e.response.status_code == 400:
# Validação OU rejeição SEFAZ: ver body["error"]["erros"]
print("Erro:", body["error"]["detail"])
elif e.response.status_code == 422:
print("Emissão assistida: campo não resolvido:", body["error"])
elif e.response.status_code == 429:
print("Rate limit. Aguarde e tente novamente")
else:
print(f"Erro {e.response.status_code}: {body}")
Não existem sefazCode/sefazMessage no corpo do erro: o formato real é RFC 7807:
error.erros[] ({codigo, descricao}) para rejeição SEFAZ. Ver
Erros e Rejeições.
Alternativa: integração direta com httpx
Se o pacote engine-api-python ainda não estiver disponível no seu ambiente, integre
diretamente:
import httpx
class EngineAPI:
def __init__(self, base_url: str, api_key: str):
self.client = httpx.Client(
base_url=f"{base_url.rstrip('/')}/v1",
headers={"x-api-key": api_key},
timeout=30.0,
)
def emitir_nfe(self, dados: dict) -> dict:
response = self.client.post("/nfe", json=dados)
response.raise_for_status()
return response.json()
def cancelar_nfe(self, id_ou_chave: str, justificativa: str) -> dict:
response = self.client.post(
f"/nfe/{id_ou_chave}/cancelar",
json={"justificativa": justificativa},
)
response.raise_for_status()
return response.json()
engine = EngineAPI("https://api.engineapi.com.br", "ek_live_SEU_API_KEY")
nfe = engine.emitir_nfe({ # payload real, ver /guides/emitir-nfe
})
print(f"NFe autorizada: {nfe['data']['accessKey']}")