SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor

Tutorial da Claude API: primeiro app em um VPS

Aprenda a criar uma chave da Claude API, protegê-la no Ubuntu 24.04 e publicar um explicador de logs em Python com streaming e controle real de custos.

O que você vai criar

Uma ferramenta de linha de comandos num VPS Ubuntu 24.04 novo. Você envia uma mensagem de erro ou um trecho de log para a ferramenta e recebe um diagnóstico em inglês simples: journalctl -u nginx -n 50 | explain. O programa tem cerca de sessenta linhas de Python. Ele cobre os componentes necessários numa aplicação real da Claude API: uma chave armazenada corretamente, um virtualenv, os formatos das respostas do SDK, streaming, a cadeia de exceções tipadas e uma unidade systemd para funcionar sem intervenção.

Escolhi este projeto de propósito. A maioria dos tutoriais de primeira aplicação de API pede que você crie um chatbot que nunca mais vai abrir. Um explicador de logs é útil num servidor desde o primeiro dia e obriga você a lidar com os dois erros que os iniciantes realmente cometem: interpretar corretamente o objeto de resposta e controlar os gastos. A API cobra por token, sem outro limite além dos que você definir. Por isso, o controle de custos faz parte do projeto desde o início, e não é uma preocupação posterior. É a mesma disciplina necessária quando você passar a executar o Claude Code neste mesmo VPS usando tmux.

Obter uma chave de API a partir da Console

O acesso à API é gerido na Anthropic Console, em platform.claude.com. Registe-se e crie uma chave em Settings → API Keys. A documentação liga diretamente a platform.claude.com/settings/keys. A Console pede um nome para a chave, uma data de expiração e a associação à sua conta ou a uma conta de serviço. A Anthropic recomenda chaves de contas de serviço para serviços de produção e para qualquer chave partilhada. A chave é apresentada uma única vez, começa por sk-ant- e não pode ser recuperada depois. Copie-a imediatamente ou elimine-a e crie outra.

Quanto aos custos, em outubro de 2026 não existe um nível gratuito contínuo para a API. A documentação de preços da Anthropic indica que os novos utilizadores recebem uma pequena quantidade de créditos gratuitos para testar o serviço. O valor exato é o que a Console mostrar durante o registo. Depois de os créditos terminarem, tem de adicionar fundos à conta para que os pedidos sejam processados. Isto é separado de uma subscrição claude.ai, e uma chave de API não dá acesso à aplicação de chat. Um plano Pro não inclui créditos de API, mas os planos Max e Team incluem-nos: em outubro de 2026, $100 por mês no Max 5x e $200 no Max 20x. No Team, são $20 por lugar Standard e $100 por lugar Premium, com um limite de $500. Para os obter, associe uma organização da Console nas definições de faturação do claude.ai. Os créditos não utilizados expiram no fim de cada ciclo de faturação e não podem ser usados no Claude Code. Os detalhes estão em como funcionam os créditos mensais de API dos planos Max e Team. Se estiver a comparar uma subscrição com a API, essa decisão é um tema separado: de que plano Claude precisa realmente.

Crie a chave com escopo limitado a um projeto ou servidor. Quando uma chave for exposta, o que acabará por acontecer se o tempo for suficiente, deve poder revogá-la sem interromper todos os outros recursos que gere.

Mantenha a chave fora de .bashrc

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

  • Todos os processos a herdam. Uma variável de ambiente exportada na sua shell de login é propagada para tudo o que iniciar: a aplicação web, o sistema de relatórios de falhas que, de forma conveniente, despeja o ambiente num relatório de erro e a página phpinfo() que alguém deixou ativada. A superfície de exposição da chave passa a ser "tudo o que este utilizador executar".
  • Escrevê-la fica registado em ~/.bash_history. Execute a exportação manualmente uma vez e a chave ficará num ficheiro de texto simples para sempre, além de ser incluída em todas as cópias de segurança do seu diretório pessoal.
  • Ela não estará disponível quando o systemd precisar dela. Os serviços não leem o seu .bashrc, por isso o padrão falha exatamente quando promove o script a uma unit, normalmente com um 401 misterioso às 6 da manhã.

O padrão correto num servidor é um ficheiro 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 a partir de um printf em vez de um editor se quiser manter a chave fora dos ficheiros temporários do editor. De qualquer forma, confirme com ls -l /etc/claude-explain.env que ele lê -rw------- e que pertence a root. As shells interativas recebem a chave por invocação através de um wrapper (abaixo), e o systemd recebe-a através de EnvironmentFile=. O root lê o ficheiro antes de remover privilégios, por isso o utilizador do serviço nunca precisa de ter acesso de leitura a ele. A chave nunca aparece no código, no git, na saída de ps nem no histórico da shell.

Instalar o SDK num venv

O Ubuntu 24.04 inclui o Python 3.12 com a aplicação da PEP 668. Por isso, executar pip install anthropic diretamente no interpretador do sistema falha com error: externally-managed-environment. Esse erro indica que o sistema operativo está a funcionar como previsto. Use um ambiente 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

Num servidor, não é necessária ativação. Executar /opt/explain/venv/bin/python diretamente usa sempre 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-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)

Duas coisas nessas doze linhas explicam a maior parte do modelo mental da API. Primeiro, anthropic.Anthropic() sem argumentos lê a chave a partir do ambiente; nunca a passe como uma literal de string. Segundo, response.content é uma lista de blocos de conteúdo, não uma string. Se a imprimir diretamente, obterá a saída clássica de quem está a fazer a primeira chamada:

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

Isso não é um erro; é a representação (repr) do objeto. As respostas podem conter vários tipos de bloco (texto, chamadas de ferramentas e raciocínio), por isso deve iterar pela lista e verificar block.type == "text" antes de aceder a .text. No Opus 5.5, isto é importante desde a primeira chamada: o raciocínio está sempre ativo. Assim, exceto quando o modelo não faz raciocínio para um pedido simples, a lista começa com um bloco thinking cujo texto está vazio por predefinição, e response.content[0].text não é a resposta. Implemente esse ciclo desde o primeiro dia para evitar toda uma classe de confusões do tipo "imprime lixo".

Use o ID exato do modelo claude-opus-5-5. A partir da geração 4.6, os IDs dos modelos não incluem datas, e o ID sem data corresponde ao snapshot fixado. Por isso, não acrescente um sufixo de data a um modelo atual por hábito ou por seguir uma publicação antiga; isso produz um erro 404, explicado abaixo. Os modelos mais antigos funcionam de outra forma: o ID canónico do Haiku 4.5 é o ID com data claude-haiku-4-5-20251001.

A ferramenta em si: explicação

Este é o programa completo. Recebe dados de stdin, transmite o diagnóstico e trata os erros:

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

Guarde-o como /opt/explain/explain.py. Depois, adicione um wrapper que carregue a chave para utilização interativa:

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 precisa de ser executado através de sudo, ou o ficheiro de ambiente precisa de pertencer a um grupo ao qual o seu utilizador administrativo pertença. Escolha uma destas opções deliberadamente, em vez de tornar o ficheiro legível por todos com a permissão 644.)

Por que transmitir os dados. client.messages.stream imprime os tokens à medida que chegam, em vez de permanecer silencioso durante toda a geração. Também evita timeouts HTTP em respostas longas. Por esse motivo, o SDK recusa valores max_tokens muito grandes em chamadas que não usam streaming. text_stream produz apenas o texto da resposta. Com a apresentação do raciocínio ativada por predefinição no Opus 5.5, nenhum texto de raciocínio é transmitido, pelo que a saída permanece limpa. Se precisar do objeto completo depois, chame stream.get_final_message() dentro do bloco with.

Por que esta ordem das exceções. O SDK lança exceções tipadas, da mais específica para a mais geral: RateLimitError corresponde a um erro 429 e inclui um cabeçalho retry-after que indica quanto tempo deve esperar; APIStatusError abrange outras respostas que não sejam 2xx (consulte e.status_code >= 500 para problemas no servidor); APIConnectionError indica que o pedido não recebeu resposta. Antes de criar um ciclo de novas tentativas, tenha em conta que o SDK já repete automaticamente os pedidos que recebem erros 429 e 5xx, duas vezes por predefinição, com recuo exponencial (max_retries no cliente). Quando except é executado, as novas tentativas já se esgotaram. Por isso, numa CLI, a opção correta é comunicar o erro e sair, não aguardar e continuar a sobrecarregar o serviço.

Controle de custos

Esta questão merece uma secção própria porque a API não tem um limite mensal integrado além do que configurar, e cada erro aqui aumenta os custos silenciosamente.

max_tokens é o seu limite máximo de custo por chamada. Os tokens de saída são a parte mais cara: no Opus 5.5, custam cinco vezes mais do que os tokens de entrada, e max_tokens define um limite rígido para a quantidade que o modelo pode produzir. Um prompt descontrolado não pode gerar mais saída do que a permitida. Nos modelos 5.5, esse limite também inclui o raciocínio: os tokens de raciocínio contam para max_tokens e são faturados como saída, embora o texto do raciocínio não lhe seja devolvido. Defina o valor de acordo com a tarefa: 4,096 dá espaço para o raciocínio necessário antes de um diagnóstico sucinto de logs; uma tarefa de classificação precisa de muito menos. Se as respostas terminarem com stop_reason: "max_tokens", o limite está demasiado baixo. Aumente-o conscientemente ou reduza a definição de esforço para o modelo raciocinar menos, em vez de usar valores enormes por defeito.

Conte os tokens antes de enviar. A entrada também tem custo, e os logs ocupam bastante espaço. A API tem um endpoint de contagem gratuito, com os seus próprios limites de taxa, separados da criação de mensagens:

count = client.messages.count_tokens(
    model="claude-opus-5-5",
    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 e indique o modelo que realmente vai chamar: os modelos Claude 4.7 e posteriores usam um tokenizer mais recente, que produz aproximadamente 30% mais tokens para o mesmo texto do que os modelos Claude anteriores. Não use tiktoken para esta finalidade. Esse é o tokenizer da OpenAI, não o do Claude, e as contagens não corresponderão ao valor faturado.

Escolha o modelo por tarefa, não por fidelidade. Em outubro de 2026, o Opus 5.5 (claude-opus-5-5) custa $4 por milhão de tokens de entrada e $20 por milhão de tokens de saída; o Haiku 5.5 (claude-haiku-5-5) custa $0.10/$0.50 para prompts até 100,000 tokens e $0.50/$2.50 acima desse limite, com um contexto de 1M; o Sonnet 5.5 (claude-sonnet-5-5) fica entre os dois, a $2/$10. Na prática, um excerto de log com 2,000 tokens e uma resposta com 500 tokens custam cerca de $0.018 no Opus e $0.00045 no Haiku, além do raciocínio realizado pelo modelo, que é faturado como saída. Comece pelo Opus enquanto avalia a qualidade da saída. Depois teste os mesmos prompts no Haiku. Para transformações simples e de grande volume, o resultado é frequentemente indistinguível, com um quadragésimo do preço por token. Confirme os valores atuais na página de preços antes de os incluir de forma fixa num orçamento.

Use batches para tudo o que puder esperar. A Batches API processa pedidos de forma assíncrona por 50% dos preços normais, e a maioria dos batches termina dentro de uma hora. Resumos noturnos, preenchimentos retroativos, classificações em massa e qualquer tarefa que não exija a espera de uma pessoa devem usar esse mecanismo.

Use o prompt caching para contexto repetido. Se cada chamada reenviar o mesmo prompt de sistema grande ou o mesmo runbook, marque-o como cacheável:

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

As gravações na cache custam 1.25x o preço da entrada durante um TTL de 5 minutos, e as leituras da cache custam 0.05x no Opus 5.5 e no Sonnet 5.5, ou 0.1x na maioria dos outros modelos. Assim, a segunda chamada dentro da janela já compensa o custo da primeira. Existem duas limitações. O prefixo em cache tem de ultrapassar um mínimo específico do modelo, de 512 tokens no Opus 5.5. Portanto, um prompt de sistema muito curto não será colocado em cache. Além disso, se cache_read_input_tokens permanecer a zero em chamadas idênticas, alguma parte do prefixo está a mudar em cada pedido. Um timestamp é a causa mais comum.

Lembre-se do que conta como entrada. Os prompts de sistema, as definições das ferramentas e, em conversas com várias interações, todo o histórico reenviado em cada turno são faturados como tokens de entrada. Um ciclo de chat que nunca reduz o histórico aumenta o custo de forma quadrática. Vale a pena compreender toda a contabilização antes de criar algo conversacional: como o uso e a faturação de tokens do Claude são realmente calculados.

Execute com systemd

A vantagem de manter os ficheiros de ambiente organizados: um temporizador que resume os erros do dia anterior todas as manhãs.

# /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 EnvironmentFile= permite: o systemd lê o ficheiro pertencente a root, com modo 600, antes de mudar para o utilizador sem privilégios explain. Assim, o processo recebe a variável, mas o utilizador não consegue ler o ficheiro de chaves. O grupo systemd-journal concede acesso aos logs. Teste com um systemctl start manual e leia journalctl -u log-digest.service. Não espere pelas 06:15 para descobrir um erro de digitação. Quando este padrão deixar de ser suficiente para um pipeline de shell, a mesma abordagem de chave num ficheiro de ambiente aplica-se diretamente a fluxos de trabalho n8n com Claude no mesmo servidor.

Modos de falha e as mensagens que verá

401 com uma chave válida. A exceção é:

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

Se a chave funciona na sua shell, mas o serviço devolve 401, o serviço nunca a recebeu. Lembre-se de que o systemd não lê .bashrc. Verifique se EnvironmentFile= aponta para o caminho correto. Outras causas possíveis são aspas coladas no ficheiro de ambiente (ANTHROPIC_API_KEY="sk-ant-..."; o systemd remove as aspas, mas o seu wrapper da shell, . file, mantém-nas no valor se tiver usado aspas de forma incorreta), espaços em branco no fim da linha, uma chave revogada na Console na semana passada ou uma chave que atingiu a data de expiração escolhida quando foi criada.

404 devido a um erro de escrita no modelo. A forma mais comum deste problema consiste em acrescentar um sufixo de data a um ID de modelo que não tem uma versão datada:

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

Os IDs da geração atual devem ser usados exatamente como estão escritos: claude-opus-5-5, claude-sonnet-5-5, claude-haiku-5-5, claude-fable-5-1. Os modelos anteriores à geração 4.6 são a exceção: claude-haiku-4-5 é um alias de conveniência para o ID datado claude-haiku-4-5-20251001, e ambas as formas funcionam. Copie os IDs da documentação dos modelos. Nunca os copie de memória ou de um tutorial antigo.

429 rate_limit_error. A string do tipo de erro é rate_limit_error e a resposta inclui um cabeçalho retry-after com o número de segundos a aguardar. O SDK já tentou novamente duas vezes, com backoff, antes de apresentar a exceção. Por isso, 429 persistentes significam que a sua taxa sustentada excede efetivamente o limite do seu nível. Agrupe o trabalho ou distribua-o ao longo do tempo. Não reduza o intervalo entre tentativas.

Imprime o objeto em vez do texto. A saída é semelhante a [TextBlock(citations=None, text='...', type='text')]. Imprimiu response.content em vez de iterar pelos blocos e ler .text nos blocos em que block.type == "text". Todos os exemplos do SDK acima fazem isto corretamente. Copie o loop.

error: externally-managed-environment. Executou pip install contra o Python do sistema no Ubuntu 24.04. Use o venv. Nunca use --break-system-packages num servidor importante.

Respostas truncadas. response.stop_reason == "max_tokens" significa que o modelo atingiu o limite de saída a meio da resposta. Este é o comportamento esperado. Aumente o limite de forma deliberada. Lembre-se de que, nos modelos 5.5, o raciocínio consome o mesmo limite.

Depois de a primeira aplicação funcionar, criar um agente de IA com Claude transforma essas mesmas chamadas à API num agente que utiliza ferramentas.

FAQ

Quanto custa experimentar a API do Claude?

Muito pouco para uma ferramenta deste tipo. Em outubro de 2026, o Opus 5.5 custa $4 por milhão de tokens de entrada e $20 por milhão de tokens de saída. Um diagnóstico típico de logs, com alguns milhares de tokens de entrada e algumas centenas de saída, custa cerca de dois cêntimos antes dos tokens de raciocínio, que são cobrados como saída. Com o Haiku 5.5 ($0.10/$0.50), fica bem abaixo de um décimo de cêntimo. Um mês de resumos diários custa menos do que um café. O risco não está no preço por chamada, mas em loops sem limite e em max_tokens sem limite. Por isso, ambos são definidos explicitamente neste guia.

Existe um nível gratuito para a API do Claude?

Não existe um nível gratuito contínuo em outubro de 2026. A documentação de preços da Anthropic indica que os novos utilizadores recebem uma pequena quantidade de créditos gratuitos para testar a API, num período experimental único. O valor exato é apresentado na Console durante o registo. Depois disso, é necessário financiar a conta. Os subscritores dos planos Max e Team são a exceção: os seus planos incluem créditos mensais para a API, conforme descrito acima. Se o objetivo for custo marginal zero por pedido, em vez de qualidade de frontier, a alternativa é alojar localmente um modelo de pesos abertos com Ollama e pagar em RAM, não em tokens.

Como mantenho a minha chave da API segura num servidor?

Nunca a coloque no código, no git ou exportada a partir de .bashrc. Nunca a introduza numa shell onde o histórico a possa guardar. Coloque-a num ficheiro pertencente a root, com permissões 600, e carregue-a por processo: através de um script wrapper para utilização interativa e de EnvironmentFile= para o systemd. Use uma chave por servidor ou projeto, para que a revogação de uma chave exposta seja uma operação precisa, não uma intervenção abrangente. Se a chave alguma vez aparecer num site de partilha de texto ou num commit do git, revogue-a imediatamente na Console. Apagar o commit não elimina a exposição da chave.

Com que modelo do Claude devo começar?

Comece com claude-opus-5-5 enquanto avalia se os resultados têm qualidade suficiente para servir de base ao trabalho. Assim, pode avaliar a ideia com a qualidade máxima. Em volumes de utilização amadores, a diferença de custo é de apenas alguns cêntimos. Depois de estabilizar o prompt, execute novamente as entradas reais com claude-haiku-5-5. Para sumarização, classificação e triagem de logs, este modelo é frequentemente suficiente por um quadragésimo do preço por token. Mude para Haiku ou Sonnet com base em medições, não por predefinição.