ACCEDI  login to app
2026-05-26 11 min lettura

Come convertire 100 fatture in 10 minuti con Python (tutorial SDK myocr)

Da zero a una pipeline batch funzionante con l'SDK ufficiale myocr-client. Codice reale, gestione errori reale, consegna via webhook.

Di team myocr.app

Perché un SDK Python dedicato fa la differenza

Se devi mettere in produzione OCR in Python hai tre strade. Puoi chiamare l'API con requests grezzi e scrivere tu il boilerplate — autenticazione, retry, mappatura degli errori, polling per job asincroni, download da signed URL. Puoi usare un generatore OpenAPI che produce un client funzionante ma poco ergonomico. Oppure installi un SDK mantenuto che astrae il protocollo e ti lascia scrivere solo codice di business.

Questo tutorial usa la terza via: myocr-client, l'SDK Python ufficiale di myocr.app. In dieci minuti passerai da pip install a uno script che converte 100 fatture PDF in parallelo, gestisce in modo pulito quota esaurita ed errori OCR, e notifica la tua applicazione via webhook al completamento di ogni job.

Cosa costruirai

Un singolo script Python che:

Totale: circa 30 righe di codice di business, più un ricevitore di webhook se vuoi delivery push.

Prerequisiti

Passo 1: installa

pip install myocr-client

Tutto qui. L'SDK dipende solo da requests. Niente estensioni native, niente runtime specifico.

Passo 2: converti la prima fattura (sync)

from myocr_client import MyOCRClient

client = MyOCRClient(api_key="sk_live_...")  # oppure setta MYOCR_API_KEY in env

result = client.convert("fattura.pdf", model="invoice")
result.save("fattura.xlsx")
print(f"{result.pages_used} pagine → fattura.xlsx ({result.request_id})")

Questa è una chiamata OCR completa. Il metodo convert() è sincrono e ideale per file sotto 5 MB e 10 pagine. Il ConversionResult restituito contiene content (bytes), pages_used, il model originale e un request_id con cui correlare i log lato server.

Passo 3: passa ad async per il batch processing

Per 100 fatture non vuoi 100 chiamate sync sequenziali — è latenza seriale. Usa /v1/batch. L'SDK lo espone come client.batch():

from pathlib import Path
from myocr_client import MyOCRClient

client = MyOCRClient()  # MYOCR_API_KEY da env

folder = Path("./fatture")
pdfs = sorted(folder.glob("*.pdf"))[:20]  # /v1/batch accetta max 20 file

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

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

Tre cose da notare. Primo, l'endpoint /v1/batch accetta al massimo 20 file per chiamata — per 100 fatture, dividi la cartella in 5 batch. Secondo, wait_all() fa polling per ogni job con backoff esponenziale (1s → 2s → 4s → 8s → 15s cap), così 100 sleep(1) non saturano il tuo rate limit. Terzo, se un singolo file fallisce l'estrazione OCR, solo quel Job finisce in failed — gli altri completano normalmente.

Passo 4: processa 100 fatture in 5 chunk

from pathlib import Path
from myocr_client import MyOCRClient

client = MyOCRClient()
folder = Path("./fatture")
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)} file")
    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} pagine)")
        else:
            print(f"  ✗ {job.request_id}: {job.error_detail}")

print("\nFatto.")

Tempo totale su una chiave del tier gratuito con concorrenza default: circa 8-10 minuti per 100 fatture, dominato dalla latenza OCR, non dal tuo codice. Sui piani a pagamento con maggior concorrenza, lo stesso carico finisce in 3-5 minuti.

Passo 5: gestione errori da adulto

L'SDK mappa ogni codice di errore dell'API in un'eccezione tipata. Codifica in modo difensivo:

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

client = MyOCRClient()

try:
    result = client.convert("fattura.pdf", model="invoice")
except QuotaExceeded as e:
    print(f"Piano {e.current_plan}: {e.calls_used}/{e.calls_limit} chiamate usate")
    print(f"Upgrade su {e.upgrade_url}, reset {e.reset_date}")
except FileTooLarge:
    print("Usa create_job() invece di convert() — passa al path async")
except OcrEngineError:
    pass  # motore OCR a monte fallito, sicuro ritentare dopo 30s
except RateLimited:
    pass  # raro: l'SDK ritenta 429 in automatico fino a 3 volte
except InvalidApiKey:
    raise SystemExit("Ruota la API key — quella corrente non è valida")

Il retry automatico su 429 e 5xx (rispettando Retry-After) è la vittoria ergonomica più comune rispetto a scrivere codice requests grezzo. Ti risparmia il classico incidente in produzione "ho dimenticato di gestire la rete instabile".

Passo 6: passa ai webhook (zero polling)

Il polling funziona ma è chiacchierone. Se hai un endpoint HTTP esponibile pubblicamente, la delivery via webhook è più pulita. Passa webhook_url alla creazione dei job:

batch = client.batch(
    pdfs,
    model="invoice",
    webhook_url="https://miaapp.com/webhooks/myocr",
)
# Niente attesa — il tuo endpoint riceverà un POST per ogni job

Sul lato ricevente (esempio Flask):

from flask import Flask, request
from myocr_client import verify_webhook_signature

app = Flask(__name__)
SECRET = "il-tuo-secret-condiviso"

@app.route("/webhooks/myocr", methods=["POST"])
def myocr_webhook():
    body = request.get_data()  # IMPORTANTE: byte grezzi, non request.get_json()
    sig = request.headers.get("X-MyOCR-Signature", "")
    if not verify_webhook_signature(body, sig, SECRET):
        return "firma non valida", 401

    event = request.get_json()
    request_id = event["data"]["request_id"]
    if event["event"] == "job.completed":
        ...  # scarica result_url, aggiorna DB, notifica utente
    elif event["event"] == "job.failed":
        ...  # logga error_detail, retry o alerta ops
    return "", 200

Due cose non ovvie: (1) verifica la firma sui byte grezzi — se ri-serializzi il JSON parsato, la firma non corrisponderà per via di whitespace e ordine delle chiavi; (2) i webhook hanno retry policy automatica lato server (1m → 5m → 30m → 2h), quindi non preoccuparti se il tuo endpoint è giù brevemente.

Passo 7: scegli il modello giusto

L'SDK espone gli stessi sei modelli prebuilt dell'API REST. Scegli il più specifico per il tipo di documento:

Usare un modello specializzato è la differenza fra ottenere una tabella destrutturata da ripulire e ottenere Fornitore / Totale / Righe articoli già parsati in colonne Excel tipizzate.

Passo 8: checklist produzione

Prima di portare questo in un carico reale:

Dove andare ora

La reference completa dell'API è in Scalar UI con esempi copy-paste per ogni endpoint. Il codice sorgente dell'SDK è open source su GitHub — leggi client.py se vuoi estenderlo, oppure apri una issue se trovi una feature mancante.

Se stai integrando in un SaaS che ha bisogno di OCR, l'SDK è anche il path raccomandato per le nostre integrazioni Zapier, QuickBooks e Xero (tutte lo usano internamente).

Riepilogo in una schermata

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

client = MyOCRClient()  # MYOCR_API_KEY env var

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

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

Stesso pattern in Node.js / TypeScript

Preferisci JavaScript? L'SDK Node myocr-client ha la stessa superficie, stessi nomi metodi (camelCase), eccezioni tipate, zero dipendenze runtime (Node 18+ usa fetch / FormData / crypto nativi).

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

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

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

Stessa API dietro le quinte, stessi codici errore (mappati a classi TS), stessa delivery webhook e verifica firma. Scegli il linguaggio del tuo stack.

Provalo oggi: ottieni una API key gratuita su myocr.app/account/api (senza carta), pip install myocr-client (o npm install myocr-client) e processa il tuo primo batch in 5 minuti.

Ottieni API key e SDK in 30 secondi

Tier gratuito: 100 conversioni al mese, tutti e 6 i modelli prebuilt, senza carta. pip install myocr-client e metti OCR in produzione oggi pomeriggio.

Ottieni API key (gratis)