SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor

Tutorial de Claude API en Ubuntu 24.04 con Python

Crea en un VPS Ubuntu 24.04 un analizador de registros con Claude API: clave segura, virtualenv, streaming, errores tipados y control real del coste.

Qué va a construir

Una herramienta de línea de comandos en un VPS nuevo con Ubuntu 24.04. Puede pasarle un mensaje de error o un fragmento de registro mediante una tubería y obtener un diagnóstico en inglés sencillo: journalctl -u nginx -n 50 | explain. El programa tiene unas sesenta líneas de Python. Incluye los elementos que necesita una aplicación real de la API de Claude: una clave almacenada correctamente, un virtualenv, las estructuras de respuesta del SDK, el streaming, la cadena de excepciones tipadas y una unidad de systemd para que se ejecute sin intervención manual.

He elegido este proyecto de forma deliberada. La mayoría de los tutoriales de «primera aplicación de API» le hacen crear un chatbot que probablemente no volverá a abrir. Un analizador de registros resulta útil en un servidor desde el primer día y le obliga a resolver los dos problemas que los principiantes suelen tener: leer correctamente el objeto de respuesta y controlar el gasto. La API factura por token y no tiene otro límite que los que usted establezca. Por eso, aquí el control de costes es un requisito de diseño, no algo que se añade después. Es la misma disciplina que necesitará cuando pase a ejecutar Claude Code en este mismo VPS dentro de tmux.

Obtener una clave de API desde la Console

El acceso a la API se administra en la Anthropic Console, en platform.claude.com. Regístrese y cree una clave en Settings → API Keys (la documentación enlaza directamente con platform.claude.com/settings/keys). La Console le pide asignar un nombre a la clave, elegir una fecha de expiración y vincularla a su cuenta o a una cuenta de servicio. Anthropic recomienda usar claves de cuentas de servicio para servicios de producción y para cualquier clave compartida. La clave se muestra una sola vez, comienza por sk-ant- y no se puede recuperar después. Cópiela inmediatamente o elimínela y genere otra.

En cuanto al coste, en octubre de 2026 no existe un nivel gratuito permanente para la API. La documentación de precios de Anthropic indica que los usuarios nuevos reciben una pequeña cantidad de créditos gratuitos para realizar pruebas. La cantidad exacta es la que muestra la Console durante el registro. Cuando se agotan, debe añadir fondos a la cuenta para que las solicitudes se procesen correctamente. Esto es independiente de una suscripción a claude.ai, y una clave de API no proporciona acceso a la aplicación de chat. El plan Pro no incluye crédito para la API, pero los planes Max y Team sí lo incluyen: en octubre de 2026, $100 al mes en Max 5x y $200 en Max 20x. Team asigna $20 por puesto Standard y $100 por puesto Premium, con un límite de $500. Para obtenerlo, vincule una organización de la Console desde la configuración de facturación de claude.ai. El crédito no utilizado caduca al final de cada ciclo de facturación y no cubre Claude Code. Los detalles están en cómo funcionan los créditos mensuales de API de Max y Team. Si está comparando una suscripción con la API, esa decisión requiere un análisis independiente: qué plan de Claude necesita realmente.

Cree la clave con un alcance limitado a un proyecto o servidor. Si una clave se filtra, tarde o temprano ocurrirá. En ese caso, podrá revocarla sin interrumpir el resto de los recursos que administra.

Mantenga la clave fuera de .bashrc

La reacción habitual es export ANTHROPIC_API_KEY=sk-ant-... en ~/.bashrc. No lo haga. Esto plantea tres problemas independientes:

  • Todos los procesos la heredan. Una variable de entorno exportada en su shell de inicio de sesión se propaga a todo lo que ejecute: la aplicación web, el informador de fallos que vuelca convenientemente el entorno en un informe de errores y la página phpinfo() que alguien dejó habilitada. La superficie de exposición de la clave pasa a ser «todo lo que este usuario ejecute».
  • Al escribirla, termina en ~/.bash_history. Ejecute la exportación manualmente una vez y la clave quedará en un archivo de texto plano para siempre. Además, se incluirá en todas las copias de seguridad de su directorio personal.
  • No está disponible cuando systemd la necesita. Los servicios no leen su .bashrc, por lo que este patrón falla justo cuando convierte el script en una unidad, normalmente con un 401 inexplicable a las 6 de la mañana.

El patrón correcto en un servidor es un archivo de entorno dedicado con permisos 600, que sólo cargue el proceso que lo necesita:

sudo mkdir -p /opt/explain
sudo install -m 600 -o root -g root /dev/null /etc/claude-explain.env
printf 'ANTHROPIC_API_KEY=sk-ant-YOUR-KEY-HERE\n' | sudo tee /etc/claude-explain.env >/dev/null

Use tee desde un printf en lugar de un editor si quiere evitar que la clave quede en archivos de intercambio del editor. En cualquier caso, compruebe con ls -l /etc/claude-explain.env que lee -rw------- y que pertenece a root. Los shells interactivos reciben la clave en cada invocación mediante un wrapper (más adelante), y systemd la recibe mediante EnvironmentFile=. root lee el archivo antes de reducir privilegios, por lo que el usuario del servicio no necesita permisos de lectura. La clave nunca aparece en el código, en git, en la salida de ps ni en el historial del shell.

Instalar el SDK en un venv

Ubuntu 24.04 incluye Python 3.12 con la aplicación de PEP 668, por lo que ejecutar pip install anthropic directamente con el intérprete del sistema falla con error: externally-managed-environment. El sistema operativo funciona como debe. Use un entorno virtual:

sudo apt update && sudo apt install -y python3-venv
sudo python3 -m venv /opt/explain/venv
sudo /opt/explain/venv/bin/pip install anthropic

En un servidor no es necesario activar el entorno: llamar directamente a /opt/explain/venv/bin/python siempre usa los paquetes del venv.

Primera llamada y lectura correcta de la respuesta

import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from the environment

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1000,
    messages=[{"role": "user", "content": "Explain what a systemd unit file is in three sentences."}],
)

for block in response.content:
    if block.type == "text":
        print(block.text)

Dos aspectos de esas doce líneas contienen la mayor parte del modelo mental de la API. Primero, anthropic.Anthropic() sin argumentos lee la clave desde el entorno. Nunca la pase como una cadena literal. Segundo, response.content es una lista de bloques de contenido, no una cadena. Si la imprime directamente, obtiene la salida clásica de quien hace su primera prueba:

[TextBlock(citations=None, text='A systemd unit file is...', type='text')]

No se trata de un error, sino de la representación del objeto. Las respuestas pueden contener varios tipos de bloques (texto, llamadas a herramientas y razonamiento), por lo que debe iterar sobre ellos y comprobar block.type == "text" antes de acceder a .text. En Opus 5.5 esto es importante desde la primera llamada: el razonamiento siempre está activado. Por tanto, salvo que el modelo omita el razonamiento en una solicitud sencilla, la lista comienza con un bloque thinking cuyo texto está vacío de forma predeterminada, y response.content[0].text no es la respuesta. Si incorpora ese bucle desde el primer día, evita toda una categoría de confusiones del tipo «imprime basura».

Use el ID exacto del modelo claude-opus-5-5. A partir de la generación 4.6, los ID de modelo no incluyen fecha y el ID sin fecha corresponde a la instantánea fijada. Por tanto, no añada por costumbre, ni porque lo indique una publicación antigua, un sufijo de fecha a un modelo actual. Eso produce un error 404, que se explica más adelante. Los modelos anteriores funcionan de otra forma: el ID canónico de Haiku 4.5 es el ID fechado claude-haiku-4-5-20251001.

La herramienta real: explicación

Este es el programa completo: recibe datos de stdin, transmite el diagnóstico y gestiona los errores:

#!/usr/bin/env python3
"""explain: pipe an error or log excerpt in, get a diagnosis out."""
import sys
import anthropic

MODEL = "claude-opus-5-5"

def main() -> int:
    text = sys.stdin.read().strip()
    if not text:
        print("usage: journalctl -u nginx -n 50 | explain", file=sys.stderr)
        return 1

    client = anthropic.Anthropic()
    try:
        with client.messages.stream(
            model=MODEL,
            max_tokens=4096,
            system=(
                "You are a senior Linux sysadmin. The user pipes you server "
                "logs or error output. Name the most likely cause outright, "
                "then give the commands to confirm and fix it. Be terse."
            ),
            messages=[{"role": "user", "content": text}],
        ) as stream:
            for chunk in stream.text_stream:
                print(chunk, end="", flush=True)
        print()
    except anthropic.RateLimitError as e:
        retry_after = e.response.headers.get("retry-after", "60")
        print(f"rate limited; retry in {retry_after}s", file=sys.stderr)
        return 2
    except anthropic.APIStatusError as e:
        print(f"API error {e.status_code}: {e.message}", file=sys.stderr)
        return 2
    except anthropic.APIConnectionError:
        print("network error reaching the API", file=sys.stderr)
        return 2
    return 0

if __name__ == "__main__":
    sys.exit(main())

Guárdelo como /opt/explain/explain.py y añada un wrapper que cargue la clave para el uso interactivo:

sudo tee /usr/local/bin/explain >/dev/null <<'EOF'
#!/bin/sh
set -a; . /etc/claude-explain.env; set +a
exec /opt/explain/venv/bin/python /opt/explain/explain.py "$@"
EOF
sudo chmod 755 /usr/local/bin/explain

(El wrapper debe ejecutarse mediante sudo, o el archivo de entorno debe tener un grupo al que pertenezca su usuario administrador. Elija una opción deliberadamente en lugar de cambiar los permisos del archivo a 644).

Por qué se usa la transmisión. client.messages.stream muestra los tokens a medida que llegan en lugar de permanecer en silencio durante toda la generación. También evita los tiempos de espera de HTTP en salidas largas. El SDK rechazará valores max_tokens muy grandes en llamadas que no usan transmisión precisamente por ese motivo. text_stream devuelve únicamente el texto de la respuesta. Además, con la visualización del razonamiento activada de forma predeterminada en Opus 5.5, no se transmite ningún texto de razonamiento, por lo que la salida permanece limpia. Si necesita el objeto ensamblado después, llame a stream.get_final_message() dentro del bloque with.

Por qué se usa ese orden de excepciones. El SDK genera excepciones tipadas, empezando por la más específica: RateLimitError corresponde a un error 429 e incluye una cabecera retry-after que indica cuánto tiempo debe esperar; APIStatusError cubre otras respuestas que no sean 2xx (compruebe e.status_code >= 500 si sospecha de un problema del servidor); APIConnectionError significa que la solicitud no recibió ninguna respuesta. Antes de crear un bucle de reintento, tenga en cuenta que el SDK ya reintenta por sí mismo los errores 429 y 5xx, dos veces de forma predeterminada y con retroceso exponencial (max_retries en el cliente). Cuando se ejecuta except, los reintentos ya se han agotado. Por tanto, en una CLI lo correcto es informar del error y salir, no esperar y sobrecargar el servicio con más solicitudes.

Control de costos

Esta información merece su propia sección porque la API no tiene un límite mensual integrado aparte del que configure, y cualquier error en este punto se acumula sin que se note.

max_tokens es el límite máximo de gasto por llamada. Los tokens de salida son más caros; en Opus 5.5 cuestan cinco veces más que los de entrada, y max_tokens establece un límite estricto para la cantidad de tokens que el modelo puede producir. Un prompt descontrolado no puede generar más costos de salida que los que haya permitido. En los modelos 5.5, ese límite también incluye el razonamiento: los tokens de razonamiento cuentan para max_tokens y se facturan como salida, aunque el texto del razonamiento no se le devuelva. Ajústelo al trabajo: 4,096 deja espacio para el razonamiento necesario antes de un diagnóstico breve de un registro; una tarea de clasificación necesita mucho menos. Si las respuestas terminan antes de tiempo con stop_reason: "max_tokens", el límite es demasiado bajo. Auméntelo de forma consciente o reduzca el ajuste de esfuerzo para que el modelo razone menos, en lugar de usar un valor enorme por defecto.

Cuente los tokens antes de enviar la solicitud. La entrada también tiene un costo y los registros pueden ser voluminosos. La API tiene un endpoint de conteo de uso gratuito, con sus propios límites de tasa, independientes de la creación de mensajes:

count = client.messages.count_tokens(
    model="claude-opus-5-5",
    messages=[{"role": "user", "content": big_log_text}],
)
print(count.input_tokens)

Úselo para evitar enviar por accidente un registro de 2 GB a través de la herramienta y especifique el modelo que realmente va a utilizar: Claude 4.7 y los modelos posteriores usan un tokenizador más reciente que produce aproximadamente un 30% más de tokens para el mismo texto que los modelos Claude anteriores. No use tiktoken para esto. Es el tokenizador de OpenAI, no el de Claude, por lo que sus conteos no coincidirán con lo que se le facture.

Elija el modelo según la tarea, no por fidelidad a una marca. En octubre de 2026, Opus 5.5 (claude-opus-5-5) cuesta $4 por millón de tokens de entrada y $20 por millón de tokens de salida; Haiku 5.5 (claude-haiku-5-5) cuesta $0.10/$0.50 para prompts de hasta 100,000 tokens ($0.50/$2.50 por encima de ese límite), con un contexto de 1M; Sonnet 5.5 (claude-sonnet-5-5) queda entre ambos, con un precio de $2/$10. En términos concretos, un fragmento de registro de 2,000 tokens con una respuesta de 500 tokens cuesta aproximadamente $0.018 en Opus y $0.00045 en Haiku, más el razonamiento que realice el modelo, que se factura como salida. Empiece con Opus mientras evalúa la calidad de las respuestas y después pruebe los mismos prompts con Haiku. Para transformaciones sencillas y de gran volumen, a menudo resulta indistinguible, con una cuadragésima parte del precio por token. Verifique los valores actuales en la página de precios antes de incluirlos de forma fija en un presupuesto.

Use Batches para todo lo que pueda esperar. Batches API procesa las solicitudes de forma asíncrona al 50% de los precios estándar, y la mayoría de los lotes termina en menos de una hora. Los resúmenes nocturnos, las cargas históricas, la clasificación masiva y cualquier tarea que no requiera que una persona espere deben ejecutarse allí.

Use el almacenamiento en caché de prompts para el contexto repetido. Si cada llamada vuelve a enviar el mismo prompt de sistema extenso o el mismo runbook, márquelo como almacenable en caché:

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1000,
    system=[{
        "type": "text",
        "text": RUNBOOK_TEXT,  # the same 30K tokens on every call
        "cache_control": {"type": "ephemeral"},
    }],
    messages=[{"role": "user", "content": question}],
)
print(response.usage.cache_read_input_tokens)  # non-zero from the second call on

Las escrituras de caché cuestan 1.25x el precio de entrada con un TTL de 5 minutos, y las lecturas de caché cuestan 0.05x en Opus 5.5 y Sonnet 5.5 (0.1x en la mayoría de los demás modelos). Por tanto, la segunda llamada dentro de ese intervalo ya compensa la primera. Hay dos aspectos que debe tener en cuenta. El prefijo almacenado en caché debe superar un mínimo específico del modelo, de 512 tokens en Opus 5.5. Por eso, un prompt de sistema muy corto puede no almacenarse en caché sin ningún aviso. Además, si cache_read_input_tokens sigue siendo cero en llamadas idénticas, algo del prefijo cambia en cada solicitud. Una marca de tiempo suele ser la causa.

Recuerde qué se considera entrada. Los prompts de sistema, las definiciones de herramientas y, en conversaciones con varios turnos, todo el historial que vuelva a enviar en cada turno se facturan como tokens de entrada. Un bucle de chat que nunca recorta el historial aumenta el costo de forma cuadrática. Conviene entender el cálculo completo antes de crear cualquier aplicación conversacional: cómo se calculan realmente el uso de tokens y la facturación de Claude.

Ejecutarlo con systemd

La ventaja de mantener los archivos de entorno de forma rigurosa es un temporizador que resume cada mañana los errores del día anterior.

# /etc/systemd/system/log-digest.service
[Unit]
Description=Daily error-log digest via the Claude API

[Service]
Type=oneshot
User=explain
Group=systemd-journal
EnvironmentFile=/etc/claude-explain.env
ExecStart=/bin/sh -c 'journalctl -p err --since yesterday | /opt/explain/venv/bin/python /opt/explain/explain.py >> /var/log/log-digest.txt'
# /etc/systemd/system/log-digest.timer
[Unit]
Description=Run the log digest every morning

[Timer]
OnCalendar=06:15
Persistent=true

[Install]
WantedBy=timers.target
sudo useradd -r -s /usr/sbin/nologin explain
sudo touch /var/log/log-digest.txt && sudo chown explain /var/log/log-digest.txt
sudo systemctl daemon-reload
sudo systemctl enable --now log-digest.timer
sudo systemctl start log-digest.service   # test it once, right now

Observe qué permite EnvironmentFile=: systemd lee el archivo propiedad de root, con modo 600, antes de cambiar al usuario sin privilegios explain. Así, el proceso recibe la variable, pero el usuario no puede leer el archivo de claves. El grupo systemd-journal concede acceso a los registros. Pruebe con un systemctl start manual y lea journalctl -u log-digest.service. No espere hasta las 06:15 para descubrir un error tipográfico. Cuando este patrón ya no sea suficiente para una canalización de shell, el mismo enfoque de usar una clave en un archivo de entorno se puede trasladar directamente a los flujos de trabajo de n8n con Claude en el mismo equipo.

Modos de fallo y cadenas que verá

401 con una clave válida. La excepción muestra:

anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}, 'request_id': 'req_011CSHoEeqs5C35K2UUqR7Fy'}

Si la clave funciona en su shell, pero el servicio devuelve 401, el servicio nunca la recibió. Recuerde que systemd no lee .bashrc; compruebe que EnvironmentFile= apunte a la ruta correcta. Otras causas posibles son las comillas pegadas en el archivo de entorno (ANTHROPIC_API_KEY="sk-ant-..."; systemd elimina las comillas, pero . file del wrapper del shell las conserva en el valor si las citó de forma incorrecta), los espacios en blanco finales, una clave revocada en la Console la semana pasada o una clave cuya fecha de expiración ya ha llegado.

404 por un error tipográfico en el modelo. La variante más habitual consiste en añadir una fecha al ID de un modelo que no tiene una versión con fecha:

anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-opus-5-5-20260922'}, 'request_id': 'req_011CSJqymAvNw4bT3qmDdMbA'}

Los ID de la generación actual deben escribirse exactamente así: claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5, claude-fable-5-1. Los modelos anteriores a la generación 4.6 son la excepción: claude-haiku-4-5 es un alias práctico del ID con fecha claude-haiku-4-5-20251001, y ambas formas funcionan. Copie los ID de la documentación de modelos. No los introduzca de memoria ni los copie de un tutorial antiguo.

429 rate_limit_error. La cadena del tipo de error es rate_limit_error y la respuesta incluye una cabecera retry-after con los segundos que debe esperar. El SDK ya ha reintentado dos veces con espera progresiva antes de mostrar la excepción. Por tanto, los errores 429 persistentes indican que su tasa sostenida supera realmente el límite de su nivel. Divida el trabajo en lotes o distribúyalo en el tiempo. No reduzca el intervalo entre reintentos.

Imprime el objeto en lugar del texto. La salida tiene este aspecto: [TextBlock(citations=None, text='...', type='text')]. Ha imprimido response.content en lugar de iterar por los bloques y leer .text de aquellos en los que block.type == "text". Todos los ejemplos del SDK anteriores lo hacen correctamente. Copie el bucle.

error: externally-managed-environment. Ha ejecutado pip install contra el Python del sistema de Ubuntu 24.04. Use el venv. Nunca use --break-system-packages en un servidor que deba conservar.

Respuestas truncadas. response.stop_reason == "max_tokens" significa que el modelo alcanzó el límite de salida a mitad de una respuesta. El comportamiento es el esperado. Aumente el límite de forma deliberada y recuerde que, en los modelos 5.5, el razonamiento consume espacio del mismo límite.

Cuando la primera aplicación funcione, crear un agente de IA con Claude convertirá esas mismas llamadas a la API en un agente que usa herramientas.

FAQ

¿Cuánto cuesta probar la API de Claude?

Realmente, muy poco para una herramienta de este tipo. En octubre de 2026, Opus 5.5 cuesta $4 por millón de tokens de entrada y $20 por millón de tokens de salida. Un diagnóstico de registros típico, con un par de miles de tokens de entrada y unos cientos de salida, cuesta alrededor de dos centavos antes de los tokens de razonamiento, que se facturan como salida. Con Haiku 5.5 ($0.10/$0.50), cuesta bastante menos de una décima de centavo. Un mes de resúmenes diarios cuesta menos que un café. El riesgo no es el precio por solicitud, sino los bucles sin límite y el max_tokens sin límite. Por eso ambos se establecen explícitamente en esta guía.

¿Existe un nivel gratuito para la API de Claude?

No existe un nivel gratuito permanente en octubre de 2026. La documentación de precios de Anthropic indica que los usuarios nuevos reciben una pequeña cantidad de créditos gratuitos para probar la API. Es una prueba única. El importe exacto se muestra en la Console durante el registro. Después, debe añadir fondos a la cuenta. Los suscriptores Max y Team son la excepción: sus planes incluyen créditos mensuales para la API, como se explicó arriba. Si su objetivo es tener un coste marginal cero por solicitud, en lugar de obtener la máxima calidad disponible, la alternativa es alojar en su propio servidor un modelo de pesos abiertos con Ollama y pagar en RAM en vez de tokens.

¿Cómo mantengo segura mi clave de API en un servidor?

Nunca la guarde en el código ni en git. No la exporte desde .bashrc ni la escriba en un shell cuyo historial la conserve. Guárdela en un archivo propiedad de root con permisos 600 y cárguela por proceso: mediante un script wrapper para uso interactivo y mediante EnvironmentFile= para systemd. Asigne una clave por servidor o proyecto para que revocar una clave filtrada sea una operación precisa y controlada. Si la clave llega alguna vez a un sitio de pegado o a un commit de git, revóquela inmediatamente en la Console. Eliminar el commit no elimina la filtración.

¿Con qué modelo de Claude debería empezar?

Empiece con claude-opus-5-5 mientras evalúa si los resultados son suficientemente buenos para usarlos como base. Así podrá valorar la idea con la máxima calidad. Con un volumen de uso personal, la diferencia de coste es de unos centavos. Cuando el prompt esté definido, vuelva a ejecutar sus entradas reales con claude-haiku-5-5. Para resumir, clasificar y realizar una clasificación inicial de registros, suele ofrecer resultados suficientes a una cuadragésima parte del precio por token. Cambie a Haiku o Sonnet basándose en mediciones, no de forma predeterminada.