Aller au contenu

CLI Python

La commande nextpdf lance l’extraction de PDF depuis le terminal. Tu la fais pointer vers un endpoint NextPDF Connect, tu lui passes un PDF, puis tu reçois une sortie structurée — texte cité, tableaux, arbre syntaxique abstrait (AST) sémantique complet ou résumé de métadonnées — sur la sortie standard (stdout) ou dans un fichier.

La commande nextpdf est un groupe de commandes Click. Les options de connexion et de session — --base-url, --api-key, --log-level, --output/-o et --strict — sont définies au niveau du groupe : tu les places donc avant la sous-commande. La sous-commande et ses propres options (comme --format ou --page) viennent après :

nextpdf [GROUP OPTIONS] COMMAND [SUBCOMMAND] PDF_PATH [COMMAND OPTIONS]

Placer une option de groupe après la sous-commande échoue. Par exemple, nextpdf info document.pdf --base-url ... renvoie Error: No such option: --base-url et se termine avec le statut 2, car au moment où Click analyse --base-url, il se trouve déjà dans la sous-commande info, qui ne définit pas cette option.

Le moyen le plus clair d’éviter le piège de l’ordre consiste à fournir les identifiants via des variables d’environnement (voir Configurer une fois par shell). Les exemples ci-dessous montrent d’abord la forme avec les options explicites, afin que le bon ordre soit clair.

Extraire le texte en JSON :

Fenêtre de terminal
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" extract text document.pdf

Extraire les tableaux en valeurs séparées par des virgules (CSV) :

Fenêtre de terminal
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" extract tables invoice.pdf --format csv

Inspecter les métadonnées et la structure du document :

Fenêtre de terminal
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" info document.pdf

Obtenir l’AST sémantique complet :

Fenêtre de terminal
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" ast document.pdf

Afficher la version du SDK installé sans contacter de serveur :

Fenêtre de terminal
nextpdf version

La commande version est la seule commande qui n’a besoin ni de --base-url ni de --api-key. Toutes les autres commandes contactent le serveur et exigent les deux.

Chaque exemple lit la clé d’API depuis la variable d’environnement NEXTPDF_API_KEY au lieu de l’inscrire sur la ligne de commande. Traite cette clé comme un secret. Une clé en clair sur la ligne de commande est visible dans l’historique de ton shell et dans la liste des processus (ps) pour les autres utilisateurs de la machine.

Place-les avant la sous-commande. Chaque option de connexion peut aussi être lue depuis une variable d’environnement ; tu peux donc omettre le flag quand la variable est définie.

OptionVariable d’environnementValeur par défautRôle
--base-urlNEXTPDF_BASE_URL(obligatoire)URL du serveur NextPDF Connect.
--api-keyNEXTPDF_API_KEY(obligatoire)Clé d’API pour l’authentification Bearer.
--log-levelwarningNiveau de verbosité des logs : debug, info, warning ou error. Les logs vont vers la sortie d’erreur standard (stderr).
--output, -ostdoutÉcrit la sortie de la commande dans un fichier au lieu de stdout.
--strictoffRéservé à un usage futur. Le flag est analysé aujourd’hui mais ne change pas le comportement.
--help, -hAffiche l’aide et quitte.

Les valeurs --base-url et --api-key sont obligatoires pour chaque commande sauf version. Si l’une des deux manque — ni flag ni variable d’environnement — la commande affiche une erreur et se termine avec le statut 1.

Extrait les blocs de texte cités. Chaque bloc porte une ancre de citation (identifiant de nœud, index de page, boîte englobante et score de confiance).

nextpdf [GROUP OPTIONS] extract text PDF_PATH [--format FORMAT] [--page N] [--headings-only]
OptionValeursValeur par défautRôle
--formatjson, markdown, plainjsonFormat de sortie.
--pageentiertoutes les pagesExtrait uniquement cet index de page à base 0.
--headings-onlyflagoffExtrait uniquement les nœuds de titre.

PDF_PATH est un chemin de fichier, ou - pour lire les octets du PDF depuis stdin.

Extrait chaque tableau avec ses ancres de citation et sa structure au niveau des cellules.

nextpdf [GROUP OPTIONS] extract tables PDF_PATH [--format FORMAT] [--page-start N] [--page-end N]
OptionValeursValeur par défautRôle
--formatjson, csvjsonFormat de sortie.
--page-startentierpremière pageIndex de la page de début (à base 0).
--page-endentierdernière pageIndex de la page de fin (à base 0).

PDF_PATH est un chemin de fichier, ou - pour lire depuis stdin.

Renvoie l’AST sémantique complet en JSON : un arbre hiérarchique de nœuds (titres, paragraphes, tableaux, listes, figures) avec leurs boîtes englobantes et leur contenu textuel.

nextpdf [GROUP OPTIONS] ast PDF_PATH [--page-start N] [--page-end N] [--token-budget N]
OptionValeursValeur par défautRôle
--page-startentierpremière pageIndex de la page de début (à base 0).
--page-endentierdernière pageIndex de la page de fin (à base 0).
--token-budgetentiersans limiteLimite approximative de tokens pour l’AST renvoyé.

PDF_PATH est un chemin de fichier, ou - pour lire depuis stdin. La commande ast produit un seul arbre de document ; elle ne compare pas deux PDF. Pour comparer des structures, voir Recipe : comparer deux révisions de PDF.

Affiche un résumé JSON compact d’un document : version du schéma, hash de la source, nombre de pages, nombre estimé de tokens, type du nœud racine et nombre d’enfants de la racine.

nextpdf [GROUP OPTIONS] info PDF_PATH

PDF_PATH est un chemin de fichier, ou - pour lire depuis stdin.

Affiche la version du SDK installé (par exemple, nextpdf 1.1.0) et quitte. Cette commande ne contacte aucun serveur et n’a besoin d’aucun identifiant.

nextpdf version

Définis les options de connexion une seule fois sous forme de variables d’environnement et omets les flags répétés. Cette forme évite aussi complètement le piège de l’ordre des options, car les identifiants n’apparaissent jamais sur la ligne de commande.

Fenêtre de terminal
export NEXTPDF_BASE_URL=http://localhost:8080
export NEXTPDF_API_KEY=your-key
nextpdf extract text document.pdf

Sous Windows PowerShell :

Fenêtre de terminal
$env:NEXTPDF_BASE_URL = "http://localhost:8080"
$env:NEXTPDF_API_KEY = "your-key"
nextpdf extract text document.pdf

Préfère charger la clé depuis un coffre à secrets ou un fichier .env que tu gardes hors du système de gestion de versions. Ne colle pas de clé de production dans une session de terminal partagée ni dans un script que tu commites.

Tu choisis le format de sortie par commande avec --format. Les commandes de texte et de tableaux prennent en charge plusieurs formats ; ast et info émettent toujours du JSON.

CommandeFormatsValeur par défaut
extract textjson, markdown, plainjson
extract tablesjson, csvjson
astjsonjson
infojsonjson

Choisis JSON quand un programme en aval a besoin des index de page, des scores de confiance ou des identifiants de nœud. Choisis CSV quand un tableur ou un pipeline tabulaire consomme les tableaux. Choisis le texte plain ou markdown quand une personne, ou un outil en mode texte uniquement, lit le résultat.

La commande d’extraction de texte émet un tableau JSON de blocs cités. Chaque bloc contient un champ text, un objet citation (node_id, page_index, bbox, confidence) et un champ node_type facultatif. Écris la sortie dans un fichier avec --output (ou redirige stdout), puis analyse-la.

Cet exemple shell utilise jq pour ne garder que les titres de la page 0 :

Fenêtre de terminal
nextpdf --output blocks.json extract text report.pdf --format json
jq '[.[] | select(.citation.page_index == 0 and .node_type == "heading") | .text]' blocks.json

Les mêmes données peuvent s’analyser proprement en Python. La CLI écrit un tableau JSON : tu le charges donc avec la bibliothèque standard et tu lis les champs typés :

"""Parse cited text blocks emitted by `nextpdf extract text --format json`."""
import json
from pathlib import Path
def load_headings(blocks_path: Path) -> list[str]:
"""Return the text of every heading block on page 0.
Args:
blocks_path: Path to the JSON file written by `nextpdf extract text`.
Returns:
The text of each heading-type block whose citation is on page 0.
"""
raw = blocks_path.read_text(encoding="utf-8")
blocks: list[dict[str, object]] = json.loads(raw)
headings: list[str] = []
for block in blocks:
citation = block["citation"]
if block.get("node_type") == "heading" and citation["page_index"] == 0:
headings.append(str(block["text"]))
return headings
if __name__ == "__main__":
for heading in load_headings(Path("blocks.json")):
print(heading)

Quand tu as besoin de modèles validés et typés plutôt que de dictionnaires bruts, appelle directement le SDK au lieu d’analyser la sortie de la CLI. Consulte la vue d’ensemble Python pour le client NextPDF et son type de retour CitedTextBlock.

Avec --format csv, la commande de tableaux écrit un bloc CSV par tableau. Une ligne de commentaire, # Table N (pM), précède chaque tableau et indique son numéro de tableau à base 1 et son index de page à base 0. Une ligne vide sépare les tableaux consécutifs. La CLI met les valeurs des cellules entre guillemets et les échappe avec le module csv de Python ; les valeurs qui contiennent des virgules, des guillemets ou des sauts de ligne sont donc préservées intactes à l’aller-retour.

Fenêtre de terminal
nextpdf --output tables.csv extract tables statement.pdf --format csv

Comme le fichier contient plusieurs blocs CSV, découpe-le sur les lignes de commentaire avant d’analyser chaque bloc comme un tableau autonome :

"""Split multi-table CSV output from `nextpdf extract tables --format csv`."""
import csv
import io
from pathlib import Path
def read_tables(csv_path: Path) -> list[list[list[str]]]:
"""Parse the multi-block CSV file into a list of tables.
Each table is a list of rows; each row is a list of cell strings.
The leading `# Table N (pM)` comment row is dropped from every table.
Args:
csv_path: Path to the file written by `nextpdf extract tables`.
Returns:
One parsed table per `# Table` block in the file.
"""
text = csv_path.read_text(encoding="utf-8")
tables: list[list[list[str]]] = []
current: list[str] = []
for line in text.splitlines(keepends=True):
if line.startswith("# Table ") and current:
tables.append(_parse_block(current))
current = []
current.append(line)
if current:
tables.append(_parse_block(current))
return tables
def _parse_block(lines: list[str]) -> list[list[str]]:
"""Parse one CSV block, discarding its leading comment row."""
reader = csv.reader(io.StringIO("".join(lines)))
rows = [row for row in reader if row]
return rows[1:] if rows and rows[0] and rows[0][0].startswith("# Table ") else rows
if __name__ == "__main__":
for index, table in enumerate(read_tables(Path("tables.csv")), start=1):
print(f"table {index}: {len(table)} rows")

La CLI utilise trois codes de sortie. Vérifie $? dans les scripts shell (ou $LASTEXITCODE sous PowerShell) pour distinguer succès et échec, et lis les messages de diagnostic depuis stderr, qui reste séparé des données sur stdout.

Code de sortieSignificationExemples
0Succès.Une commande s’est terminée ; version a affiché sa sortie.
1Erreur d’exécution. La CLI affiche Error: <message> sur stderr.Fichier d’entrée introuvable ou qui n’est pas un fichier régulier, stdin vide, --base-url/--api-key manquant ou invalide, toute erreur côté serveur (licence requise, quota dépassé, PDF non balisé, délai de build dépassé ou autre échec de l’API).
2Erreur d’utilisation, signalée par Click.Commande ou option inconnue (y compris une option de groupe placée après la sous-commande), un argument obligatoire manquant tel que PDF_PATH.

Chaque échec côté serveur se manifeste par le code de sortie 1 avec un message lisible par un humain sur stderr. Le SDK lève une exception typée — NextPDFLicenseError (HTTP 402), QuotaExceededError (HTTP 429), AstNoStructTreeError (HTTP 422, PDF non balisé), AstBuildTimeoutError (HTTP 504) ou l’exception de base NextPDFAPIError. La CLI les intercepte toutes via leur base partagée NextPDFError, affiche le message et se termine avec le code 1. La CLI n’expose pas de codes de sortie distincts par type d’échec. Pour distinguer, par exemple, une erreur de quota d’une erreur de licence dans un script, inspecte le texte du message sur stderr ou appelle directement le SDK (voir la vue d’ensemble Python pour les classes d’exception).

Un modèle de script qui sépare les données des diagnostics :

#!/usr/bin/env bash
set -euo pipefail
# Credentials come from the environment, not the command line.
: "${NEXTPDF_BASE_URL:?set NEXTPDF_BASE_URL}"
: "${NEXTPDF_API_KEY:?set NEXTPDF_API_KEY}"
if nextpdf --output contract.ast.json ast contract.pdf; then
echo "AST written to contract.ast.json"
else
status=$?
echo "nextpdf failed with exit code ${status}" >&2
exit "${status}"
fi

Note que --output écrit les données dans le fichier nommé et n’affiche que la ligne de confirmation Written to <path> sur stderr ; stdout reste donc vide. Sans --output, les données vont vers stdout et tu peux les rediriger.

Les exemples ci-dessous utilisent uniquement des commandes et des flags vérifiés. Les identifiants viennent de l’environnement dans chaque cas.

Recipe : extraire les tableaux de factures en CSV

Section intitulée « Recipe : extraire les tableaux de factures en CSV »

Transforme un dossier de factures en un fichier CSV par document pour un tableur ou un pipeline comptable :

#!/usr/bin/env bash
set -euo pipefail
: "${NEXTPDF_BASE_URL:?set NEXTPDF_BASE_URL}"
: "${NEXTPDF_API_KEY:?set NEXTPDF_API_KEY}"
mkdir -p out
for pdf in invoices/*.pdf; do
name="$(basename "${pdf}" .pdf)"
nextpdf --output "out/${name}.csv" extract tables "${pdf}" --format csv
done

Chaque out/<name>.csv contient un bloc CSV par tableau détecté, avec un en-tête # Table N (pM) avant chacun. Analyse les blocs avec le lecteur CSV présenté ci-dessus.

Combine --headings-only avec le format markdown pour produire rapidement un plan que tu peux lire ou coller dans des notes :

Fenêtre de terminal
nextpdf --output outline.md extract text whitepaper.pdf --headings-only --format markdown

La commande ast de la CLI renvoie l’arbre d’un seul document ; elle n’a pas de sous-commande de comparaison. La comparaison structurelle est disponible dans le SDK via client.ast.get_ast_diff(...) et dans le serveur Model Context Protocol (MCP) via l’outil nextpdf_diff. Lance la comparaison via le SDK :

"""Compare two PDF revisions structurally with the NextPDF SDK.
The API key is read from the environment, never hard-coded.
"""
import os
from pathlib import Path
from nextpdf import NextPDF
def diff_revisions(original: Path, modified: Path) -> None:
"""Print a structural diff summary between two PDF revisions.
Args:
original: Path to the earlier PDF revision.
modified: Path to the later PDF revision.
"""
base_url = os.environ["NEXTPDF_BASE_URL"]
api_key = os.environ["NEXTPDF_API_KEY"]
client = NextPDF(base_url=base_url, api_key=api_key)
result = client.ast.get_ast_diff(
original.read_bytes(),
modified.read_bytes(),
)
summary = result.summary
print(f"added: {summary.added_node_count}")
print(f"removed: {summary.removed_node_count}")
print(f"changed: {summary.changed_node_count}")
for entry in result.diff:
preview = entry.text_preview or ""
print(f" {entry.type:<8} {entry.node_type:<12} p{entry.page_index} {preview}")
if __name__ == "__main__":
diff_revisions(Path("contract-v1.pdf"), Path("contract-v2.pdf"))

Pour lancer la même comparaison depuis un agent IA plutôt qu’un script, enregistre le serveur MCP et appelle l’outil nextpdf_diff. Consulte la page serveur MCP Python.

Recipe : envoyer un PDF en flux depuis un autre outil

Section intitulée « Recipe : envoyer un PDF en flux depuis un autre outil »

Lis les octets du PDF depuis stdin avec - pour chaîner nextpdf après un outil qui émet un PDF sur sa propre sortie stdout :

Fenêtre de terminal
curl --silent https://example.com/report.pdf | nextpdf info -

L’argument - indique à la commande de lire le document depuis stdin. Si aucun octet n’arrive, la commande signale une erreur et se termine avec le code 1.

La CLI construit chaque réponse en mémoire et l’écrit en une seule fois : rediriger ou envoyer la sortie dans un pipe est donc simple, mais la sortie n’est pas produite de façon incrémentale. Un AST volumineux ou un grand ensemble de tableaux est entièrement mis en mémoire tampon avant que le premier octet n’atteigne stdout ou le fichier --output. Prévois la mémoire et la latence pour des réponses portant sur le document entier, pas pour un flux.

Chaque invocation de nextpdf crée un nouveau client et une nouvelle connexion HTTP ; une boucle sur plusieurs fichiers ouvre et ferme donc une connexion par fichier. Le coût de connexion est généralement faible par rapport au temps d’extraction côté serveur, mais il devient réel à grande échelle.

  • Réutilise un seul endpoint. Pointe chaque invocation vers le même déploiement NextPDF Connect pour que le serveur réutilise les caches déjà chauds et les pools de connexions. Évite de répartir un lot sur plusieurs endpoints sauf si tu fais volontairement de l’équilibrage de charge.
  • Borne le travail par appel. Utilise --page, --page-start/--page-end ou --token-budget pour ne traiter que les pages dont tu as besoin. Des plages de pages plus petites réduisent à la fois le temps serveur et la taille de la réponse ; --token-budget plafonne l’AST que ton agent doit lire.
  • Regroupe les gros travaux dans un seul processus. Pour les lots à fort volume, préfère le SDK Python aux appels CLI répétés. Un seul client NextPDF (ou AsyncNextPDF) à longue durée de vie réutilise une connexion HTTP du pool d’un document à l’autre, ce qui supprime le coût de démarrage du processus et de mise en place de la connexion qu’une boucle CLI paie à chaque fois. La vue d’ensemble Python montre le cycle de vie du client, et AsyncNextPDF prend en charge l’extraction concurrente sur plusieurs PDF.
  • Garde les logs hors du chemin des données. Laisse --log-level à sa valeur par défaut pour les exécutions par lots. Les logs de diagnostic vont vers stderr et ne corrompent pas les données de stdout, mais passer au niveau debug ajoute du bruit et un léger surcoût.