Claude API gebruiken op Ubuntu 24.04: stap-voor-stap
Bouw een Python log-analyser met de Claude API op uw VPS. Leer hoe u veilig API-keys beheert, streaming implementeert, foutafhandeling inricht en uw kosten effectief beheerst.
Wat u bouwt
Een opdrachtregelprogramma op een nieuwe Ubuntu 24.04 VPS waaraan u een foutmelding of een fragment uit een log doorgeeft en dat een diagnose in eenvoudig Engels retourneert: journalctl -u nginx -n 50 | explain. Het programma bestaat uit ongeveer zestig regels Python. Het behandelt alle onderdelen die een echte Claude API-toepassing nodig heeft: een correct opgeslagen key, een virtualenv, de response-objectstructuren van de SDK, streaming, de getypeerde exception-keten en een systemd-unit, zodat het zonder uw tussenkomst draait.
Ik heb dit project bewust gekozen. Bij de meeste tutorials voor een "eerste API-toepassing" bouwt u een chatbot die u daarna nooit meer opent. Een loguitlegger is vanaf de eerste dag nuttig op een server. Daarnaast dwingt het u om de twee zaken goed te doen waar beginners in de praktijk vaak fouten mee maken: het response-object correct lezen en de kosten beheersen. De API brengt kosten per token in rekening, zonder andere bovengrens dan de limieten die u zelf instelt. Kostenbeheersing is hier daarom onderdeel van het ontwerp en geen maatregel achteraf. Het gaat om dezelfde discipline die belangrijk wordt wanneer u overstapt op Claude Code uitvoeren op deze VPS in tmux.
Een API-sleutel verkrijgen via de Console
API-toegang wordt beheerd in de Anthropic Console op platform.claude.com. Meld u aan en maak een sleutel aan onder Settings → API Keys (de documentatie linkt direct naar platform.claude.com/settings/keys). De Console vraagt u om de sleutel een naam te geven, een vervaldatum te kiezen en deze te koppelen aan uzelf of aan een service-account. Anthropic raadt service-account-sleutels aan voor productieservices en gedeelde toepassingen. De sleutel wordt eenmalig getoond, begint met sk-ant- en kan niet opnieuw worden opgevraagd; kopieer deze direct of verwijder de sleutel en maak een nieuwe aan.
Over de kosten: per oktober 2026 is er geen doorlopend gratis niveau voor de API. Volgens de prijsdocumentatie van Anthropic ontvangen nieuwe gebruikers een klein bedrag aan gratis tegoed om te testen. Het exacte bedrag is wat de Console toont bij aanmelding; zodra dit op is, moet u het account opwaarderen voordat verzoeken slagen. Dit staat los van een claude.ai-abonnement en een API-sleutel geeft u geen toegang tot de chat-applicatie. Een Pro-abonnement bevat geen API-tegoed, maar Max- en Team-abonnementen wel: per oktober 2026 is dit $100 per maand bij Max 5x en $200 bij Max 20x (Team bundelt $20 per Standard-seat en $100 per Premium-seat, met een maximum van $500). Dit wordt geactiveerd door een Console-organisatie te koppelen vanuit uw factureringsinstellingen in claude.ai. Ongebruikt tegoed vervalt aan het einde van elke factureringscyclus en dekt geen Claude Code; de details staan in hoe de maandelijkse API-tegoeden voor Max en Team werken. Als u een abonnement afweegt tegen de API, is die afweging een onderwerp op zich: welk Claude-abonnement u daadwerkelijk nodig heeft.
Maak de sleutel aan met een bereik dat beperkt is tot één project of server. Wanneer een sleutel lekt — en op de lange termijn zal dat gebeuren — wilt u deze kunnen intrekken zonder dat al uw andere zaken onbruikbaar worden.
Houd de sleutel uit .bashrc
De voor de hand liggende stap is export ANTHROPIC_API_KEY=sk-ant-... in ~/.bashrc. Doe dat niet. Er zijn drie afzonderlijke problemen:
- Elk proces erft de variabele. Een omgevingsvariabele die in uw aanmeldshell wordt geëxporteerd, wordt doorgegeven aan alles wat u start: de webapplicatie, de crashreporter die behulpzaam de omgeving in een bugrapport dumpt, en de
phpinfo()-pagina die iemand ingeschakeld heeft laten staan. Het blootstellingsoppervlak van de sleutel wordt daarmee "alles wat deze gebruiker ooit uitvoert". - Als u de sleutel invoert, komt deze in
~/.bash_historyterecht. Voer de exportopdracht eenmaal handmatig uit en uw sleutel staat permanent in een tekstbestand. Het bestand wordt bovendien opgenomen in elke back-up van uw home-directory. - De sleutel is niet beschikbaar wanneer systemd deze nodig heeft. Services lezen uw
.bashrcniet. Dit patroon werkt dus niet meer zodra u het script omzet in een unit. Meestal ziet u dan om 6 uur 's ochtends een onverklaarde 401.
Het juiste patroon op een server is een speciaal omgevingsbestand met 600-machtigingen. Alleen het proces dat de sleutel nodig heeft, laadt dit bestand:
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/nullGebruik tee vanuit een printf-opdracht in plaats van een editor als u wilt voorkomen dat de sleutel in tijdelijke editorbestanden terechtkomt. Controleer in beide gevallen met ls -l /etc/claude-explain.env of het bestand -rw------- bevat en eigendom is van root. Interactieve shells krijgen de sleutel per aanroep via een wrapper (hieronder). systemd krijgt de sleutel via EnvironmentFile=. root leest het bestand voordat het proces zijn privileges verlaagt. De servicegebruiker hoeft daardoor geen leestoegang tot het bestand te hebben. De sleutel verschijnt nooit in code, git, ps-uitvoer of de shellgeschiedenis.
De SDK in een venv installeren
Ubuntu 24.04 levert Python 3.12 met PEP 668-afdwinging. Daarom mislukt een directe pip install anthropic tegen de systeeminterpreter met error: externally-managed-environment. Dit is de verwachte werking van het besturingssysteem. Gebruik een virtualenv:
sudo apt update && sudo apt install -y python3-venv
sudo python3 -m venv /opt/explain/venv
sudo /opt/explain/venv/bin/pip install anthropicActiveren is op een server niet nodig. Als u /opt/explain/venv/bin/python rechtstreeks aanroept, gebruikt dit altijd de pakketten van de venv.
De eerste aanroep en het correct uitlezen van het antwoord
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)Twee zaken in die twaalf regels vormen de kern van het mentale model van de API. Ten eerste leest anthropic.Anthropic() zonder argumenten de sleutel uit de omgevingsvariabelen; geef deze nooit door als een string-literal. Ten tweede is response.content een lijst met inhoudsblokken, geen string. Print u deze direct, dan krijgt u de klassieke uitvoer van een beginner:
[TextBlock(citations=None, text='A systemd unit file is...', type='text')]Dit is geen bug; het is de repr van het object. Antwoorden kunnen meerdere bloktypes bevatten (tekst, tool-aanroepen, denkprocessen), dus u moet itereren en block.type == "text" controleren voordat u .text benadert. Op Opus 5.5 is dit vanaf de allereerste aanroep van belang: het denkproces staat altijd aan. Tenzij het model het denkproces bij een eenvoudig verzoek overslaat, begint de lijst met een thinking-blok waarvan de tekst standaard leeg is, en is response.content[0].text niet uw antwoord. Implementeer die lus vanaf de eerste dag en een hele categorie verwarring over "het print onzin" wordt voorkomen.
Gebruik het exacte model-ID claude-opus-5-5. Vanaf de 4.6-generatie zijn model-ID's datumloos en het datumloze ID is de vastgezette snapshot. Weersta dus de gewoonte (of het oude blogbericht) om een datum-achtervoegsel aan een huidig model toe te voegen; dat resulteert in een 404, zoals hieronder beschreven. Oudere modellen werken andersom: het canonieke ID van Haiku 4.5 is het gedateerde claude-haiku-4-5-20251001.
De eigenlijke tool: uitleg
Hier is het volledige programma, stdin als invoer, gestreamde diagnose als uitvoer, fouten afgehandeld:
#!/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())Sla dit op als /opt/explain/explain.py en voeg vervolgens een wrapper toe die de sleutel laadt voor interactief gebruik:
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(De wrapper moet draaien via sudo of het env-bestand moet een groep bevatten waar uw admin-gebruiker lid van is; kies bewust voor een van beide in plaats van de rechten van het bestand te verruimen naar 644.)
Waarom streamen. client.messages.stream print tokens zodra ze binnenkomen in plaats van stil te blijven totdat de volledige generatie is voltooid. Dit voorkomt HTTP-timeouts bij lange uitvoer; de SDK weigert zelfs zeer grote max_tokens-waarden bij niet-streamende aanroepen om precies die reden. text_stream levert alleen de antwoordtekst op, en met de standaard weergave van het denkproces op Opus 5.5 wordt er helemaal geen denktekst gestreamd, waardoor de uitvoer schoon blijft. Als u het samengestelde object achteraf nodig heeft, roep dan stream.get_final_message() aan binnen het with-blok.
Waarom deze volgorde van uitzonderingen. De SDK genereert getypeerde uitzonderingen, van meest specifiek naar algemeen: RateLimitError is een 429-fout en bevat een retry-after-header die aangeeft hoe lang u moet wachten; APIStatusError dekt andere niet-2xx-antwoorden (controleer e.status_code >= 500 voor problemen aan de serverzijde); APIConnectionError betekent dat het verzoek helemaal geen antwoord heeft gekregen. En voordat u een retry-loop bouwt: de SDK voert zelf al retries uit voor 429- en 5xx-fouten, standaard twee keer met exponentiële backoff (max_retries op de client). Tegen de tijd dat uw except wordt uitgevoerd, zijn de retries verbruikt. De juiste actie in een CLI is daarom rapporteren en afsluiten, niet wachten en opnieuw proberen.
Kostenbeheersing
Dit onderwerp verdient een eigen sectie, omdat de API geen ingebouwde maandelijkse limiet heeft buiten wat u zelf configureert, en elke fout hier ongemerkt oploopt.
max_tokens is uw bestedingsplafond per aanroep. Output-tokens zijn de kostbare kant; bij Opus 5.5 zijn deze vijf keer zo duur als input-tokens, en max_tokens is een harde limiet voor het aantal tokens dat het model mag genereren. Een op hol geslagen prompt kan nooit meer output kosten dan u heeft toegestaan. Bij de 5.5-modellen dekt die limiet ook het denkproces: denk-tokens tellen mee voor max_tokens en worden gefactureerd als output, ook al krijgt u de denktekst zelf niet te zien. Stem dit af op de taak: 4,096 tokens biedt ruimte voor een beknopte log-diagnose inclusief het denkproces; een classificatietaak heeft veel minder nodig. Als antwoorden voortijdig afbreken met stop_reason: "max_tokens", heeft u de limiet te krap ingesteld. Verhoog deze bewust (of verlaag de inspanningsinstelling zodat het model minder denkt) in plaats van standaard voor een zeer hoge waarde te kiezen.
Tel voordat u verzendt. Input kost ook geld en logs zijn omvangrijk. De API heeft een tel-endpoint dat gratis te gebruiken is (dit heeft eigen rate limits, los van het aanmaken van berichten):
count = client.messages.count_tokens(
model="claude-opus-5-5",
messages=[{"role": "user", "content": big_log_text}],
)
print(count.input_tokens)Gebruik dit om te voorkomen dat u per ongeluk een logbestand van 2 GB door de tool stuurt, en geef het model door dat u daadwerkelijk gaat aanroepen: Claude 4.7 en latere modellen gebruiken een nieuwere tokenizer die ongeveer 30% meer tokens genereert voor dezelfde tekst dan eerdere Claude-modellen. Gebruik hiervoor niet tiktoken; dat is de tokenizer van OpenAI, niet die van Claude, waardoor de tellingen niet overeenkomen met wat u gefactureerd krijgt.
Kies het model per taak, niet op basis van loyaliteit. Per oktober 2026 kost Opus 5.5 (claude-opus-5-5) $4 per miljoen input-tokens en $20 per miljoen output-tokens; Haiku 5.5 (claude-haiku-5-5) kost $0.10/$0.50 voor prompts tot 100,000 tokens ($0.50/$2.50 daarboven) met een context van 1M; Sonnet 5.5 (claude-sonnet-5-5) zit daar tussenin met $2/$10. Concreet: een logfragment van 2,000 tokens met een antwoord van 500 tokens kost ongeveer $0.018 op Opus en $0.00045 op Haiku, plus het denkwerk van het model, dat als output wordt gefactureerd. Begin op Opus terwijl u de outputkwaliteit beoordeelt en probeer daarna dezelfde prompts op Haiku; voor eenvoudige transformaties op grote schaal is het resultaat vaak niet te onderscheiden, tegen een veertigste van de prijs per token. Controleer de actuele tarieven op de prijspagina voordat u deze hardcode in een budget.
Batches voor alles wat kan wachten. De Batches API verwerkt verzoeken asynchroon tegen 50% van de standaardtarieven, en de meeste batches zijn binnen een uur voltooid. Nachtelijke overzichten, backfills, bulkclassificatie; alles waarvoor geen mens hoeft te wachten, hoort hier thuis.
Prompt caching voor herhaalde context. Als elke aanroep dezelfde grote systeemprompt of handleiding opnieuw verstuurt, markeer deze dan als cacheable:
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 onCache-writes kosten 1.25x de inputprijs bij een TTL van 5 minuten, en cache-reads kosten 0.05x op Opus 5.5 en Sonnet 5.5 (0.1x op de meeste andere modellen), waardoor de tweede aanroep binnen het tijdsbestek de eerste al terugverdient. Twee aandachtspunten: het gecachte voorvoegsel moet een minimum per model overschrijden, 512 tokens op Opus 5.5, dus een zeer korte systeemprompt wordt stilletjes helemaal niet gecachet. En als cache_read_input_tokens nul blijft bij identieke aanroepen, verandert er bij elk verzoek iets in uw voorvoegsel (een tijdstempel is meestal de boosdoener).
Onthoud wat telt als input. Systeemprompts, tooldefinities en, bij gesprekken met meerdere beurten, de volledige geschiedenis die u bij elke beurt opnieuw verstuurt, worden allemaal gefactureerd als input-tokens. Een chat-loop die de geschiedenis nooit inkort, groeit kwadratisch in kosten. Het is verstandig de volledige berekening te begrijpen voordat u iets bouwt dat conversaties bevat: hoe Claude tokengebruik en facturatie daadwerkelijk optellen.
Uitvoeren onder systemd
Het voordeel van het werken met een omgevingsbestand: elke ochtend een timer die de fouten van gisteren samenvat.
# /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.targetsudo 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 nowLet op wat EnvironmentFile= u oplevert: systemd leest het bestand dat eigendom is van root en modus 600 heeft voordat het overschakelt naar de onbevoegde gebruiker explain. Het proces krijgt de variabele daardoor wel, terwijl de gebruiker het sleutelbestand niet kan lezen. De groep systemd-journal verleent toegang tot de logbestanden. Test met een handmatige systemctl start en lees journalctl -u log-digest.service; wacht niet tot 06:15 om een typefout te ontdekken. Wanneer dit patroon te groot wordt voor een shell-pipeline, kunt u dezelfde aanpak met een sleutel in een omgevingsbestand rechtstreeks gebruiken in Claude-aangestuurde n8n-workflows op dezelfde server.
Foutmodi en de bijbehorende meldingen
401 bij een werkende sleutel. De uitzondering luidt:
anthropic.AuthenticationError: Error code: 401 - {'type': 'error', 'error': {'type': 'authentication_error', 'message': 'invalid x-api-key'}, 'request_id': 'req_011CSHoEeqs5C35K2UUqR7Fy'}Als de sleutel wel werkt in uw shell maar de service geeft een 401, dan heeft de service de sleutel niet ontvangen. Onthoud dat systemd .bashrc niet inleest; controleer of EnvironmentFile= naar het juiste pad verwijst. Andere oorzaken: aanhalingstekens in het env-bestand (ANTHROPIC_API_KEY="sk-ant-...", systemd verwijdert de aanhalingstekens, maar de . file van uw shell-wrapper behoudt ze in de waarde als u ze onjuist hebt geplaatst), spaties aan het einde van de regel, een sleutel die u vorige week in de Console hebt ingetrokken, of een sleutel die de vervaldatum heeft bereikt die u bij het aanmaken hebt gekozen.
404 door een typefout in het model. De meest voorkomende vorm hiervan is het toevoegen van een datum-achtervoegsel aan een model-ID dat geen gedateerde vorm heeft:
anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-opus-5-5-20260922'}, 'request_id': 'req_011CSJqymAvNw4bT3qmDdMbA'}ID's van de huidige generatie zijn exact zoals geschreven: claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5, claude-fable-5-1. Modellen van vóór de 4.6-generatie vormen de uitzondering: claude-haiku-4-5 is een handige alias voor het gedateerde ID claude-haiku-4-5-20251001, en beide vormen werken. Kopieer ID's uit de documentatie van de modellen, nooit uit het hoofd of uit een oude handleiding.
429 rate_limit_error. De fouttype-string is rate_limit_error en het antwoord bevat een retry-after-header met het aantal seconden dat u moet wachten. De SDK heeft al twee keer opnieuw geprobeerd met backoff voordat u de uitzondering ziet. Aanhoudende 429-fouten betekenen dus dat uw constante snelheid daadwerkelijk uw limiet overschrijdt; groepeer het werk of spreid het uit, maak de retry-loop niet korter.
Het print het object, niet de tekst. De uitvoer ziet eruit als [TextBlock(citations=None, text='...', type='text')]. U hebt response.content geprint in plaats van door de blokken te itereren en .text te lezen uit de blokken waar block.type == "text" geldt. Elk SDK-voorbeeld hierboven doet dit correct; kopieer de loop.
error: externally-managed-environment. U hebt pip install uitgevoerd op de systeem-Python van Ubuntu 24.04. Gebruik de venv, voer nooit --break-system-packages uit op een server die belangrijk voor u is.
Afgekapte antwoorden. response.stop_reason == "max_tokens" betekent dat het model halverwege de gedachte uw uitvoerlimiet heeft bereikt. Dit werkt zoals ontworpen; verhoog de limiet bewust en onthoud dat bij de 5.5-modellen het denkproces ook van dezelfde limiet gebruikmaakt.
Zodra uw eerste app werkt, verandert het bouwen van een AI-agent met Claude diezelfde API-aanroepen in een agent die tools gebruikt.
FAQ
Hoeveel kost het om de Claude API te proberen?
Verrassend weinig voor een tool van dit kaliber. Sinds oktober 2026 kost Opus 5.5 $4 per miljoen input-tokens en $20 per miljoen output-tokens. Een typische log-analyse, met een paar duizend tokens in en een paar honderd uit, kost dus ongeveer twee cent, exclusief 'thinking tokens' (die als output worden gefactureerd). Bij Haiku 5.5 ($0,10/$0,50) ligt dit bedrag ruim onder een tiende van een cent. Een maand aan dagelijkse samenvattingen kost minder dan een kop koffie. Het risico zit niet in de prijs per aanroep, maar in onbegrensde lussen en onbegrensde max_tokens; daarom worden beide in deze handleiding expliciet ingesteld.
Is er een gratis laag voor de Claude API?
Er is sinds oktober 2026 geen doorlopende gratis laag. Volgens de prijsdocumentatie van Anthropic ontvangen nieuwe gebruikers een klein bedrag aan gratis tegoed om de API te testen; dit is een eenmalige proefperiode waarvan het exacte bedrag bij aanmelding in de Console wordt getoond. Daarna moet het account worden opgewaardeerd. Max- en Team-abonnees vormen de uitzondering: hun abonnementen bevatten maandelijkse API-tegoeden, zoals hierboven beschreven. Als uw doel geen topkwaliteit is, maar nul marginale kosten per verzoek, is het alternatief om een open-weight model zelf te hosten met Ollama en te betalen in RAM in plaats van in tokens.
Hoe houd ik mijn API-sleutel veilig op een server?
Zet deze nooit in code, nooit in git, exporteer deze nooit vanuit .bashrc en typ deze nooit in een shell waar de geschiedenis de sleutel bewaart. Plaats de sleutel in een bestand dat eigendom is van root met 600-rechten, laad deze per proces, gebruik een wrapper-script voor interactief gebruik, gebruik EnvironmentFile= voor systemd en beperk één sleutel tot één server of project. Zo is het intrekken van een gelekte sleutel een gerichte ingreep in plaats van een ingrijpende operatie. Als de sleutel ooit op een paste-site of in een git-commit terechtkomt, trek deze dan onmiddellijk in via de Console; het verwijderen van de commit maakt het lek niet ongedaan.
Met welk Claude-model kan ik het beste beginnen?
Begin met claude-opus-5-5 terwijl u evalueert of de output voldoet om op voort te bouwen. U wilt het concept op volledige kwaliteit beoordelen en bij hobbymatig gebruik is het prijsverschil slechts enkele centen. Zodra de prompt definitief is, voert u uw echte input opnieuw uit met claude-haiku-5-5; voor samenvattingen, classificatie en log-triage is dit model vaak toereikend tegen een veertigste van de prijs per token. Stap pas over op Haiku of Sonnet op basis van metingen, niet uit gewoonte.