ENTRAR  login to app
2026-05-26 11 min de leitura

Como converter 100 faturas em 10 minutos com Python (tutorial SDK myocr)

Do zero a um pipeline batch funcional com o SDK oficial myocr-client. Código real, tratamento de erros real, entrega via webhook.

Por equipe myocr.app

Por que um SDK Python dedicado importa

Se você vai colocar OCR em produção em Python, tem três opções. Pode chamar a API com requests cru e escrever todo o boilerplate — autenticação, retries, mapeamento de erros, polling para jobs async, downloads de signed URL. Pode usar um gerador OpenAPI que produz um cliente funcional mas pouco ergonômico. Ou pode instalar um SDK mantido que abstrai o protocolo e deixa você escrever apenas código de negócio.

Este tutorial usa a terceira via: myocr-client, o SDK oficial Python do myocr.app. Em 11 minutos você sai do pip install a um script que converte 100 faturas PDF em paralelo, gerencia com elegância quota esgotada e erros OCR, e notifica seu app via webhook quando cada job termina.

O que você vai construir

Um único script Python que:

Total: ~30 linhas de código de negócio, mais um receptor de webhook se quiser entrega push.

Pré-requisitos

Passo 1: instalar

pip install myocr-client

É tudo. O SDK depende apenas de requests. Sem extensões nativas, sem runtime específico do SDK.

Passo 2: converta sua primeira fatura (sync)

from myocr_client import MyOCRClient

client = MyOCRClient(api_key="sk_live_...")  # ou defina MYOCR_API_KEY env var

result = client.convert("fatura.pdf", model="invoice")
result.save("fatura.xlsx")
print(f"{result.pages_used} páginas → fatura.xlsx ({result.request_id})")

Essa é uma chamada OCR completa. O método convert() é síncrono e melhor para arquivos abaixo de 5 MB e 10 páginas. O ConversionResult retornado inclui content (bytes), pages_used, o model original e um request_id que você pode grep nos logs do servidor.

Passo 3: mude para async para processamento batch

Para 100 faturas você não quer 100 chamadas sync sequenciais — é latência serial. Use /v1/batch. O SDK expõe como client.batch():

from pathlib import Path
from myocr_client import MyOCRClient

client = MyOCRClient()  # MYOCR_API_KEY do env

folder = Path("./faturas")
pdfs = sorted(folder.glob("*.pdf"))[:20]  # /v1/batch aceita até 20 arquivos

batch = client.batch([str(p) for p in pdfs], model="invoice")
print(f"Criados {batch.jobs_created} jobs, {len(batch.errors)} erros")

done = batch.wait_all(timeout=600)
for job in done:
    if job.is_done:
        job.download(f"out/{job.request_id}.xlsx")
    else:
        print(f"Falhou: {job.request_id} — {job.error_detail}")

Três coisas a notar. Primeiro, o endpoint /v1/batch aceita no máximo 20 arquivos por chamada — para 100 faturas, divida a pasta em 5 batches. Segundo, wait_all() faz polling de cada job individualmente com backoff exponencial (1s → 2s → 4s → 8s → 15s cap), então 100 chamadas sleep(1) não queimam seu rate limit. Terceiro, se um único arquivo falhar a extração OCR, apenas aquele Job termina em failed — os outros completam normalmente.

Passo 4: processe 100 faturas em 5 chunks

from pathlib import Path
from myocr_client import MyOCRClient

client = MyOCRClient()
folder = Path("./faturas")
all_pdfs = sorted(folder.glob("*.pdf"))
CHUNK = 20

out_dir = Path("./out"); out_dir.mkdir(exist_ok=True)

for i in range(0, len(all_pdfs), CHUNK):
    chunk = all_pdfs[i:i + CHUNK]
    print(f"\nBatch {i // CHUNK + 1}: {len(chunk)} arquivos")
    batch = client.batch([str(p) for p in chunk], model="invoice")
    for job in batch.wait_all(timeout=900):
        target = out_dir / f"{job.request_id}.xlsx"
        if job.is_done:
            job.download(str(target))
            print(f"  ✓ {target.name} ({job.pages_used} páginas)")
        else:
            print(f"  ✗ {job.request_id}: {job.error_detail}")

print("\nFeito.")

Tempo total numa key do tier grátis com concorrência padrão: aproximadamente 8-10 minutos para 100 faturas, dominado pela latência do processamento OCR, não pelo seu código. Em planos pagos com maior concorrência, o mesmo workload termina em 3-5 minutos.

Passo 5: tratamento de erros como adulto

O SDK mapeia cada código de erro da API para uma exceção tipada. Codifique defensivamente:

from myocr_client import (
    MyOCRClient,
    QuotaExceeded,
    OcrEngineError,
    RateLimited,
    InvalidApiKey,
    FileTooLarge,
)

client = MyOCRClient()

try:
    result = client.convert("fatura.pdf", model="invoice")
except QuotaExceeded as e:
    print(f"Plano {e.current_plan}: {e.calls_used}/{e.calls_limit} chamadas usadas")
    print(f"Upgrade em {e.upgrade_url}, reseta {e.reset_date}")
except FileTooLarge:
    print("Use create_job() em vez de convert() — mude para o path async")
except OcrEngineError:
    pass  # upstream OCR falhou, seguro tentar de novo após 30s
except RateLimited:
    pass  # raro: o SDK tenta 429 automaticamente até 3 vezes
except InvalidApiKey:
    raise SystemExit("Rotacione sua API key — a atual não é válida")

O retry automático em respostas 429 e 5xx (respeitando Retry-After) é a vitória ergonômica mais comum comparado com escrever código requests cru. Te salva do típico incidente em produção "esqueci de tratar a rede instável".

Passo 6: mude para webhooks (zero polling)

Polling funciona mas é ruidoso. Se você tem um endpoint HTTP exponível publicamente, a entrega via webhook é mais limpa. Passe webhook_url ao criar jobs:

batch = client.batch(
    pdfs,
    model="invoice",
    webhook_url="https://meuapp.com/webhooks/myocr",
)
# Sem esperar — seu endpoint receberá um POST por job

No lado receptor (exemplo Flask):

from flask import Flask, request
from myocr_client import verify_webhook_signature

app = Flask(__name__)
SECRET = "seu-secret-compartilhado"

@app.route("/webhooks/myocr", methods=["POST"])
def myocr_webhook():
    body = request.get_data()  # IMPORTANTE: bytes crus, não request.get_json()
    sig = request.headers.get("X-MyOCR-Signature", "")
    if not verify_webhook_signature(body, sig, SECRET):
        return "assinatura inválida", 401

    event = request.get_json()
    request_id = event["data"]["request_id"]
    if event["event"] == "job.completed":
        ...  # baixa result_url, atualiza DB, notifica usuário
    elif event["event"] == "job.failed":
        ...  # logue error_detail, retry ou alerte ops
    return "", 200

Duas coisas não óbvias: (1) verifique a assinatura sobre bytes crus — se você serializar o JSON parseado de volta para string, a assinatura não vai bater por causa de whitespace e ordem das chaves; (2) os webhooks têm política de retry automática do lado servidor (1m → 5m → 30m → 2h), então não se preocupe se seu endpoint ficou fora brevemente.

Passo 7: escolha o modelo certo

O SDK expõe os mesmos seis modelos prebuilt que a API REST oferece. Escolha o mais específico para seu tipo de documento:

Usar um modelo especializado é a diferença entre obter uma tabela desestruturada que precisa de limpeza e obter Fornecedor / Total / Itens já parseados em colunas Excel tipadas.

Passo 8: checklist de produção

Antes de enviar isso para um workload real:

Onde ir depois

A referência completa da API está em Scalar UI com exemplos copy-paste para cada endpoint. O código fonte do SDK é open source no GitHub — leia client.py se quiser estendê-lo, ou abra um issue se encontrar uma feature faltando.

Se você está integrando num SaaS que precisa de OCR, o SDK é também o path recomendado para nossas integrações Zapier, QuickBooks e Xero (todas usam internamente).

Resumo em uma tela

# pip install myocr-client
import os
from myocr_client import MyOCRClient, QuotaExceeded

client = MyOCRClient()  # MYOCR_API_KEY env var

# Sync (≤5MB, ≤10 páginas):
client.convert("doc.pdf", model="invoice").save("out.xlsx")

# Async + batch (≤20 arquivos/chamada, ≤50MB cada):
batch = client.batch(
    ["a.pdf", "b.pdf", "c.pdf"],
    model="invoice",
    webhook_url="https://meuapp.com/webhooks/myocr",
)
for job in batch.wait_all(timeout=600):
    if job.is_done:
        job.download(f"out/{job.request_id}.xlsx")

Mesmo padrão em Node.js / TypeScript

Prefere JavaScript? O SDK Node myocr-client tem a mesma superfície, mesmos nomes de método (camelCase), exceções tipadas, zero dependências runtime (Node 18+ usa fetch / FormData / crypto nativos).

// npm install myocr-client
import { MyOCRClient } from 'myocr-client';

const client = new MyOCRClient();  // MYOCR_API_KEY do env

// Sync:
const r = await client.convert('fatura.pdf', { model: 'invoice' });
await r.save('fatura.xlsx');

// Async + batch:
const batch = await client.batch(['a.pdf', 'b.pdf', 'c.pdf'], { model: 'invoice' });
const done = await batch.waitAll({ timeoutMs: 600_000 });
for (const job of done) {
  if (job.isDone) await job.download(`out/${job.requestId}.xlsx`);
}

Mesma API por trás, mesmos códigos de erro (mapeados em classes TS), mesma entrega de webhook e verificação de assinatura. Escolha a linguagem que encaixa no seu stack.

Experimente hoje: obtenha uma API key grátis em myocr.app/account/api (sem cartão), pip install myocr-client (ou npm install myocr-client) e processe seu primeiro batch em 5 minutos.

Obtenha sua API key e SDK em 30 segundos

Tier grátis: 100 conversões por mês, todos os 6 modelos prebuilt, sem cartão. pip install myocr-client e envie OCR esta tarde.

Obter API key (grátis)