SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor

Claude API su VPS: tutorial Python con Ubuntu 24.04

Crea un analizzatore di log in Python su Ubuntu 24.04: key sicura, virtualenv, streaming, errori tipizzati, systemd e controllo dei costi API.

Cosa stai costruendo

Uno strumento da riga di comando su un VPS Ubuntu 24.04 appena installato. È possibile passargli tramite pipe un messaggio di errore o una parte di un log per ottenere una diagnosi in linguaggio semplice: journalctl -u nginx -n 50 | explain. Il programma contiene circa sessanta righe di Python e mette alla prova tutti gli elementi necessari in una vera applicazione che usa la Claude API: una key archiviata correttamente, un virtualenv, le strutture delle risposte dell'SDK, lo streaming, la catena delle eccezioni tipizzate e un'unità systemd, così il programma viene eseguito senza interventi manuali.

Ho scelto volutamente questo progetto. La maggior parte dei tutorial sulle «prime applicazioni API» propone di creare un chatbot che non verrà mai più utilizzato. Un analizzatore di log è utile su un server fin dal primo giorno e obbliga ad affrontare i due aspetti che i principianti gestiscono più spesso in modo errato: leggere correttamente l'oggetto della risposta e controllare la spesa. L'API addebita i token utilizzati e non impone alcun limite massimo oltre a quelli configurati dall'utente. Per questo il controllo dei costi è un requisito progettuale, non un'attività successiva: è la stessa disciplina necessaria quando si passa a usare Claude Code su questo stesso VPS in tmux.

Ottenere una chiave API dalla Console

L'accesso alle API si gestisce nella Anthropic Console all'indirizzo platform.claude.com. Registrati, quindi crea una chiave in Settings → API Keys (la documentazione rimanda direttamente a platform.claude.com/settings/keys). La Console chiede di assegnare un nome alla chiave, scegliere una scadenza e associarla al proprio account o a un service account. Anthropic consiglia le chiavi dei service account per i servizi di produzione e per qualsiasi chiave condivisa. La chiave viene mostrata una sola volta, inizia con sk-ant- e non può essere recuperata in seguito. Copiala immediatamente oppure eliminala e generane una nuova.

Per quanto riguarda i costi, a ottobre 2026 non è disponibile un piano gratuito continuativo per le API. La documentazione sui prezzi di Anthropic indica che i nuovi utenti ricevono una piccola quantità di crediti gratuiti per i test. L'importo esatto è quello mostrato dalla Console durante la registrazione. Quando i crediti terminano, devi ricaricare il saldo dell'account prima che le richieste vadano a buon fine. Questo sistema è separato da un abbonamento claude.ai e una chiave API non dà accesso all'app di chat. Il piano Pro non include crediti API, mentre i piani Max e Team li includono: a ottobre 2026, $100 al mese per Max 5x e $200 per Max 20x; Team assegna $20 per ogni postazione Standard e $100 per ogni postazione Premium, con un limite di $500. Per utilizzare questi crediti devi collegare un'organizzazione della Console dalle impostazioni di fatturazione di claude.ai. I crediti inutilizzati scadono alla fine di ogni ciclo di fatturazione e non coprono Claude Code. I dettagli sono disponibili in come funzionano i crediti API mensili dei piani Max e Team. Se devi scegliere tra abbonamento e API, considera che si tratta di una valutazione distinta: quale piano Claude ti serve davvero.

Crea la chiave limitandola a un singolo progetto o server. Se una chiave viene esposta, prima o poi succederà, devi poterla revocare senza interrompere tutto il resto delle risorse che gestisci.

Tieni la chiave fuori da .bashrc

La prima reazione è export ANTHROPIC_API_KEY=sk-ant-... in ~/.bashrc. Non farlo. I problemi sono tre:

  • Ogni processo la eredita. Una variabile d'ambiente esportata nella shell di login viene propagata a tutto ciò che avvii: l'applicazione web, il crash reporter che salva opportunamente l'ambiente in un report di errore, la pagina phpinfo() che qualcuno ha lasciato abilitata. La superficie di esposizione della chiave diventa "tutto ciò che questo utente esegue".
  • Se la digiti, finisce in ~/.bash_history. Esegui l'export manualmente una sola volta e la chiave resta in un file in testo semplice, per sempre, oltre a essere inclusa in ogni backup della tua home directory.
  • Non è disponibile quando serve a systemd. I servizi non leggono il tuo .bashrc, quindi questo approccio fallisce proprio quando trasformi lo script in un'unità, di solito con un misterioso errore 401 alle 6 del mattino.

Su un server, l'approccio corretto consiste in un file d'ambiente dedicato con permessi 600, caricato solo dal processo che ne ha bisogno:

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

Usa tee da un printf invece di un editor se vuoi evitare che la chiave finisca nei file di swap dell'editor; in entrambi i casi, verifica con ls -l /etc/claude-explain.env che legga -rw------- e che il file sia di proprietà di root. Le shell interattive ricevono la chiave a ogni esecuzione tramite un wrapper (riportato sotto), mentre systemd la riceve tramite EnvironmentFile=; root legge il file prima di abbandonare i privilegi, quindi l'utente del servizio non deve avere accesso in lettura. La chiave non compare mai nel codice, in git, nell'output di ps o nella cronologia della shell.

Installare l’SDK in un venv

Ubuntu 24.04 include Python 3.12 con l’applicazione di PEP 668, quindi un comando pip install anthropic eseguito sull’interprete di sistema restituisce error: externally-managed-environment. L’errore indica che il sistema operativo sta funzionando come previsto. Usare un virtualenv:

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

Su un server non è necessaria alcuna attivazione: chiamando direttamente /opt/explain/venv/bin/python si usano sempre i pacchetti del venv.

Prima chiamata e lettura corretta della risposta

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)

Due aspetti di queste dodici righe descrivono gran parte del modello mentale dell'API. Primo: anthropic.Anthropic() senza argomenti legge la chiave dall'ambiente; non passarla mai come stringa letterale. Secondo: response.content è un elenco di blocchi di contenuto, non una stringa. Se lo stampi direttamente, ottieni il tipico output di chi effettua la prima chiamata:

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

Non è un bug, ma la rappresentazione dell'oggetto. Le risposte possono contenere più tipi di blocco (testo, chiamate agli strumenti, ragionamento), quindi devi iterare sull'elenco e verificare block.type == "text" prima di accedere a .text. Con Opus 5.5 questo è importante fin dalla prima chiamata: il ragionamento è sempre attivo, quindi, a meno che il modello non lo ometta per una richiesta semplice, l'elenco inizia con un blocco thinking il cui testo è vuoto per impostazione predefinita, mentre response.content[0].text non contiene la risposta. Implementa questo ciclo fin dal primo giorno per evitare completamente una classe di problemi del tipo «stampa dati senza senso».

Usa l'ID esatto del modello claude-opus-5-5. A partire dalla generazione 4.6, gli ID dei modelli non includono la data e l'ID senza data identifica lo snapshot fissato; non seguire quindi l'abitudine di aggiungere un suffisso con la data al modello corrente, come indicato da alcuni vecchi articoli: così ottieni un errore 404, descritto di seguito. I modelli precedenti funzionano in modo diverso: l'ID canonico di Haiku 4.5 è quello con data claude-haiku-4-5-20251001.

Lo strumento effettivo: spiegazione

Ecco il programma completo: legge da stdin, restituisce la diagnosi in streaming e gestisce gli errori:

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

Salvalo come /opt/explain/explain.py, quindi aggiungi un wrapper che carichi la chiave per l'uso interattivo:

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

(Il wrapper deve essere eseguito tramite sudo oppure il file dell'ambiente deve appartenere a un gruppo di cui fa parte l'utente amministratore. Scegli deliberatamente una delle due opzioni, invece di rendere il file accessibile a tutti con i permessi 644.)

Perché usare lo streaming. client.messages.stream stampa i token quando arrivano, invece di restare inattivo per tutta la durata della generazione. Inoltre evita i timeout HTTP per gli output lunghi: per questo motivo l'SDK rifiuta esplicitamente valori max_tokens molto grandi nelle chiamate non in streaming. text_stream restituisce soltanto il testo della risposta e, con la visualizzazione predefinita del ragionamento attiva in Opus 5.5, non viene trasmesso alcun testo del ragionamento. L'output resta quindi pulito. Se in seguito ti serve l'oggetto completo assemblato, chiamalo con stream.get_final_message() all'interno del blocco with.

Perché questo ordine delle eccezioni. L'SDK genera eccezioni tipizzate, dalla più specifica alla più generica: RateLimitError indica un errore 429 e contiene un'intestazione retry-after che specifica per quanto tempo attendere; APIStatusError copre le altre risposte non 2xx (controlla e.status_code >= 500 per individuare problemi lato server); APIConnectionError indica che la richiesta non ha ricevuto alcuna risposta. Prima di creare un ciclo di nuovi tentativi, considera che l'SDK ripete già automaticamente le richieste in caso di errori 429 e 5xx, per impostazione predefinita due volte con backoff esponenziale (max_retries sul client). Quando viene eseguito except, i tentativi automatici sono già terminati. In una CLI, quindi, la scelta corretta è segnalare l'errore e terminare, non attendere e sovraccaricare nuovamente il servizio.

Controllo dei costi

Questo argomento merita una sezione dedicata perché l'API non ha un limite mensile integrato oltre a quello configurato dall'utente, e ogni errore in questo ambito si accumula senza segnali evidenti.

max_tokens è il limite massimo di spesa per chiamata. I token di output sono la componente più costosa: su Opus 5.5 il prezzo è cinque volte quello dei token di input, e max_tokens imposta il limite massimo di token che il modello può produrre. Un prompt fuori controllo non può generare più output di quello consentito. Sui modelli 5.5 questo limite include anche il thinking: i token di thinking vengono conteggiati in max_tokens e fatturati come output, anche se il testo del thinking non viene restituito. Imposta il valore in base all'attività: 4,096 lascia a una diagnosi sintetica di un log spazio sufficiente per il thinking necessario; un'attività di classificazione richiede molto meno. Se le risposte si interrompono con stop_reason: "max_tokens", il limite è troppo basso: aumentalo consapevolmente, oppure riduci l'impostazione dell'effort per fare in modo che il modello esegua meno thinking, invece di impostare automaticamente un valore molto alto.

Calcola i token prima dell'invio. Anche l'input ha un costo e i log possono essere voluminosi. L'API offre un endpoint di conteggio gratuito, soggetto a limiti di frequenza propri e separati da quelli per la creazione dei messaggi:

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

Usalo per evitare di inoltrare accidentalmente un log da 2 GB allo strumento e specifica il modello che utilizzerai effettivamente: Claude 4.7 e i modelli successivi usano un tokenizer più recente, che produce circa il 30% di token in più a parità di testo rispetto ai modelli Claude precedenti. Non usare tiktoken per questo scopo: è il tokenizer di OpenAI, non quello di Claude, quindi i conteggi non corrisponderanno a quelli fatturati.

Scegli il modello in base all'attività, non per abitudine. A ottobre 2026, Opus 5.5 (claude-opus-5-5) costa $4 per milione di token di input e $20 per milione di token di output; Haiku 5.5 (claude-haiku-5-5) costa $0.10/$0.50 per prompt fino a 100,000 token ($0.50/$2.50 oltre questa soglia), con un contesto da 1M; Sonnet 5.5 (claude-sonnet-5-5) si colloca tra i due, a $2/$10. In concreto, un estratto di log da 2,000 token con una risposta da 500 token costa circa $0.018 su Opus e $0.00045 su Haiku, oltre al thinking eseguito dal modello, che viene fatturato come output. Inizia con Opus mentre valuti la qualità dell'output, quindi prova gli stessi prompt su Haiku: per trasformazioni semplici e ad alto volume, la differenza è spesso impercettibile, a un quarantesimo del prezzo per token. Verifica i valori aggiornati nella pagina dei prezzi prima di inserirli in modo fisso in un budget.

Usa i batch per tutto ciò che può attendere. La Batches API elabora le richieste in modo asincrono al 50% dei prezzi standard e la maggior parte dei batch viene completata entro un'ora. Report notturni, backfill, classificazione in blocco e qualsiasi attività per cui non è necessario che una persona attenda la risposta appartengono a questa modalità.

Usa il caching dei prompt per il contesto ripetuto. Se ogni chiamata invia nuovamente lo stesso system prompt o lo stesso runbook voluminoso, contrassegnalo come memorizzabile nella cache:

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

La scrittura nella cache costa 1.25 volte il prezzo dell'input con un TTL di 5 minuti, mentre le letture dalla cache costano 0.05 volte il prezzo su Opus 5.5 e Sonnet 5.5 (0.1 volte sulla maggior parte degli altri modelli); quindi la seconda chiamata effettuata entro questa finestra ha già compensato il costo della prima. Ci sono due aspetti da considerare. Il prefisso memorizzato nella cache deve superare il minimo previsto dal modello, pari a 512 token su Opus 5.5; un system prompt molto breve, quindi, potrebbe non essere memorizzato affatto, senza alcun avviso. Inoltre, se cache_read_input_tokens rimane a zero tra chiamate identiche, significa che qualcosa nel prefisso cambia a ogni richiesta; il caso più comune è un timestamp.

Ricorda che cosa viene conteggiato come input. I system prompt, le definizioni degli strumenti e, nelle conversazioni a più turni, l'intera cronologia reinviata a ogni turno vengono tutti fatturati come token di input. Un ciclo di chat che non riduce mai la cronologia aumenta i costi in modo quadratico. Prima di realizzare un sistema conversazionale, è importante comprendere l'intero meccanismo di conteggio: come vengono calcolati l'uso dei token e la fatturazione di Claude.

Eseguirlo con systemd

Il vantaggio della disciplina nell’uso dei file di ambiente è un timer che ogni mattina riepiloga gli errori del giorno precedente.

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

Notare cosa consente EnvironmentFile=: systemd legge il file di proprietà di root e con modalità 600 prima di passare all’utente senza privilegi explain. In questo modo il processo riceve la variabile, mentre l’utente non può leggere il file che contiene la chiave. Il gruppo systemd-journal concede l’accesso ai log. Eseguire un test con un systemctl start manuale e leggere journalctl -u log-digest.service. Non aspettare le 06:15 per scoprire un errore di battitura. Quando questo schema supera le possibilità di una pipeline shell, lo stesso approccio basato sulla chiave nel file di ambiente può essere trasferito direttamente ai workflow n8n basati su Claude sullo stesso server.

Modalità di errore e stringhe visualizzate

401 con una chiave valida. L'eccezione è:

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

Se la chiave funziona nella shell ma il servizio restituisce 401, significa che il servizio non l'ha mai ricevuta. Ricorda che systemd non legge .bashrc; verifica che EnvironmentFile= punti al percorso corretto. Altre cause possibili: virgolette incollate nel file dell'ambiente (ANTHROPIC_API_KEY="sk-ant-..."; systemd rimuove le virgolette, mentre . file del wrapper della shell le mantiene nel valore se le hai racchiuse tra virgolette in modo errato), spazi vuoti finali, una chiave revocata nella Console la settimana scorsa oppure una chiave arrivata alla scadenza impostata durante la creazione.

404 a causa di un errore di digitazione nel modello. Il caso più comune consiste nell'aggiungere un suffisso con la data a un ID modello che non dispone di una versione datata:

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

Gli ID della generazione attuale devono essere riportati esattamente come sono scritti: claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5, claude-fable-5-1. I modelli precedenti alla generazione 4.6 fanno eccezione: claude-haiku-4-5 è un alias di praticità per l'ID datato claude-haiku-4-5-20251001 e funzionano entrambe le forme. Copia gli ID dalla documentazione dei modelli, non dalla memoria o da un tutorial obsoleto.

429 rate_limit_error. La stringa del tipo di errore è rate_limit_error e la risposta include un'intestazione retry-after con il numero di secondi di attesa. L'SDK ha già effettuato due tentativi con backoff prima che venga visualizzata l'eccezione. Pertanto, 429 persistenti indicano che la velocità sostenuta supera effettivamente quella prevista dal tuo tier. Raggruppa il lavoro in batch o distribuiscilo nel tempo; non ridurre l'intervallo tra i tentativi.

Stampa l'oggetto anziché il testo. L'output è simile a [TextBlock(citations=None, text='...', type='text')]. Hai stampato response.content invece di iterare sui blocchi e leggere .text da quelli per cui block.type == "text". Tutti gli esempi SDK precedenti lo fanno correttamente: copia il ciclo.

error: externally-managed-environment. Hai eseguito pip install usando il Python di sistema di Ubuntu 24.04. Usa il venv; non usare mai --break-system-packages su un server importante.

Risposte troncate. response.stop_reason == "max_tokens" indica che il modello ha raggiunto il limite di output a metà dell'elaborazione. Il comportamento è previsto. Aumenta il limite in modo deliberato e ricorda che nei modelli 5.5 il ragionamento utilizza lo stesso limite.

Dopo aver fatto funzionare la prima applicazione, creare un agente AI con Claude trasforma le stesse chiamate API in un agente che usa strumenti.

FAQ

Quanto costa provare l'API di Claude?

Davvero poco per uno strumento di questo tipo. A ottobre 2026, Opus 5.5 costa $4 per milione di token di input e $20 per milione di token di output. Una diagnosi tipica dei log, con qualche migliaio di token in ingresso e qualche centinaio in uscita, costa circa due centesimi, prima dei token di ragionamento, che vengono fatturati come output. Con Haiku 5.5 ($0.10/$0.50), il costo è ampiamente inferiore a un decimo di centesimo. Un mese di riepiloghi giornalieri costa meno di un caffè. Il rischio non è il prezzo della singola chiamata, ma i loop senza limiti e max_tokens senza limiti. Per questo, in questa guida, entrambi vengono impostati esplicitamente.

È disponibile un piano gratuito per l'API di Claude?

No, a ottobre 2026 non è disponibile un piano gratuito continuativo. La documentazione sui prezzi di Anthropic indica che i nuovi utenti ricevono una piccola quantità di crediti gratuiti per provare l'API. Si tratta di una prova una tantum e l'importo esatto viene mostrato nella Console durante la registrazione. In seguito, è necessario finanziare l'account. Gli abbonati Max e Team fanno eccezione: i relativi piani includono crediti API mensili, come descritto sopra. Se l'obiettivo è azzerare il costo marginale per richiesta, invece di ottenere la massima qualità, l'alternativa è eseguire in hosting autonomo un modello open-weight con Ollama e pagare in RAM anziché in token.

Come posso proteggere la mia chiave API su un server?

Mai nel codice, mai in git e mai esportata da .bashrc. Non va mai digitata in una shell in cui la cronologia possa conservarla. Inseriscila in un file di proprietà di root con permessi 600 e caricala per ogni processo. Per l'uso interattivo puoi usare uno script wrapper, mentre per systemd puoi usare EnvironmentFile=. Limita inoltre l'ambito a una chiave per server o progetto, così la revoca di una chiave esposta resta un'operazione mirata, non una sostituzione completa. Se la chiave entra anche solo una volta in un sito per la condivisione di testo o in un commit git, revocala immediatamente nella Console. Eliminare il commit non la rende nuovamente segreta.

Con quale modello Claude dovrei iniziare?

Inizia con claude-opus-5-5 mentre valuti se gli output sono abbastanza validi per costruirci sopra. In questa fase devi giudicare l'idea alla massima qualità e, con un volume da hobbista, la differenza di costo è di pochi centesimi. Quando il prompt è definito, esegui nuovamente gli input reali su claude-haiku-5-5. Per riepilogo, classificazione e analisi preliminare dei log, questo modello è spesso sufficientemente valido a un quarantesimo del prezzo per token. Passa a Haiku o Sonnet sulla base delle misurazioni, non per impostazione predefinita.