Claude API PHP SDK Integration Guide
Does Claude have an official PHP SDK?
No. Anthropic maintains official SDKs for Python and TypeScript/JavaScript, but there's no official PHP SDK as of now. If you're building a PHP application — Laravel, Symfony, WordPress plugin, or plain PHP — you have two realistic options: call Claude's REST API directly with Guzzle or cURL, or use a community-maintained wrapper package. This guide covers both, plus a third option that simplifies auth and billing if you're shipping a product to other developers.
The good news is that Claude's API is a straightforward JSON-over-HTTPS interface, so integrating it in PHP without an SDK takes maybe 30 lines of code. Let's build it properly: authentication, request structure, streaming, error handling, and token usage tracking.
Setting up authentication
Claude's API uses an x-api-key header rather than a bearer token, plus an anthropic-version header that pins the API schema you're coding against. Store your key in an environment variable, never in source control.
# .env
ANTHROPIC_API_KEY=sk-ant-api03-xxxxx
Load it with vlucas/phpdotenv or your framework's config system (Laravel's env(), Symfony's .env handling).
Basic request with Guzzle
Install Guzzle if you don't already have it:
composer require guzzlehttp/guzzle
Here's a minimal client for a non-streaming message:
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
function askClaude(string $prompt, string $model = 'claude-3-5-sonnet-20241022'): array
{
$client = new Client(['base_uri' => 'https://api.anthropic.com/']);
try {
$response = $client->post('v1/messages', [
'headers' => [
'x-api-key' => getenv('ANTHROPIC_API_KEY'),
'anthropic-version' => '2023-06-01',
'content-type' => 'application/json',
],
'json' => [
'model' => $model,
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => $prompt],
],
],
]);
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
error_log('Claude API error: ' . $e->getMessage());
throw $e;
}
}
$result = askClaude('Explain dependency injection in two sentences.');
echo $result['content'][0]['text'];
This works, but production code needs more: retry logic for rate limits, timeout handling, and parsing the response shape consistently. That's exactly what an SDK normally gives you for free — and why its absence in PHP is a real gap.
Wrapping it in a reusable client class
Rather than calling Guzzle inline everywhere, wrap the logic in a small class so your application code doesn't need to know about headers or JSON shapes:
<?php
class ClaudeClient
{
private Client $http;
private string $apiKey;
private string $apiVersion = '2023-06-01';
public function __construct(string $apiKey)
{
$this->apiKey = $apiKey;
$this->http = new Client([
'base_uri' => 'https://api.anthropic.com/',
'timeout' => 60,
]);
}
public function message(array $messages, string $model = 'claude-3-5-sonnet-20241022', int $maxTokens = 1024): array
{
$response = $this->http->post('v1/messages', [
'headers' => $this->headers(),
'json' => [
'model' => $model,
'max_tokens' => $maxTokens,
'messages' => $messages,
],
]);
return json_decode($response->getBody(), true);
}
private function headers(): array
{
return [
'x-api-key' => $this->apiKey,
'anthropic-version' => $this->apiVersion,
'content-type' => 'application/json',
];
}
}
This pattern also makes it trivial to swap the base URI later, which matters if you decide to route requests through a gateway instead of calling Anthropic directly.
Handling streaming responses in PHP
Claude supports server-sent events for streaming tokens as they're generated, but streaming in PHP is awkward because most frameworks buffer output and PHP's execution model isn't built around long-lived connections the way Node's is. If you need streaming, you'll want to disable output buffering explicitly:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // disable nginx buffering
$response = $client->post('v1/messages', [
'headers' => $headers,
'json' => array_merge($payload, ['stream' => true]),
'stream' => true,
]);
$body = $response->getBody();
while (!$body->eof()) {
echo $body->read(1024);
flush();
}
This works but is fragile across hosting environments (PHP-FPM, shared hosting, CDNs in front of your app all buffer differently). If your product needs reliable streaming to a browser, a lot of teams find it easier to proxy through an API designed for that exact job rather than fighting PHP's execution model. SubToAPI exposes Claude's streaming endpoint over a standard HTTPS interface with documented SSE behavior — see /docs/streaming for details.
Error handling and rate limits
Claude's API returns standard HTTP status codes: 429 for rate limits, 400 for malformed requests, 401 for auth failures, 529 when the service is overloaded. Wrap calls with retry logic that respects the retry-after header where present:
function callWithRetry(callable $fn, int $maxRetries = 3)
{
$attempt = 0;
while (true) {
try {
return $fn();
} catch (RequestException $e) {
$status = $e->getResponse()?->getStatusCode();
if (in_array($status, [429, 529]) && $attempt < $maxRetries) {
sleep(2 ** $attempt);
$attempt++;
continue;
}
throw $e;
}
}
}
Tracking token usage
Every response includes a usage object with input_tokens and output_tokens. If you're billing customers or just monitoring cost, log this on every call:
$usage = $result['usage'];
error_log("tokens: in={$usage['input_tokens']} out={$usage['output_tokens']}");
If you're exposing Claude functionality to multiple internal apps or external customers, manually aggregating these numbers per API key gets tedious fast. SubToAPI tracks usage per application key automatically and surfaces it in a dashboard, which saves you building that reporting layer yourself — check /docs for the full feature set.
A simpler path: skip the raw integration
Building the PHP wrapper above is a fine learning exercise, but if you're shipping a product that needs application-level API keys, team seats, and usage metadata without writing and maintaining that infrastructure yourself, SubToAPI turns your Claude access into a managed HTTPS API. You get sub_live_... keys scoped per app, streaming support, tool use, and a dashboard — all callable from PHP with the exact same Guzzle pattern shown above, just pointed at https://api.subtoapi.app/v1/messages with Authorization: Bearer $SUBTOAPI_KEY. Start at /signup or see the request/response shapes at /docs/messages and /docs/quickstart.
FAQ
Is there an official Anthropic PHP SDK?
No. Anthropic officially supports Python and TypeScript. PHP developers need to call the REST API directly (with Guzzle or cURL) or use a community package.
Can I use Composer packages instead of writing my own client?
Yes, several unofficial Composer packages wrap the Claude API, but they vary in maintenance quality and API coverage. For production use, verify they support the current /v1/messages endpoint and model versions before depending on them.
Why does streaming feel harder to implement in PHP than in Node or Python?
PHP's traditional request-response execution model and common hosting setups (PHP-FPM, nginx buffering) weren't built for long-lived SSE connections. It's solvable with the right headers and flush() calls, but it's more fragile than in frameworks designed around async streams.