Skip to main content

Claude API (Anthropic): Integrating with a VPS

AI Agents on VPS · 19.04.2026 · 7 min read
Illustration for “Claude API (Anthropic): Integrating with a VPS”

Claude API: AI by Anthropic

Anthropic Claude is a strong competitor to ChatGPT, with a context window of up to 200K tokens. Claude 3.5 Sonnet often outperforms GPT-4o at code analysis and instruction following.

Getting an API Key

Sign up at console.anthropic.com. The first $5 of credits are provided for free.

First Request (cURL)

curl https://api.anthropic.com/v1/messages   -H "x-api-key: $ANTHROPIC_API_KEY"   -H "anthropic-version: 2023-06-01"   -H "content-type: application/json"   -d '{
    "model": "claude-3-5-haiku-20241022",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "How do I set up Nginx on Ubuntu?"}]
  }'

Python SDK

pip install anthropic
import anthropic, os
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
message = client.messages.create(
    model="claude-3-5-haiku-20241022",
    max_tokens=1024,
    system="You are a technical expert on Linux and web hosting.",
    messages=[{"role": "user", "content": "Explain the difference between Apache and Nginx."}]
)
print(message.content[0].text)

PHP Integration

<?php
function askClaude(string $prompt, string $system = ''): string {
    $data = [
        'model'      => 'claude-3-5-haiku-20241022',
        'max_tokens' => 1024,
        'messages'   => [['role' => 'user', 'content' => $prompt]],
    ];
    if ($system) $data['system'] = $system;
    $ch = curl_init('https://api.anthropic.com/v1/messages');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'x-api-key: ' . $_ENV['ANTHROPIC_API_KEY'],
            'anthropic-version: 2023-06-01',
            'content-type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode($data),
    ]);
    $r = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $r['content'][0]['text'] ?? '';
}
echo askClaude('Write a meta description for a hosting company', 'You are an SEO specialist.');

Vision: Image Analysis

import anthropic, base64
client = anthropic.Anthropic()
with open("screenshot.png", "rb") as f:
    img_data = base64.b64encode(f.read()).decode("utf-8")
msg = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    messages=[{"role": "user", "content": [
        {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": img_data}},
        {"type": "text", "text": "Describe the interface in the screenshot."}
    ]}]
)
print(msg.content[0].text)

Claude vs ChatGPT

CriterionClaude 3.5 SonnetGPT-4o
Context200K tokens128K tokens
Code analysisBetterGood
Output (1M)$15$10
Tip: Claude 3 Haiku ($0.25/$1.25 per 1M) is a fast and cheap option for high-load chatbots.

Common Errors and API Response Codes

{
  "type": "error",
  "error": {
    "type": "authentication_error",
    "message": "invalid x-api-key"
  }
}
HTTP code / error typeCauseFix
401 authentication_errorThe API key is wrong, revoked, or sent in the wrong headerCheck the x-api-key header, reissue the key in the console if needed
429 rate_limit_errorThe account's requests-per-minute or tokens-per-minute limit was exceededImplement exponential backoff between retries and watch the rate limit headers in the response
529 overloaded_errorTemporary overload on the API side, unrelated to your accountRetry the request with a delay — this isn't a client error, just temporary service unavailability
400 invalid_request_errorMalformed request — for example, a required field like max_tokens is missingCheck the request body against the API schema, verify the JSON field types

Streaming Output

import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
    model="claude-3-5-haiku-20241022",
    max_tokens=1024,
    messages=[{"role": "user", "content": "List the steps to install Nginx"}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

Streaming is useful for chat interfaces: the user sees text as it's generated instead of waiting for the whole response at once. Without streaming, a request with a long answer can feel stuck for several seconds.

SDK vs. Direct HTTP Requests

ApproachWhen to use itComplexity
Official SDK (Python/TypeScript)Main development work, needs streaming and retry support out of the boxLow, comes with ready-made error handling
Direct HTTP requests (curl, any language)No SDK for your language, or you need full visibility into the requestMedium, you write error handling and retries yourself
Third-party wrappers (LiteLLM and similar)You need an abstraction over several AI providers at onceLow, but adds one more dependency to the project

When the Claude API Doesn't Fit

  • You need to work fully offline or without sending data outside the company's perimeter — a local model is needed here, not a cloud API
  • The task is simple classification with a fixed set of labels on a large data stream — a specialized ML model can be an order of magnitude cheaper and faster than calling an LLM on every item
  • The project budget doesn't account for per-token costs, and the load is unpredictably high — API spend can grow faster than expected without hard limits on the application side
  • You need a guaranteed response latency in single-digit milliseconds — a cloud API adds a network round-trip that a locally-run model doesn't have

Verifying the Integration Works

# Quick check of the key and API availability
curl -s https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"claude-3-5-haiku-20241022","max_tokens":10,"messages":[{"role":"user","content":"ping"}]}' \
  -o /dev/null -w '%{http_code}\n'
  1. Run a test request and confirm the response code is 200, not 401/403 — that confirms the key is valid
  2. Check that the content field in the response is non-empty and holds meaningful text, not an error wrapped in JSON
  3. Measure the response time for a typical request from your application — if it's noticeably more than a few seconds, check whether your code is blocking on synchronous waiting where streaming is what's needed
  4. Log the error code and error type (error.type) separately from the message text — this makes alerting on mass failures much easier

A comparison of Claude with other LLM APIs on price and capabilities is in the article comparing AI APIs, and secure operation of AI agents with access to external systems is covered in AI agent security.

Frequently asked questions

Do I need a dedicated server to use the Claude API, or can I connect it from regular hosting?

Claude API is just a standard HTTPS endpoint, so there's no hard requirement for a VPS: any environment capable of making outgoing requests and storing environment variables will work. A VPS adds more control over background processes and logging for heavy integrations, but a simple script or website runs fine on regular hosting with PHP or Python support.

How do I keep the API key safe so it doesn't end up in the wrong hands?

Store the key in environment variables or a config file outside the repository, never in page code or front-end scripts, since any visitor could see it in the browser source. Requests to the model should run server-side, access to that file should be restricted at the OS level, and a suspected leak means reissuing the key in the console.

Why does the model's answer sometimes cut off midway through a sentence?

The most common cause is setting max_tokens too low: the model stops once it hits the token limit for the answer, even if the thought isn't finished. The fix is raising that limit to match the expected answer length and checking the stop_reason field, which shows whether the stop came from the limit or a natural end.

How hard is it to switch to the Claude API if a project is already built around ChatGPT?

The request structure for both models is similar: a system instruction, a list of messages, and a length parameter, so the basic logic carries over easily. Field names, the response format, and image handling differ, so the integration layer needs rewriting — the function that sends requests and parses responses — while the surrounding business logic usually stays untouched.

Was this article helpful?
← Back to Knowledge Base Ask Support