ENTRAR  login to app
2026-05-26 11 min lectura

Cómo convertir 100 facturas en 10 minutos con Python (tutorial SDK myocr)

De cero a un pipeline batch funcional con el SDK oficial myocr-client. Código real, manejo de errores real, entrega vía webhook.

Por equipo myocr.app

Por qué importa un SDK Python dedicado

Si vas a desplegar OCR en producción en Python tienes tres opciones. Puedes llamar a la API con requests crudo y escribir tú mismo todo el boilerplate — autenticación, reintentos, mapeo de errores, polling para jobs asíncronos, descargas de signed URL. Puedes usar un generador OpenAPI que produce un cliente funcional pero poco ergonómico. O puedes instalar un SDK mantenido que abstrae el protocolo y te deja escribir solo código de negocio.

Este tutorial usa la tercera vía: myocr-client, el SDK oficial de Python para myocr.app. En 11 minutos pasarás de pip install a un script que convierte 100 facturas PDF en paralelo, maneja con elegancia cuota agotada y errores OCR, y notifica a tu app vía webhook cuando cada job termina.

Lo que vas a construir

Un único script Python que:

Total: ~30 líneas de código de negocio, más un receptor de webhook si quieres entrega push.

Requisitos previos

Paso 1: instalar

pip install myocr-client

Eso es todo. El SDK depende solo de requests. Sin extensiones nativas, sin runtime específico del SDK.

Paso 2: convierte tu primera factura (sync)

from myocr_client import MyOCRClient

client = MyOCRClient(api_key="sk_live_...")  # o setea MYOCR_API_KEY env var

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

Esa es una llamada OCR completa. El método convert() es síncrono y mejor para archivos bajo 5 MB y 10 páginas. El ConversionResult retornado incluye content (bytes), pages_used, el model original y un request_id que puedes grep en los logs del servidor.

Paso 3: cambia a async para procesamiento batch

Para 100 facturas no quieres 100 llamadas sync secuenciales — es latencia en serie. Usa /v1/batch. El SDK lo expone como client.batch():

from pathlib import Path
from myocr_client import MyOCRClient

client = MyOCRClient()  # MYOCR_API_KEY desde env

folder = Path("./facturas")
pdfs = sorted(folder.glob("*.pdf"))[:20]  # /v1/batch acepta hasta 20 archivos

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

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"Falló: {job.request_id} — {job.error_detail}")

Tres cosas a notar. Primero, el endpoint /v1/batch acepta como máximo 20 archivos por llamada — para 100 facturas, divide la carpeta en 5 batches. Segundo, wait_all() hace polling de cada job individualmente con backoff exponencial (1s → 2s → 4s → 8s → 15s tope), así 100 llamadas sleep(1) no queman tu rate limit. Tercero, si un único archivo falla la extracción OCR, solo ese Job termina en failed — los otros completan normalmente.

Paso 4: procesa 100 facturas en 5 chunks

from pathlib import Path
from myocr_client import MyOCRClient

client = MyOCRClient()
folder = Path("./facturas")
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)} archivos")
    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("\nHecho.")

Tiempo total en una key del tier gratuito con concurrencia por defecto: aproximadamente 8-10 minutos para 100 facturas, dominado por la latencia del procesamiento OCR, no por tu código. En planes pagos con mayor concurrencia, el mismo workload termina en 3-5 minutos.

Paso 5: manejo de errores como un adulto

El SDK mapea cada código de error de la API a una excepción tipada. Codifica de forma defensiva:

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

client = MyOCRClient()

try:
    result = client.convert("factura.pdf", model="invoice")
except QuotaExceeded as e:
    print(f"Plan {e.current_plan}: {e.calls_used}/{e.calls_limit} llamadas usadas")
    print(f"Upgrade en {e.upgrade_url}, resetea {e.reset_date}")
except FileTooLarge:
    print("Usa create_job() en vez de convert() — cambia al path async")
except OcrEngineError:
    pass  # upstream OCR falló, seguro reintentar tras 30s
except RateLimited:
    pass  # raro: el SDK reintenta 429 automáticamente hasta 3 veces
except InvalidApiKey:
    raise SystemExit("Rota tu API key — la actual no es válida")

El reintento automático en respuestas 429 y 5xx (respetando Retry-After) es la victoria ergonómica más común comparado con escribir código requests crudo. Te ahorra el típico incidente en producción "olvidé manejar una red inestable".

Paso 6: cambia a webhooks (cero polling)

El polling funciona pero es ruidoso. Si tienes un endpoint HTTP exponible públicamente, la entrega vía webhook es más limpia. Pasa webhook_url al crear jobs:

batch = client.batch(
    pdfs,
    model="invoice",
    webhook_url="https://miapp.com/webhooks/myocr",
)
# Sin esperar — tu endpoint recibirá un POST por cada job

En el lado receptor (ejemplo Flask):

from flask import Flask, request
from myocr_client import verify_webhook_signature

app = Flask(__name__)
SECRET = "tu-secret-compartido"

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

    event = request.get_json()
    request_id = event["data"]["request_id"]
    if event["event"] == "job.completed":
        ...  # descarga result_url, actualiza DB, notifica usuario
    elif event["event"] == "job.failed":
        ...  # logea error_detail, reintenta o alerta ops
    return "", 200

Dos cosas no obvias: (1) verifica la firma sobre bytes crudos — si serializas el JSON parseado de vuelta, la firma no coincidirá por whitespace y orden de claves; (2) los webhooks tienen política de reintento automática del lado servidor (1m → 5m → 30m → 2h), así que no te preocupes si tu endpoint está caído brevemente.

Paso 7: elige el modelo correcto

El SDK expone los mismos seis modelos prebuilt que la API REST. Elige el más específico para tu tipo de documento:

Usar un modelo especializado es la diferencia entre obtener una tabla desestructurada que necesita limpieza y obtener Proveedor / Total / Líneas ya parseadas en columnas Excel tipadas.

Paso 8: checklist de producción

Antes de enviar esto a un workload real:

Dónde ir después

La referencia completa de la API está en Scalar UI con ejemplos copy-paste para cada endpoint. El código fuente del SDK es open source en GitHub — lee client.py si quieres extenderlo, o abre un issue si encuentras una feature faltante.

Si estás integrando en un SaaS que necesita OCR, el SDK es también el path recomendado para nuestras integraciones Zapier, QuickBooks y Xero (todas lo usan internamente).

Resumen en una pantalla

# 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 archivos/llamada, ≤50MB cada uno):
batch = client.batch(
    ["a.pdf", "b.pdf", "c.pdf"],
    model="invoice",
    webhook_url="https://miapp.com/webhooks/myocr",
)
for job in batch.wait_all(timeout=600):
    if job.is_done:
        job.download(f"out/{job.request_id}.xlsx")

Mismo patrón en Node.js / TypeScript

¿Prefieres JavaScript? El SDK Node myocr-client tiene la misma superficie, mismos nombres de método (camelCase), excepciones tipadas, cero dependencias runtime (Node 18+ usa fetch / FormData / crypto nativos).

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

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

// Sync:
const r = await client.convert('factura.pdf', { model: 'invoice' });
await r.save('factura.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`);
}

Misma API por detrás, mismos códigos de error (mapeados a clases TS), misma entrega de webhook y verificación de firma. Elige el lenguaje que encaja en tu stack.

Pruébalo hoy: obtén una API key gratis en myocr.app/account/api (sin tarjeta), pip install myocr-client (o npm install myocr-client), y procesa tu primer batch en 5 minutos.

Obtén tu API key y SDK en 30 segundos

Tier gratis: 100 conversiones al mes, los 6 modelos prebuilt, sin tarjeta. pip install myocr-client y envía OCR esta tarde.

Obtener API key (gratis)