Panoramica dell’SDK Python
Panoramica dell’SDK Python
Sezione intitolata “Panoramica dell’SDK Python”L’SDK Python di NextPDF è pensato per applicazioni Python che richiedono un’estrazione da PDF con origine tracciabile. Restituisce blocchi strutturati con ancoraggi di citazione, come l’indice di pagina, il livello di confidenza, i riquadri di delimitazione facoltativi e gli identificatori dei nodi semantici quando il PDF di origine espone tale struttura.
Usare l’SDK quando la pipeline deve rispondere a domande come «da quale pagina proviene questo testo?», «quale tabella supporta questo valore?» oppure «che cosa è cambiato tra questi due PDF?» senza ridurre l’estrazione da PDF a semplice testo anonimo.
Che cosa fornisce
Sezione intitolata “Che cosa fornisce”- Un client
NextPDFsincrono per script, processi batch e notebook. - Un client
AsyncNextPDFasincrono perasyncio, FastAPI e altri runtime asincroni. - Un’interfaccia a riga di comando (CLI)
nextpdfper una singola estrazione da un percorso di file o dallo standard input, con output verso lo standard output o un file. - Un server Model Context Protocol (MCP) facoltativo, per consentire agli agenti di intelligenza artificiale (IA) di richiamare direttamente gli strumenti di estrazione da PDF.
- Un backend remoto per l’uso in produzione con NextPDF Connect.
- Un backend locale per l’estrazione offline e solo a livello di libreria tramite
pypdf.
Scelte del backend
Sezione intitolata “Scelte del backend”Il backend remoto invia i byte del PDF a un server NextPDF Connect. È il percorso consigliato per la produzione perché centralizza il comportamento dell’estrazione, l’autenticazione, le quote e i controlli operativi.
Il backend locale viene eseguito nel processo Python e legge i PDF tramite pypdf. È utile per lo sviluppo offline e per i PDF con tag, anche se non può fornire riquadri di delimitazione precisi e si basa su un’estrazione euristica a livello di paragrafo per i PDF senza tag. Il backend locale è disponibile solo a livello di libreria: vi si accede inserendo un LocalBackend in AsyncNextPDF. La CLI nextpdf e il server MCP non possono utilizzarlo. Per il confronto completo, vedere Matrice di scelta del backend.
Limitazioni
Sezione intitolata “Limitazioni”L’SDK non esegue il riconoscimento ottico dei caratteri (OCR). I PDF acquisiti tramite scanner o composti solo da immagini richiedono una fase di OCR prima che NextPDF possa estrarre il testo incorporato. Anche layout complessi, testo sovrapposto e produttori di PDF non comuni possono ridurre la qualità dell’estrazione.
La CLI nextpdf funziona solo in modalità remota e non è un’interfaccia di streaming. Ogni comando legge l’intero PDF in memoria (da un percorso di file o dallo standard input), lo invia a un server NextPDF Connect, costruisce il risultato completo in memoria e quindi lo serializza con una singola scrittura. È possibile reindirizzare tale output verso un file con --output (oppure -o) o verso lo standard output, ma il risultato è interamente memorizzato nel buffer, non generato in modo incrementale. La CLI non può utilizzare il backend locale pypdf.
Scelta di un client: sincrono o asincrono
Sezione intitolata “Scelta di un client: sincrono o asincrono”Entrambi i client condividono lo stesso spazio dei nomi dei metodi ast e restituiscono gli stessi modelli Pydantic. La differenza riguarda il modello di concorrenza.
| Contesto | Da usare | Motivo |
|---|---|---|
| Script e processi batch | NextPDF (sincrono) | Flusso di controllo lineare; nessun ciclo di eventi da gestire. |
| Notebook Jupyter | NextPDF (sincrono) | run_sync rileva il ciclo di eventi in esecuzione e delega la chiamata a un thread di lavoro, in modo che le chiamate bloccanti funzionino all’interno delle celle. |
CLI nextpdf | NextPDF (sincrono, interno) | La CLI crea automaticamente un client sincrono. |
asyncio (servizi) | AsyncNextPDF | Supporto await nativo; nessun passaggio tra thread. |
| FastAPI, Starlette, ASGI | AsyncNextPDF | Condivide il ciclo di eventi della richiesta e lo stesso pool di connessioni. |
| Distribuzione ad alta concorrenza | AsyncNextPDF | Consente molte estrazioni contemporanee con asyncio.gather su un unico client in pool. |
NextPDF incapsula un AsyncNextPDF interno ed esegue ogni chiamata tramite run_sync. All’interno di un ciclo di eventi in esecuzione (ad esempio un notebook), run_sync delega la coroutine a un thread di lavoro singolo dotato di un proprio ciclo, in modo da evitare l’errore di asyncio.run annidato. In un servizio asyncio o ASGI, richiamare direttamente AsyncNextPDF anziché sostenere il costo di tale passaggio tra thread a ogni chiamata.
Il client asincrono gestisce un httpx.AsyncClient per il pooling delle connessioni; riutilizzare quindi un’unica istanza di AsyncNextPDF e chiuderla una sola volta. Il client sincrono NextPDF non espone un metodo close(). Per carichi di lavoro asincroni di lunga durata, è preferibile usare AsyncNextPDF e gestirne il ciclo di vita in modo esplicito (vedere Modello operativo di produzione).
Matrice di scelta del backend
Sezione intitolata “Matrice di scelta del backend”Un backend implementa il protocollo PdfBackend. Il backend remoto (RemoteBackend) viene selezionato automaticamente quando vengono passati base_url e api_key. Il backend locale (LocalBackend) deve essere inserito esplicitamente tramite il parametro backend= di AsyncNextPDF; non è esportato dal pacchetto nextpdf di livello superiore e non è raggiungibile dalla CLI o dal server MCP.
| Funzionalità | Remoto (RemoteBackend) | Locale (LocalBackend) |
|---|---|---|
| Selezionato da | base_url + api_key | AsyncNextPDF(backend=LocalBackend(...)) |
| Rete | NextPDF Connect tramite HyperText Transfer Protocol Secure (HTTPS) | Nessuna; viene eseguito all’interno del processo |
| Autenticazione, quote, misurazione | Centralizzate sul server | Nessuna |
| Osservabilità e controlli operativi | Lato server | Nessuno |
| Estrazione da PDF con tag (StructTree) | Sì | Sì |
| Estrazione da PDF senza tag | Motore del server | Suddivisione euristica dei paragrafi, confidenza 0.5 |
| Riquadri di delimitazione | Sì (quando il server li fornisce) | No (bbox è None) |
| Estrazione di tabelle dai PDF senza tag | Motore del server | Non restituisce alcuna tabella |
| Raggiungibile dalla CLI / dal server MCP | Sì | No (solo a livello di libreria) |
| Consigliato per | Produzione | Sviluppo offline, test su PDF con tag |
Usare il backend remoto per la produzione: è l’unico percorso che offre autenticazione centralizzata, applicazione delle quote, misurazione e osservabilità. Usare il backend locale per lo sviluppo offline e i test sui PDF con tag, accettando però risultati euristici, l’assenza di riquadri di delimitazione e l’assenza di tabelle nell’input senza tag.
"""Inject the local backend for offline, library-only extraction."""
from nextpdf import AsyncNextPDFfrom nextpdf.backends.local import LocalBackend
async def extract_offline(pdf_bytes: bytes) -> None: """Extract cited text without a NextPDF Connect server.""" async with AsyncNextPDF(backend=LocalBackend()) as client: blocks = await client.ast.extract_cited_text(pdf_bytes) for block in blocks: # Heuristic blocks on untagged PDFs report confidence 0.5. print(block.citation.confidence, block.text)Modello operativo di produzione
Sezione intitolata “Modello operativo di produzione”In produzione, il backend remoto viene eseguito su NextPDF Connect. I pattern riportati di seguito riguardano il riutilizzo del client, la gestione degli errori, i tentativi ripetuti, la gestione delle quote e i timeout. Ogni simbolo qui utilizzato esiste nell’SDK; l’SDK non esegue tentativi ripetuti automaticamente, pertanto il ciclo di ripetizione resta sotto la responsabilità dell’applicazione.
Riutilizzare il client e mettere in pool le connessioni
Sezione intitolata “Riutilizzare il client e mettere in pool le connessioni”RemoteBackend mantiene un unico httpx.AsyncClient persistente per il pooling delle connessioni. Creare AsyncNextPDF una sola volta, condividerlo tra le richieste e chiuderlo all’arresto. Non creare un client per ogni richiesta.
"""Reuse one pooled async client for the lifetime of the process."""
import asyncioimport osfrom pathlib import Path
from nextpdf import AsyncNextPDF
async def main() -> None: """Run several extractions over a single pooled client.""" base_url = os.environ["NEXTPDF_BASE_URL"] # Treat the API key as a secret; read it from the environment, never hard-code it. api_key = os.environ["NEXTPDF_API_KEY"]
async with AsyncNextPDF(base_url=base_url, api_key=api_key) as client: pdf_paths = (Path("a.pdf"), Path("b.pdf"), Path("c.pdf")) tasks = [ client.ast.get_document_ast(path.read_bytes()) for path in pdf_paths ] documents = await asyncio.gather(*tasks) for document in documents: print(document.page_count, document.estimated_tokens)
if __name__ == "__main__": asyncio.run(main())All’uscita, il gestore di contesto asincrono richiama close(), che chiude il trasporto sottostante. Senza un gestore di contesto, richiamare manualmente await client.close().
Gestire gli errori con la gerarchia delle eccezioni
Sezione intitolata “Gestire gli errori con la gerarchia delle eccezioni”L’SDK espone una gerarchia tipizzata di eccezioni. Tutti gli errori derivano da NextPDFError; gli errori a livello HTTP derivano da NextPDFAPIError e includono uno status_code. Intercettare i tipi specifici su cui è possibile intervenire e usare il tipo di base come fallback.
| Eccezione | Generata quando | Attributi principali |
|---|---|---|
NextPDFError | Tipo di base per ogni errore dell’SDK | status_code |
NextPDFAPIError | Qualsiasi errore HTTP proveniente dal server | status_code, error_code |
NextPDFLicenseError | HTTP 402; la funzionalità richiede un livello server superiore | status_code (402) |
QuotaExceededError | HTTP 429; limite di frequenza o quota superati | retry_after |
AstNoStructTreeError | HTTP 422; PDF senza tag con la modalità euristica disattivata | status_code (422) |
AstBuildTimeoutError | HTTP 504; timeout della creazione dell’AST | status_code (504) |
"""Map SDK exceptions to caller-facing outcomes."""
from nextpdf import ( AstBuildTimeoutError, AstNoStructTreeError, AsyncNextPDF, NextPDFAPIError, NextPDFError, NextPDFLicenseError, QuotaExceededError,)
async def safe_extract(client: AsyncNextPDF, pdf_bytes: bytes) -> str: """Extract text, translating known failures into a stable status string.""" try: blocks = await client.ast.extract_cited_text(pdf_bytes) except QuotaExceededError as exc: # exc.retry_after holds the server Retry-After value in seconds, or None. return f"rate-limited; retry after {exc.retry_after}s" except NextPDFLicenseError: return "feature requires a higher server tier" except AstNoStructTreeError: return "untagged PDF; enable heuristic mode or use a tagged PDF" except AstBuildTimeoutError: return "build timed out; reduce the page range" except NextPDFAPIError as exc: return f"server error (status {exc.status_code})" except NextPDFError: return "extraction failed" return "\n".join(block.text for block in blocks)Ritentare gli errori temporanei con backoff
Sezione intitolata “Ritentare gli errori temporanei con backoff”L’SDK non esegue tentativi ripetuti automaticamente. Racchiudere le chiamate in un ciclo proprio che ritenti in caso di errori HTTP temporanei e rispetti il valore Retry-After del server, che QuotaExceededError espone come retry_after (un numero intero di secondi oppure None). Usare un backoff esponenziale per gli altri stati temporanei e non ritentare NextPDFLicenseError.
"""Retry transient failures with exponential backoff and Retry-After support."""
import asynciofrom collections.abc import Awaitable, Callablefrom typing import TypeVar
from nextpdf import NextPDFAPIError, QuotaExceededError
_RETRYABLE_STATUS = frozenset({500, 502, 503, 504})
_T = TypeVar("_T")
async def with_retry( coro_factory: Callable[[], Awaitable[_T]], *, max_attempts: int = 4,) -> _T: """Call coro_factory() with bounded retries on transient server errors.
Args: coro_factory: A zero-argument callable returning a fresh awaitable. max_attempts: Maximum number of attempts before giving up.
Returns: The awaited result of the first successful attempt.
Raises: NextPDFAPIError: When all attempts fail or the error is not retryable. """ delay = 1.0 for attempt in range(1, max_attempts + 1): try: return await coro_factory() except QuotaExceededError as exc: if attempt == max_attempts: raise await asyncio.sleep(exc.retry_after if exc.retry_after is not None else delay) delay *= 2.0 except NextPDFAPIError as exc: if attempt == max_attempts or exc.status_code not in _RETRYABLE_STATUS: raise await asyncio.sleep(delay) delay *= 2.0 raise RuntimeError("unreachable")Gestire quote, limiti di frequenza e timeout
Sezione intitolata “Gestire quote, limiti di frequenza e timeout”L’applicazione delle quote e dei limiti di frequenza risiede sul server. In caso di HTTP 429 l’SDK genera QuotaExceededError e analizza l’intestazione Retry-After in retry_after. Il backend remoto espone inoltre le intestazioni X-RateLimit-* nelle risposte di rendering, in modo da poter limitare la frequenza in modo proattivo prima di raggiungere un limite rigido.
Per i timeout delle richieste viene usato un valore predefinito fisso di 60 secondi complessivi, con un timeout di connessione di 10 secondi (httpx.Timeout(60.0, connect=10.0)). Per limitare le creazioni di AST lunghe, è preferibile restringere il lavoro con page_range_start, page_range_end o token_budget anziché affidarsi al solo timeout; una creazione eccessivamente lunga restituisce AstBuildTimeoutError (HTTP 504).
Esempi di architetture
Sezione intitolata “Esempi di architetture”Processo batch
Sezione intitolata “Processo batch”Un worker batch legge i PDF, estrae il testo citato e scrive un output strutturato. Riutilizzare un unico client in pool, limitare la concorrenza con un semaforo e applicare l’helper di ripetizione descritto sopra.
"""Batch-extract a directory of PDFs over one pooled async client."""
import asyncioimport osfrom pathlib import Path
from nextpdf import AsyncNextPDF
async def run_batch(input_dir: Path, concurrency: int = 8) -> None: """Extract cited text for every PDF in input_dir, bounded by concurrency.""" semaphore = asyncio.Semaphore(concurrency)
async def worker(client: AsyncNextPDF, path: Path) -> None: async with semaphore: blocks = await client.ast.extract_cited_text(path.read_bytes()) out = path.with_suffix(".txt") out.write_text("\n".join(b.text for b in blocks), encoding="utf-8")
async with AsyncNextPDF( base_url=os.environ["NEXTPDF_BASE_URL"], api_key=os.environ["NEXTPDF_API_KEY"], ) as client: await asyncio.gather(*(worker(client, p) for p in input_dir.glob("*.pdf")))Servizio web
Sezione intitolata “Servizio web”Un servizio FastAPI condivide un unico AsyncNextPDF tra le richieste per l’intera durata dell’applicazione, in modo che ogni richiesta riutilizzi il pool di connessioni. Leggere le credenziali dall’ambiente e trattare la chiave API come un segreto.
"""FastAPI service that shares one pooled NextPDF client across requests."""
import osfrom contextlib import asynccontextmanager
from fastapi import FastAPI, UploadFile
from nextpdf import AsyncNextPDF
@asynccontextmanagerasync def lifespan(app: FastAPI): """Create the pooled client on startup and close it on shutdown.""" app.state.client = AsyncNextPDF( base_url=os.environ["NEXTPDF_BASE_URL"], api_key=os.environ["NEXTPDF_API_KEY"], ) try: yield finally: await app.state.client.close()
app = FastAPI(lifespan=lifespan)
@app.post("/extract")async def extract(file: UploadFile) -> dict[str, list[str]]: """Return cited text blocks for an uploaded PDF.""" pdf_bytes = await file.read() blocks = await app.state.client.ast.extract_cited_text(pdf_bytes) return {"text": [block.text for block in blocks]}Strumento per agenti
Sezione intitolata “Strumento per agenti”Per gli agenti IA, eseguire il server MCP. Espone strumenti per i PDF (ad esempio nextpdf_extract_text, nextpdf_extract_tables, nextpdf_get_ast, nextpdf_info, nextpdf_search, nextpdf_get_outline, nextpdf_diff e nextpdf_health) tramite standard input e standard output. Il server legge NEXTPDF_BASE_URL e NEXTPDF_API_KEY dall’ambiente ed è quindi supportato in modalità remota; come la CLI, non può utilizzare il backend locale. Installare il componente facoltativo aggiuntivo ed eseguire il modulo.
pip install "nextpdf[mcp]"python -m nextpdf.mcpVedere Server MCP Python per la procedura dettagliata di integrazione degli agenti, CLI Python per l’uso da terminale e Documentazione di riferimento dell’API Python per la superficie completa di client, modelli ed eccezioni.