EzAI
Back to Blog
Tutorial Apr 20, 2026 7 min read

Using EzAI API with PHP — Claude & GPT in Laravel

E

EzAI Team

Using EzAI with PHP

PHP still powers a huge slice of the web — WordPress, Laravel, Symfony, Magento, and a mountain of custom CMSes. If you're adding AI features to a PHP app, you don't need a new stack or a rewrite. EzAI exposes an Anthropic-compatible endpoint, so anything that can send a POST request can talk to Claude, GPT, Gemini, and Grok through a single URL.

This guide shows three ways to call EzAI from PHP: raw cURL, Guzzle, and Laravel's HTTP client — plus streaming with Server-Sent Events.

Setup

Three PHP integration options for EzAI

Pick the client that fits your stack — all three hit the same EzAI endpoint

First, grab an API key from the dashboard. Every new account ships with 15 free credits, which is plenty for testing. Store the key in your .env:

env
EZAI_API_KEY=sk-your-key-here
EZAI_BASE_URL=https://ezaiapi.com

The endpoint https://ezaiapi.com/v1/messages speaks native Anthropic protocol. Use the same request body format as the official Claude API — any PHP Anthropic library will work unchanged.

Option 1: Plain cURL (Zero Dependencies)

If you're on a small shared host or don't want Composer in the mix, stdlib cURL gets you there in 15 lines:

php
<?php
function ezai_chat(string $prompt, string $model = 'claude-sonnet-4-5'): string
{
    $ch = curl_init('https://ezaiapi.com/v1/messages');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'x-api-key: ' . getenv('EZAI_API_KEY'),
            'anthropic-version: 2023-06-01',
            'content-type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'model' => $model,
            'max_tokens' => 1024,
            'messages' => [['role' => 'user', 'content' => $prompt]],
        ]),
        CURLOPT_TIMEOUT => 60,
    ]);

    $response = curl_exec($ch);
    if ($response === false) {
        throw new RuntimeException(curl_error($ch));
    }
    curl_close($ch);

    $data = json_decode($response, true);
    return $data['content'][0]['text'] ?? '';
}

echo ezai_chat('Write a haiku about cURL.');

That's the whole integration. Swap claude-sonnet-4-5 for gpt-5, gemini-2.5-pro, or grok-4 and the same code calls a different provider. The response shape follows Anthropic's Messages API — see our docs for the field list.

Option 2: Guzzle

PHP request flow through EzAI

One endpoint, any model — EzAI routes based on the model field in your request

Guzzle gives you async, connection pooling, and cleaner error handling. Install it with Composer:

bash
composer require guzzlehttp/guzzle
php
<?php
use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://ezaiapi.com',
    'headers' => [
        'x-api-key' => $_ENV['EZAI_API_KEY'],
        'anthropic-version' => '2023-06-01',
    ],
    'timeout' => 60,
]);

$response = $client->post('/v1/messages', [
    'json' => [
        'model' => 'claude-sonnet-4-5',
        'max_tokens' => 2048,
        'system' => 'You are a terse senior PHP developer.',
        'messages' => [
            ['role' => 'user', 'content' => 'Refactor this loop: for($i=0;$i<count($a);$i++){...}'],
        ],
    ],
]);

$data = json_decode($response->getBody(), true);
echo $data['content'][0]['text'];

Guzzle also supports $client->postAsync() which returns a Promise — useful when you're batching multiple prompts in parallel. See our post on parallel model requests for the pattern.

Option 3: Laravel HTTP Client

On Laravel 10+ the facade reads cleanest. Drop this into an action or service class:

php
<?php
namespace App\Services;

use Illuminate\Support\Facades\Http;

class Ezai
{
    public function ask(string $prompt, string $model = 'claude-sonnet-4-5'): string
    {
        $response = Http::withHeaders([
                'x-api-key' => config('services.ezai.key'),
                'anthropic-version' => '2023-06-01',
            ])
            ->timeout(60)
            ->retry(3, 200)
            ->post('https://ezaiapi.com/v1/messages', [
                'model' => $model,
                'max_tokens' => 1024,
                'messages' => [['role' => 'user', 'content' => $prompt]],
            ])
            ->throw();

        return $response->json('content.0.text');
    }
}

retry(3, 200) handles transient 429/5xx responses automatically — much safer than naked requests in production. Pair this with a queued job (ShouldQueue) for long generations so your web workers don't block.

Streaming Responses

Long completions feel snappier when you stream them. Set stream: true and read the SSE body incrementally. With cURL's CURLOPT_WRITEFUNCTION, it's a callback per chunk:

php
<?php
$ch = curl_init('https://ezaiapi.com/v1/messages');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'x-api-key: ' . getenv('EZAI_API_KEY'),
        'anthropic-version: 2023-06-01',
        'content-type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'model' => 'claude-sonnet-4-5',
        'max_tokens' => 1024,
        'stream' => true,
        'messages' => [['role' => 'user', 'content' => 'Explain PSR-7 in 3 bullets.']],
    ]),
    CURLOPT_WRITEFUNCTION => function ($ch, $chunk) {
        foreach (explode("\n", $chunk) as $line) {
            if (str_starts_with($line, 'data: ')) {
                $event = json_decode(substr($line, 6), true);
                if (($event['type'] ?? '') === 'content_block_delta') {
                    echo $event['delta']['text'] ?? '';
                    flush();
                }
            }
        }
        return strlen($chunk);
    },
]);
curl_exec($ch);
curl_close($ch);

In Laravel, wrap this in a StreamedResponse and you've got a ChatGPT-style typing effect in your Blade view. We cover the full pattern in streaming AI responses.

OpenAI Format, Too

If you'd rather use the OpenAI-style openai-php/client SDK, EzAI also speaks that dialect at /v1/chat/completions. Point the SDK's base URL at EzAI and it works identically — no code changes beyond the URL. That's handy if you're migrating from OpenAI to Claude without wanting to rewrite your client layer.

Production Checklist

  • Keep keys out of code. Use .env + config/services.php — see securing API keys.
  • Set timeouts. 60 seconds for normal completions, 300+ for long extended-thinking calls.
  • Retry on 429/5xx. Laravel's ->retry() or Guzzle's retry middleware handles it cleanly.
  • Queue heavy calls. Don't block PHP-FPM workers on 30-second completions; push to Redis/Horizon.
  • Log token counts. The response includes usage.input_tokens and usage.output_tokens — store them per user for cost attribution.

Wrapping Up

PHP doesn't need a dedicated SDK to talk to modern AI models. A single POST request to EzAI gives you Claude, GPT, Gemini, and Grok behind one key, with token tracking on the dashboard. Start with cURL for scripts, move to Guzzle or Laravel HTTP for apps, and turn on streaming when your prompts get long.

Already on another stack? Check our guides for Go, Ruby, and TypeScript.


Related Posts