SSD Nodes Learn
Guias Matt ConnorPor Matt Connor · Atualizado 2026-07-24

Como usar Claude API no Ubuntu 24.04 com Python

Aprenda a criar um script Python para analisar logs no VPS. Inclui configuração de virtualenv, streaming, tratamento de erros e controle de custos via API.

O que você está construindo

Uma ferramenta de linha de comando em um VPS Ubuntu 24.04 recém-instalado. Você envia uma mensagem de erro ou um trecho de log via pipe e recebe um diagnóstico em linguagem simples: journalctl -u nginx -n 50 | explain. O código possui cerca de sessenta linhas de Python e utiliza todos os recursos necessários para uma aplicação real da Claude API — uma chave armazenada corretamente, um virtualenv, os formatos de resposta do SDK, streaming, a cadeia de exceções tipadas e uma unidade systemd para execução em segundo plano.

Escolhi este projeto deliberadamente. A maioria dos tutoriais de "primeiro app com API" foca em chatbots que você nunca mais abrirá. Um explicador de logs é útil em um servidor desde o primeiro dia e obriga você a lidar com os dois erros comuns de iniciantes: ler o objeto de resposta corretamente e controlar gastos. A API cobra por token sem limites além dos definidos por você, portanto, o controle de custos é um requisito de design, não um detalhe secundário — a mesma disciplina necessária ao executar o Claude Code neste mesmo VPS no tmux.

Obtenha uma chave de API no Console

O acesso à API é gerenciado no Anthropic Console em platform.claude.com — faça o cadastro e crie uma chave em Settings → API Keys (o link da documentação direciona para platform.claude.com/settings/keys). A chave é exibida apenas uma vez, começa com sk-ant- e não pode ser recuperada depois — copie-a imediatamente ou exclua e gere uma nova.

Sobre custos: a partir de julho de 2026, não haverá um nível gratuito contínuo para a API. A documentação de preços da Anthropic informa que novos usuários recebem uma pequena quantidade de créditos gratuitos para testes; o valor exato é o que o Console exibir no cadastro e, após o uso desses créditos, você deve adicionar fundos à conta para que as requisições funcionem. Isso é independente de uma assinatura do claude.ai — planos Pro ou Max não incluem créditos de API, e uma chave de API não concede acesso ao aplicativo de chat. Se você está comparando assinatura com API, esse é um tópico separado: qual plano do Claude você realmente precisa.

Crie a chave com escopo limitado a um projeto ou servidor. Quando uma chave vazar — e em um período longo, isso ocorrerá — você precisará revogá-la sem interromper todos os seus outros serviços.

Não coloque a chave no .bashrc

O movimento reflexivo é export ANTHROPIC_API_KEY=sk-ant-... em ~/.bashrc. Não faça isso. Existem três problemas distintos:

  • Todo processo a herda. Uma variável de ambiente exportada no seu shell de login se propaga para tudo o que você iniciar — o web app, o crash reporter que anexa o ambiente em um relatório de erro, ou a página phpinfo() que alguém deixou ativa. A superfície de exposição da chave torna-se "tudo o que este usuário executar".
  • Digitá-la salva em ~/.bash_history. Execute o export manualmente uma vez e sua chave ficará em um arquivo de texto puro, permanentemente, e será sincronizada em todos os backups do seu home directory.
  • Ela não está disponível quando o systemd precisa. Serviços não leem o seu .bashrc, portanto o padrão falha justamente quando você promove o script para uma unit — geralmente resultando em um erro 401 misterioso às 6 a.m.

O padrão correto em um servidor é um arquivo de ambiente dedicado com permissões 600, carregado apenas pelo processo que precisa dele:

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 via printf em vez de um editor se quiser evitar que a chave vá para arquivos swap do editor; de qualquer forma, verifique com ls -l /etc/claude-explain.env se ele lê -rw------- e se pertence ao root. Shells interativos recebem a chave por invocação através de um wrapper (abaixo), e o systemd a recebe via EnvironmentFile= — o root lê o arquivo antes de reduzir os privilégios, então o usuário do serviço nunca precisa de acesso de leitura a ele. A chave nunca aparece no código, no git, no output do ps ou no shell history.

Instale o SDK em um venv

O Ubuntu 24.04 vem com o Python 3.12 e aplica o PEP 668, portanto, um pip install anthropic direto no interpretador do sistema falha com error: externally-managed-environment. Esse erro indica que o SO está funcionando conforme o esperado — use um 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

Não é necessário ativar o ambiente em um servidor: chamar o /opt/explain/venv/bin/python diretamente sempre utiliza os pacotes do venv.

Primeira chamada e leitura correta da resposta

import anthropic

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

response = client.messages.create(
    model="claude-opus-4-8",
    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)

Duas coisas nessas doze linhas definem o modelo mental da API. Primeiro, anthropic.Anthropic() sem argumentos lê a chave do ambiente — nunca a passe como uma string literal. Segundo, response.content é uma lista de blocos de conteúdo, não uma string. Imprima-a diretamente e você terá o erro clássico de iniciante:

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

Isso não é um bug; é o repr do objeto. Respostas podem conter múltiplos tipos de blocos (text, tool calls, thinking), portanto, você deve iterar e verificar block.type == "text" antes de acessar .text. Implemente esse loop desde o início para evitar erros de "saída corrompida".

Use o ID de modelo exato claude-opus-4-8. IDs de geração atual não possuem data — ignore o hábito (ou posts antigos) de adicionar um sufixo de data; isso gera um erro 404, explicado abaixo.

A ferramenta real: explicação

Aqui está o programa completo — entrada via stdin, diagnóstico via stream de saída e tratamento de erros:

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

MODEL = "claude-opus-4-8"

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=1500,
            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())

Salve como /opt/explain/explain.py e adicione um wrapper que carrega a chave para uso interativo:

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

(O wrapper deve ser executado via sudo ou o arquivo de ambiente deve pertencer a um grupo do qual seu usuário admin faz parte — escolha uma opção em vez de alterar as permissões para 644.)

Por que usar streaming. O client.messages.stream imprime os tokens conforme eles chegam em vez de aguardar a geração completa, o que evita timeouts de HTTP em saídas longas — o SDK rejeita valores de max_tokens muito grandes em chamadas sem streaming justamente por esse motivo. Se precisar do objeto completo posteriormente, chame stream.get_final_message() dentro do bloco with.

Por que essa ordem de exceções. O SDK lança exceções tipadas, da mais específica para a mais genérica: RateLimitError é um erro 429 e contém um header retry-after indicando o tempo de espera; APIStatusError cobre outras respostas que não sejam 2xx (verifique e.status_code >= 500 para problemas no servidor); APIConnectionError significa que a requisição não recebeu resposta. Antes de criar um loop de retentativa: o SDK já realiza retentativas automáticas para erros 429 e 5xx, duas vezes por padrão com exponential backoff (max_retries no cliente). Quando o seu except for executado, as retentativas já terão sido esgotadas — portanto, a abordagem correta em uma CLI é reportar o erro e sair, em vez de pausar e sobrecarregar o servidor.

Controle de custos

Esta seção é necessária porque a API não possui um limite mensal integrado além do que você configurar, e qualquer erro aqui gera custos acumulados silenciosamente.

max_tokens é o seu teto de gastos por chamada. Tokens de saída são a parte cara — no Opus 4.8, custam cinco vezes o preço de entrada — e max_tokens é o limite rígido de quantos tokens o modelo pode produzir. Um prompt mal estruturado não pode gerar mais saída do que o permitido. Ajuste conforme a necessidade: 1.500 é suficiente para o diagnóstico de um log; uma tarefa de classificação precisa de 100. Se as respostas pararem no meio da frase com stop_reason: "max_tokens", o limite foi definido de forma muito baixa — aumente o valor de forma consciente em vez de usar um valor padrão muito alto.

Conte antes de enviar. O input também tem custo, e logs são volumosos. A API possui um endpoint de contagem que é gratuito (possui seus próprios limites de taxa, separados da criação de mensagens):

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

Use-o para evitar enviar acidentalmente um log de 2 GB através da ferramenta. Não use tiktoken para isso — esse é o tokenizer da OpenAI, e ele subestima os tokens do Claude em cerca de 15–20% em textos comuns, e ainda mais em código.

Escolha o modelo por tarefa, não por preferência. Em julho de 2026, o Opus 4.8 (claude-opus-4-8) custa $5 por milhão de tokens de entrada e $25 por milhão de saída; o Haiku 4.5 (claude-haiku-4-5) custa $1/$5 com contexto de 200K; o Sonnet 5 (claude-sonnet-5) fica entre eles a $3/$15, com preço promocional de $2/$10 até 31 de agosto de 2026. Na prática: um trecho de log de 2.000 tokens com uma resposta de 500 tokens custa cerca de $0.0225 no Opus e $0.0045 no Haiku. Comece pelo Opus enquanto avalia a qualidade da saída, depois teste os mesmos prompts no Haiku — para transformações simples de alto volume, o resultado costuma ser indistinguível custando um quinto do preço. Verifique os valores atuais na página de preços antes de fixar esses valores em um orçamento.

Use Batches para o que puder esperar. A Batches API processa requisições de forma assíncrona a 50% do preço padrão, e a maioria dos batches termina em uma hora. Resumos diários, backfills, classificação em massa — qualquer coisa que não exija espera humana deve usar este método.

Prompt caching para contexto repetido. Se cada chamada reenvia o mesmo system prompt ou runbook extenso, marque-o como cacheável:

response = client.messages.create(
    model="claude-opus-4-8",
    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

Escritas em cache custam cerca de 1.25x o preço de entrada; leituras em cache custam cerca de 0.1x, com um TTL de 5 minutos — portanto, a segunda chamada dentro da janela já paga pela primeira. Dois pontos de atenção. O prefixo em cache deve atingir um mínimo por modelo — alguns milhares de tokens no Opus — portanto, um system prompt curto não será cacheado. E se cache_read_input_tokens permanecer em zero em chamadas idênticas, algo no seu prefixo muda a cada requisição (um timestamp é o culpado comum).

Lembre-se do que conta como input. System prompts, definições de ferramentas e — em conversas de múltiplos turnos — todo o histórico reenviado a cada turno são cobrados como tokens de entrada. Um loop de chat que nunca remove o histórico cresce quadraticamente em custo. É importante entender o cálculo completo antes de construir qualquer sistema conversacional: como o uso de tokens e o faturamento do Claude realmente funcionam.

Execute sob o systemd

O benefício de usar o arquivo de ambiente: um timer que resume os erros de ontem toda manhã.

# /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 o que o EnvironmentFile= oferece: o systemd lê o arquivo com dono root e permissão 600 antes de mudar para o usuário sem privilégios explain, permitindo que o processo receba a variável enquanto o usuário não consegue ler o arquivo de chave. O grupo systemd-journal concede acesso aos logs. Teste com um systemctl start manual e leia o journalctl -u log-digest.service — não espere até as 06:15 para encontrar um erro de digitação. Quando este padrão se tornar complexo demais para um pipeline de shell, a mesma abordagem de chave-em-arquivo-de-ambiente funciona diretamente em fluxos de trabalho n8n via Claude na mesma máquina.

Modos de falha e as strings que você verá

401 em uma chave funcional. A exceção apresenta:

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

Se a chave funciona no seu shell mas o serviço retorna 401, o serviço nunca a recebeu — lembre-se que o systemd não lê .bashrc; verifique se EnvironmentFile= aponta para o caminho correto. Outras causas: aspas coladas no arquivo env (ANTHROPIC_API_KEY="sk-ant-..." — o systemd remove as aspas, mas o . file do seu shell wrapper as mantém no valor se você as usou de forma incorreta), espaços em branco no final da linha, ou uma chave que você revogou no Console na semana passada.

404 por erro de digitação no modelo. A versão mais comum disso é adicionar um sufixo de data a um ID de modelo atual:

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

IDs de geração atual são exatos conforme escritos — claude-opus-4-8, claude-haiku-4-5, claude-sonnet-5. Copie-os da documentação dos modelos, nunca de memória ou de um tutorial antigo.

429 rate_limit_error. A string do tipo de erro é rate_limit_error e a resposta contém um header retry-after com os segundos de espera. O SDK já tentou duas vezes com backoff antes de você ver a exceção, portanto, 429 persistentes significam que sua taxa sustentada realmente excede seu tier — agrupe o trabalho em lotes ou distribua-o, não diminua o intervalo do loop de retry.

Imprime o objeto, não o texto. O output parece com [TextBlock(citations=None, text='...', type='text')]. Você imprimiu response.content em vez de iterar os blocos e ler .text daqueles onde block.type == "text". Todos os exemplos de SDK acima fazem isso corretamente; copie o loop.

error: externally-managed-environment. Você executou pip install contra o Python do sistema do Ubuntu 24.04. Use o venv — nunca use --break-system-packages em um servidor importante.

Respostas truncadas. response.stop_reason == "max_tokens" significa que o modelo atingiu seu limite de saída durante o processamento. Comportamento esperado; aumente o limite deliberadamente.

Assim que seu primeiro app funcionar, building an AI agent with Claude transforma essas mesmas chamadas de API em um agente que utiliza ferramentas.

FAQ

Quanto custa testar a Claude API?

O custo é muito baixo para uma ferramenta como esta. Em julho de 2026, o Opus 4.8 custa $5 por milhão de tokens de entrada e $25 por milhão de saída. Um diagnóstico de log típico — alguns milhares de tokens de entrada e centenas de saída — custa cerca de dois centavos. No Haiku 4.5 ($1/$5), o custo é inferior a meio centavo. Um mês de resumos diários custa menos que um café. O risco não é o preço por chamada; o risco são loops infinitos e max_tokens ilimitados, por isso ambos são configurados explicitamente neste guia.

Existe um nível gratuito para a Claude API?

Não há um nível gratuito contínuo em julho de 2026. A documentação de preços da Anthropic informa que novos usuários recebem uma pequena quantidade de créditos gratuitos para testar a API — um teste único, com o valor exato exibido no Console ao se cadastrar — após o qual é necessário adicionar fundos à conta. Se o seu objetivo é custo marginal zero por requisição em vez de qualidade de ponta, a alternativa é self-host um modelo open-weight com Ollama e pagar com RAM em vez de tokens.

Como manter minha chave de API segura em um servidor?

Nunca coloque no código, nunca no git, nunca exporte via .bashrc e nunca digite em um shell que salve o histórico. Coloque a chave em um arquivo com dono root e permissões 600. Carregue a chave por processo — um script wrapper para uso interativo e EnvironmentFile= para systemd. Use uma chave por servidor ou projeto para que a revogação de uma chave vazada seja um procedimento simples, não uma amputação. Se a chave for exposta em um site de paste ou em um commit do git, revogue-a no Console imediatamente; deletar o commit não remove o vazamento.

Com qual modelo Claude devo começar?

Comece com claude-opus-4-8 enquanto avalia se os outputs são bons o suficiente para o seu projeto. Você deve julgar a ideia com qualidade máxima e, para volumes de uso pessoal, a diferença de custo é de centavos. Após definir o prompt, execute seus inputs reais no claude-haiku-4-5; para sumarização, classificação e triagem de logs, ele costuma ser tão bom quanto custando um quinto do preço. Mude para o Haiku ou Sonnet com base em métricas, não por padrão.