Inicio Blog Cómo convertir 100 facturas en 10 minutos con Python (tutorial SDK myocr) 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 100 facturas PDF → 100 archivos Excel estructurados en menos de 10 minutos con 30 líneas de Python 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: Lee una carpeta de facturas PDF (mezcla de digitales y escaneadas) Las envía como jobs asíncronos vía /v1/batch Hace polling o recibe notificaciones webhook cuando cada job termina Descarga archivos Excel estructurados (Proveedor, Cliente, Líneas, Total, IVA) Se recupera de cuota agotada, fallos de red y errores OCR por archivo sin crashear Total: ~30 líneas de código de negocio, más un receptor de webhook si quieres entrega push. Requisitos previos Python 3.8+ Una API key de myocr.app/account/api (tier gratis: 100 llamadas/mes, sin tarjeta) Una carpeta de facturas PDF de prueba — o usa los fixtures de ejemplo en el repo del SDK 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: invoice — facturas de proveedor, líneas, totales, IVA. El que usa este tutorial. receipt — recibos POS, comerciante, items, total. Bueno para informes de gastos. bank_statement — transacciones, fechas, débito/crédito, saldo corriente. business_card — contacto, empresa, teléfonos, emails, direcciones. tables — extracción de tablas genérica para documentos sin modelo especializado. text — texto OCR plano, sin estructura. 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: Usa keys sk_test_* en dev — están rate-limited pero no consumen tu cuota facturable Setea MYOCR_API_KEY vía env var, nunca hardcoded — el SDK la lee automáticamente Configura tu secret manager para el webhook signing secret en ambos lados (servidor myocr y tu receptor) Logea request_id en cada llamada para correlacionar con logs del servidor si abres un ticket de soporte Setea un timeout razonable en Job.wait() — el default 600s va bien para facturas, súbelo para extractos bancarios con 50+ páginas Monitorea QuotaExceeded en tu error tracker — es la señal para hacer upgrade del plan antes que te muerda 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)