Ir al contenido

CLI de Python

El comando nextpdf ejecuta la extracción de PDF desde la terminal. Se le indica un endpoint de NextPDF Connect, se le pasa un PDF y se obtiene una salida estructurada (texto con citas, tablas, el árbol de sintaxis abstracta (AST) semántico completo o un resumen de metadatos) en la salida estándar (stdout) o en un archivo.

El comando nextpdf es un grupo de comandos de Click. Las opciones de conexión y de sesión (--base-url, --api-key, --log-level, --output/-o y --strict) se definen en el grupo, por lo que deben colocarse antes del subcomando. El subcomando y sus propias opciones (como --format o --page) van después:

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

Colocar una opción del grupo después del subcomando falla. Por ejemplo, nextpdf info document.pdf --base-url ... informa Error: No such option: --base-url y termina con el estado 2, porque cuando Click analiza --base-url ya está dentro del subcomando info, que no define esa opción.

La forma más clara de evitar el problema del orden es proporcionar las credenciales mediante variables de entorno (consulta Configurar una vez por shell). Los ejemplos siguientes muestran primero el uso explícito de opciones para que el orden correcto quede claro.

Extraer texto como JSON:

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

Extraer tablas como valores separados por comas (CSV):

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

Inspeccionar los metadatos y la estructura del documento:

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

Obtener el AST semántico completo:

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

Imprimir la versión del SDK instalada sin contactar con un servidor:

Ventana de terminal
nextpdf version

El comando version es el único comando que no necesita ni --base-url ni --api-key. Cualquier otro comando se comunica con el servidor y requiere ambos.

En cada ejemplo, la clave de API se lee desde la variable de entorno NEXTPDF_API_KEY en lugar de incrustarse en la línea de comandos. La clave debe tratarse como un secreto. Una clave literal en la línea de comandos queda visible en el historial del shell y en la lista de procesos (ps) para otros usuarios del host.

Deben colocarse antes del subcomando. Cada opción de conexión también lee de una variable de entorno, por lo que el indicador puede omitirse cuando la variable está definida.

OpciónVariable de entornoPredeterminadoPropósito
--base-urlNEXTPDF_BASE_URL(obligatorio)URL del servidor NextPDF Connect.
--api-keyNEXTPDF_API_KEY(obligatorio)Clave de API para la autenticación bearer.
--log-levelwarningVerbosidad del registro: debug, info, warning o error. Los registros van a la salida de error estándar (stderr).
--output, -ostdoutEscribe la salida del comando en un archivo en lugar de en stdout.
--strictdesactivadoReservado para uso futuro. Actualmente el indicador se analiza, pero no cambia el comportamiento.
--help, -hMuestra la ayuda y termina.

Los valores de --base-url y --api-key son obligatorios para todos los comandos excepto version. Si falta alguno de los dos (sin indicador y sin variable de entorno), el comando imprime un error y termina con estado 1.

Extrae bloques de texto con citas. Cada bloque incluye un ancla de cita (identificador de nodo, índice de página, cuadro delimitador y puntuación de confianza).

nextpdf [GROUP OPTIONS] extract text PDF_PATH [--format FORMAT] [--page N] [--headings-only]
OpciónValoresPredeterminadoPropósito
--formatjson, markdown, plainjsonFormato de salida.
--pageenterotodas las páginasExtrae solo este índice de página con base 0.
--headings-onlyindicadordesactivadoExtrae solo los nodos de encabezado.

PDF_PATH es una ruta de archivo o - para leer los bytes del PDF desde stdin.

Extrae todas las tablas con anclas de cita y estructura a nivel de celdas.

nextpdf [GROUP OPTIONS] extract tables PDF_PATH [--format FORMAT] [--page-start N] [--page-end N]
OpciónValoresPredeterminadoPropósito
--formatjson, csvjsonFormato de salida.
--page-startenteroprimera páginaÍndice de la página inicial (con base 0).
--page-endenteroúltima páginaÍndice de la página final (con base 0).

PDF_PATH es una ruta de archivo o - para leer desde stdin.

Devuelve el AST semántico completo como JSON: un árbol jerárquico de nodos (encabezados, párrafos, tablas, listas, figuras) con cuadros delimitadores y contenido de texto.

nextpdf [GROUP OPTIONS] ast PDF_PATH [--page-start N] [--page-end N] [--token-budget N]
OpciónValoresPredeterminadoPropósito
--page-startenteroprimera páginaÍndice de la página inicial (con base 0).
--page-endenteroúltima páginaÍndice de la página final (con base 0).
--token-budgetenterosin límiteLímite aproximado de tokens para el AST devuelto.

PDF_PATH es una ruta de archivo o - para leer desde stdin. El comando ast produce un único árbol del documento; no compara dos PDF. Para la comparación estructural, consulta Recipe: comparar dos revisiones de PDF.

Imprime un resumen JSON compacto de un documento: la versión del esquema, el hash de origen, el número de páginas, el número estimado de tokens, el tipo del nodo raíz y el número de hijos de la raíz.

nextpdf [GROUP OPTIONS] info PDF_PATH

PDF_PATH es una ruta de archivo o - para leer desde stdin.

Imprime la versión del SDK instalada (por ejemplo, nextpdf 1.1.0) y termina. Este comando no se comunica con ningún servidor y no necesita credenciales.

nextpdf version

Definir las opciones de conexión una sola vez como variables de entorno permite omitir los indicadores repetidos. Esta forma también evita por completo el problema del orden de las opciones, porque las credenciales nunca aparecen en la línea de comandos.

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

En Windows PowerShell:

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

Es preferible cargar la clave desde un almacén de secretos o desde un archivo .env que se mantenga fuera del control de versiones. No debe pegarse una clave de producción en una sesión de terminal compartida ni en un script que se confirme en el repositorio.

El formato de salida se selecciona por comando con --format. Los comandos de texto y de tablas admiten más de un formato; ast e info siempre emiten JSON.

ComandoFormatosPredeterminado
extract textjson, markdown, plainjson
extract tablesjson, csvjson
astjsonjson
infojsonjson

Conviene elegir JSON cuando un programa posterior necesite índices de página, puntuaciones de confianza o identificadores de nodo. Conviene elegir CSV cuando una hoja de cálculo o una canalización tabular consuma las tablas. Conviene elegir texto plain o markdown cuando vaya a leerlo una persona o una herramienta de solo texto.

El comando de texto emite un arreglo JSON de bloques con citas. Cada bloque tiene text, un objeto citation (node_id, page_index, bbox, confidence) y un node_type opcional. Enviar la salida a un archivo con --output (o redirigir stdout) permite analizarla después.

Este ejemplo de shell usa jq para conservar solo los encabezados de la página 0:

Ventana 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

Los mismos datos pueden analizarse sin problemas en Python. La CLI escribe un arreglo JSON, por lo que se carga con la biblioteca estándar y se leen los campos tipados:

"""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)

Cuando se necesitan modelos validados y tipados en lugar de diccionarios sin procesar, conviene llamar al SDK directamente en vez de analizar la salida de la CLI. Consulta la introducción a Python para conocer el cliente NextPDF y su tipo de retorno CitedTextBlock.

Con --format csv, el comando de tablas escribe un bloque CSV por tabla. Una fila de comentario, # Table N (pM), precede a cada tabla e indica su número de tabla con base 1 y su índice de página con base 0. Una línea en blanco separa las tablas consecutivas. La CLI entrecomilla y escapa los valores de las celdas con el módulo csv de Python, por lo que los valores que contienen comas, comillas o saltos de línea se conservan de forma segura al leerlos y escribirlos.

Ventana de terminal
nextpdf --output tables.csv extract tables statement.pdf --format csv

Como el archivo contiene varios bloques CSV, debe dividirse por las filas de comentario antes de analizar cada bloque como una tabla independiente:

"""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 usa tres códigos de salida. En scripts de shell, comprobar $? (o $LASTEXITCODE en PowerShell) permite ramificar según el éxito o el fallo, y leer los mensajes de diagnóstico de stderr, que se mantiene separado de los datos de stdout.

Código de salidaSignificadoEjemplos
0Éxito.Un comando se completó; se imprimió version.
1Error en tiempo de ejecución. La CLI imprime Error: <message> en stderr.Archivo de entrada no encontrado o no es un archivo normal, stdin vacío, --base-url/--api-key ausente o no válido, cualquier error del lado del servidor (licencia requerida, cuota superada, PDF sin etiquetar, tiempo de espera de la compilación agotado u otro fallo de la API).
2Error de uso, informado por Click.Comando u opción desconocidos (incluida una opción del grupo colocada después del subcomando), falta un argumento obligatorio como PDF_PATH.

Cada fallo del lado del servidor se manifiesta como el código de salida 1 con un mensaje legible para personas en stderr. El SDK lanza una excepción tipada: NextPDFLicenseError (HTTP 402), QuotaExceededError (HTTP 429), AstNoStructTreeError (HTTP 422, PDF sin etiquetar), AstBuildTimeoutError (HTTP 504) o la base NextPDFAPIError. La CLI las captura todas a través de su base compartida NextPDFError, imprime el mensaje y termina con 1. La CLI no expone códigos de salida distintos por tipo de fallo. Para distinguir, por ejemplo, un error de cuota de un error de licencia en un script, se puede inspeccionar el texto del mensaje en stderr o llamar al SDK directamente (consulta la introducción a Python para conocer las clases de excepción).

Patrón de scripting para separar los datos de los diagnósticos:

#!/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

Debe tenerse en cuenta que --output escribe los datos en el archivo indicado e imprime solo la línea de confirmación Written to <path> en stderr, por lo que stdout queda vacío. Sin --output, los datos van a stdout y es posible redirigirlos.

Las siguientes recetas usan solo comandos e indicadores verificados. Las credenciales provienen del entorno en cada caso.

Convertir una carpeta de facturas en un archivo CSV por documento para una hoja de cálculo o una canalización de contabilidad:

#!/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

Cada out/<name>.csv contiene un bloque CSV por cada tabla detectada, con un encabezado # Table N (pM) que precede a cada bloque. Analizar los bloques con el lector de CSV mostrado arriba.

Combinar --headings-only con el formato markdown permite producir un esquema rápido que se pueda leer o pegar en notas:

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

El comando ast de la CLI devuelve el árbol de un único documento; no tiene un subcomando de comparación. La comparación estructural está disponible en el SDK como client.ast.get_ast_diff(...) y en el servidor Model Context Protocol (MCP) como la herramienta nextpdf_diff. Ejecutar la comparación a través del 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"))

Para ejecutar la misma comparación desde un agente de IA en lugar de un script, registrar el servidor MCP y llamar a la herramienta nextpdf_diff. Consulta la página servidor MCP de Python.

Recipe: transmitir un PDF desde otra herramienta

Sección titulada «Recipe: transmitir un PDF desde otra herramienta»

Leer los bytes del PDF desde stdin con - permite encadenar nextpdf después de una herramienta que emite un PDF en su propio stdout:

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

El argumento - indica al comando que lea el documento desde stdin. Si no llega ningún byte, el comando informa un error y termina con 1.

La CLI construye cada respuesta en memoria y la escribe de una sola vez, por lo que redirigir o canalizar la salida es sencillo, pero la salida no se produce de forma incremental. Un AST o un conjunto de tablas grande se almacena por completo en búfer antes de que el primer byte llegue a stdout o al archivo de --output. Planificar la memoria y la latencia para respuestas de documento completo, no para una transmisión.

Cada invocación de nextpdf crea un cliente y una conexión HTTP nuevos, por lo que un bucle sobre muchos archivos abre y cierra una conexión por archivo. El costo de la conexión suele ser pequeño frente al tiempo de extracción del lado del servidor, pero es una sobrecarga real a gran escala.

  • Reutilizar un solo endpoint. Dirigir cada invocación al mismo despliegue de NextPDF Connect permite que el servidor reutilice las cachés precalentadas y los pools de conexiones. Evitar repartir un lote entre varios endpoints a menos que se esté balanceando la carga a propósito.
  • Acotar el trabajo por llamada. Usar --page, --page-start/--page-end o --token-budget para procesar solo las páginas que se necesitan. Los rangos de páginas más pequeños reducen tanto el tiempo del servidor como el tamaño de la respuesta; --token-budget limita el AST que el agente debe leer.
  • Agrupar en un solo proceso los trabajos grandes. Para lotes de gran volumen, es preferible el SDK de Python frente a llamadas repetidas a la CLI. Un único cliente NextPDF (o AsyncNextPDF) persistente reutiliza una conexión HTTP mantenida en un pool para cada documento, lo que elimina el arranque por proceso y la configuración de la conexión que un bucle de la CLI paga cada vez. La introducción a Python muestra el ciclo de vida del cliente, y AsyncNextPDF admite la extracción concurrente en muchos PDF.
  • Mantener los registros fuera de la ruta de datos. Dejar --log-level en su valor predeterminado para las ejecuciones por lotes. Los registros de diagnóstico van a stderr y no corrompen los datos de stdout, pero subir el nivel a debug añade ruido y una sobrecarga menor.