# Why I Built a PHP Client for Anthropic's Claude API

**Author:** Mozex | **Published:** 2026-04-02 | **Tags:** Laravel, PHP, Open Source, AI, Anthropic | **Package:** mozex/anthropic-php | **URL:** https://mozex.dev/blog/9-why-i-built-a-php-client-for-anthropics-claude-api

---


In May 2024, I published the first version of [anthropic-php](https://github.com/mozex/anthropic-php), a PHP client for Anthropic's Claude API. Nearly two years and 370,000+ downloads later, it's the most installed dedicated Anthropic SDK in the PHP ecosystem. This is the story of why I built it, what it can do, and why I think packages born from real necessity tend to be the good ones.

<!--more-->

## The Problem

I was building a multi-provider AI chat app called Sevantia. It needed to work with Claude, GPT, and other models from a single codebase. OpenAI had [openai-php/client](https://github.com/openai-php/client), and that worked perfectly. Clean API, well-maintained, Laravel wrapper included.

Anthropic? Not so much.

There was no official PHP SDK. A few community packages existed, but I tried them all. One supported streaming but not tool use. Another had tool use but broke on edge cases. Most hadn't been updated in months. The code quality across the board was rough.

I couldn't ship a product where Claude's integration was held together by duct tape and hope. So after weeks of evaluating options, I did what any stubborn developer would do.

I built my own.

## Building It

The first commit landed on April 30, 2024. Version 1.0.0 hit Packagist the next day.

I modeled the architecture after [openai-php/client](https://github.com/openai-php/client), which I was already using for the OpenAI integration. Same patterns, same philosophy: clean interfaces, proper value objects for responses, and a factory pattern that lets you configure everything without magic. If you've used the OpenAI PHP client, anthropic-php feels familiar. That was intentional.

Every line was hand-written. I built the message handling, the streaming logic, the error types, the response objects. Then I used it in my own project. Every bug, every edge case, every "this response shape isn't quite what I expected" scenario got caught because I was hitting the API daily in a production application. Real usage beats unit tests for finding the weird stuff.

That feedback loop is what made the package solid. I wasn't building a theoretical SDK by reading docs. I was building something I depended on, fixing problems as I hit them.

## What It Can Do

The package covers the full Anthropic API. Here's a quick look at the core features.

### Messages

Send a message, get a response:

```php
$response = $client->messages()->create([
    'model' => 'claude-sonnet-4-5-20250514',
    'max_tokens' => 1024,
    'messages' => [
        ['role' => 'user', 'content' => 'Explain PHP generators in three sentences.'],
    ],
]);

echo $response->content[0]->text;
```

### Streaming

For real-time responses:

```php
$stream = $client->messages()->createStreamed([
    'model' => 'claude-sonnet-4-5-20250514',
    'max_tokens' => 1024,
    'messages' => [
        ['role' => 'user', 'content' => 'Write a short poem about PHP.'],
    ],
]);

foreach ($stream as $response) {
    echo $response->content[0]->text;
}
```

This was one of the trickiest parts to get right. Anthropic's streaming API uses server-sent events, and handling partial JSON chunks and proper event parsing in PHP took real work. But streaming was non-negotiable for a real-time chat interface, so it had to work perfectly.

### Extended Thinking

Claude's thinking capability lets the model reason through complex problems before responding:

```php
$response = $client->messages()->create([
    'model' => 'claude-sonnet-4-5-20250514',
    'max_tokens' => 16000,
    'thinking' => [
        'type' => 'enabled',
        'budget_tokens' => 10000,
    ],
    'messages' => [
        ['role' => 'user', 'content' => 'What is the optimal strategy for this problem?'],
    ],
]);
```

### Tool Use

Full support for defining tools and handling tool-use responses, including while streaming:

```php
$response = $client->messages()->create([
    'model' => 'claude-sonnet-4-5-20250514',
    'max_tokens' => 1024,
    'tools' => [
        [
            'name' => 'get_weather',
            'description' => 'Get current weather for a location.',
            'input_schema' => [
                'type' => 'object',
                'properties' => [
                    'location' => [
                        'type' => 'string',
                        'description' => 'City name',
                    ],
                ],
                'required' => ['location'],
            ],
        ],
    ],
    'messages' => [
        ['role' => 'user', 'content' => 'What is the weather in Istanbul?'],
    ],
]);
```

### Message Batches

For high-volume workloads, batches let you send up to 100,000 messages at once with 50% cost savings:

```php
$batch = $client->batches()->create([
    'requests' => [
        [
            'custom_id' => 'request-1',
            'params' => [
                'model' => 'claude-sonnet-4-5-20250514',
                'max_tokens' => 1024,
                'messages' => [
                    ['role' => 'user', 'content' => 'Summarize this document.'],
                ],
            ],
        ],
    ],
]);
```

### Token Counting

Know exactly how many tokens your request will consume before sending it:

```php
$response = $client->messages()->countTokens([
    'model' => 'claude-sonnet-4-5-20250514',
    'messages' => [
        ['role' => 'user', 'content' => 'Your message here.'],
    ],
]);

echo $response->inputTokens; // 12
```

### Models

List available models or get details about a specific one:

```php
$models = $client->models()->list();
$model = $client->models()->retrieve('claude-sonnet-4-5-20250514');
```

### Testing

This is one of the features I'm most proud of. The package includes a built-in test fake so you don't need to mock HTTP calls or hit the real API in your test suite:

```php
use Anthropic\Testing\ClientFake;
use Anthropic\Responses\Messages\CreateResponse;

$client = new ClientFake([
    CreateResponse::fake([
        'content' => [['text' => 'Test response']],
    ]),
]);

// Use $client as normal in your test
$response = $client->messages()->create([...]);

$client->assertSent(Messages::class, function ($method, $parameters) {
    return $parameters['model'] === 'claude-sonnet-4-5-20250514';
});
```

No more fragile HTTP mocks. No more accidentally hitting production APIs in CI. Clean, deterministic tests.

### And More

The package also handles rate limit information via response meta, dedicated `RateLimitException` for HTTP 429 responses, configurable base URI for proxies, and PSR-18/PSR-7 compliance so you can bring your own HTTP client.

New features get added as Anthropic ships them. The 1.2.x and 1.3.x releases in March 2026 added thinking support, token counting, models, batches, and rate limit headers over a burst of eight releases in two days. The package grows with the API.

## The Laravel Wrapper

If you're working in Laravel, [anthropic-laravel](https://github.com/mozex/anthropic-laravel) wraps the PHP client with everything you'd expect from a first-class Laravel package.

Install it:

```bash
composer require mozex/anthropic-laravel
```

Run the install command:

```bash
php artisan anthropic:install
```

This publishes the config file and adds `ANTHROPIC_API_KEY` to your `.env`. Then use the facade:

```php
use Anthropic\Laravel\Facades\Anthropic;

$response = Anthropic::messages()->create([
    'model' => 'claude-sonnet-4-5-20250514',
    'max_tokens' => 1024,
    'messages' => [
        ['role' => 'user', 'content' => 'Hello, Claude!'],
    ],
]);
```

Every feature from the PHP package is available through the facade. Streaming, tool use, batches, thinking, token counting, models. All of it.

The Laravel wrapper also adds its own testing integration:

```php
use Anthropic\Laravel\Facades\Anthropic;
use Anthropic\Responses\Messages\CreateResponse;

Anthropic::fake([
    CreateResponse::fake(),
]);

// Run the code that calls the API...

Anthropic::assertSent(Messages::class);
```

This swaps the real client for a fake in Laravel's service container, so your entire application uses the fake automatically. No dependency injection gymnastics needed.

I didn't write a separate post for the Laravel package because the features are identical to the PHP client. The wrapper adds three things: a facade, a publishable config file, and Laravel-specific testing helpers. If you're in Laravel, use it. If you're in vanilla PHP or another framework, the base package works everywhere.

## The Numbers

As of today, the PHP package has over 370,000 downloads and the Laravel wrapper has over 230,000. Combined, that's 600,000+ installs across both packages.

All of this growth has been organic. I never promoted these packages. No blog posts (until now), no tweets, no conference talks. People found them on Packagist, tried them, and kept using them.

Anthropic released their own official PHP SDK in August 2025, about 16 months after mine. It's well-maintained and gaining traction.

Here's how the two compare right now:

| | anthropic-php | Official SDK |
|---|---|---|
| Total downloads | 370,000+ | 144,000+ |
| First release | May 2024 | August 2025 |
| Stable release | Yes (v1.3.3) | No (v0.8.0, pre-1.0) |
| Dependents on Packagist | 11 | 4 |

The official SDK has higher daily downloads at this point, which makes sense. It has Anthropic's name behind it. If you're starting a new project today, both packages are solid choices. I wrote a [full comparison of all the major AI packages for Laravel](https://mozex.dev/blog/5-which-ai-package-should-you-actually-use-in-laravel) that covers multi-provider options like Laravel AI SDK and Prism too, if you're still deciding.

I still prefer my own for the projects I work on. It's stable at 1.x with no breaking changes, I know every corner of the codebase, and it has the testing infrastructure that I rely on in production. The official SDK is still pre-1.0, which means breaking changes can happen between minor versions.

## Packages Born from Need

There's a pattern I've noticed across the PHP ecosystem. The best packages tend to come from developers who needed them for their own work.

anthropic-php wasn't an idea I had while scanning Packagist for gaps. I didn't set out to build a popular PHP SDK. I was building an AI chat app, I needed an Anthropic client that worked, and nothing available met my standards. So I built one for myself and published it.

That origin matters. When you build a package for your own project, you're the first user, the first QA tester, and the first person to hit the edge cases. You don't ship features you haven't used. You don't skip error handling because you've seen what happens when errors go unhandled in production.

I've seen this same pattern with packages like Pest, Filament, and much of Spatie's catalog. They weren't built to fill a market gap. They were built because someone had a problem and cared enough to solve it well.

If you're building something with Claude's API in PHP, give [anthropic-php](https://github.com/mozex/anthropic-php) a try. And if you're on Laravel, [anthropic-laravel](https://github.com/mozex/anthropic-laravel) makes the setup even easier.