Claude API в PHP: Guzzle и curl для обращения к Zukko

Как обращаться к Claude и GPT из PHP: Guzzle, встроенный curl, разбор ошибок, стриминг и интеграция в Laravel.

4 мин · · разработка

PHP по-прежнему держит большую часть сайтов и CRM-доработок, и подключить к ним Claude API несложно: это обычный HTTPS-запрос с JSON. Ниже — рабочие примеры для Zukko через Guzzle и через встроенные функции curl_*, обработка ошибок, потоковый ответ и вариант для Laravel. Запросы идут в OpenAI-совместимый эндпоинт https://api.zukko.pro/v1/chat/completions, поэтому тот же код подходит для Claude, GPT и GLM.

Способ 1: Guzzle

Guzzle — самый распространённый HTTP-клиент в PHP, он уже есть в Laravel и многих фреймворках.

bash
composer require guzzlehttp/guzzle
bash
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://api.zukko.pro/v1/',
    'timeout'  => 60,
]);

$response = $client->post('chat/completions', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('ZUKKO_API_KEY'),
    ],

Опция json сама сериализует массив и ставит заголовок Content-Type: application/json. Обратите внимание на base_uri: он заканчивается на /, а путь chat/completions — без ведущего слэша. Иначе Guzzle по правилам разрешения URL отбросит /v1 и запрос уйдёт не туда. По умолчанию Guzzle бросает исключение на ответах 4xx и 5xx — это используем ниже.

Способ 2: встроенный curl

Если ставить зависимости нельзя, хватит расширения curl, которое включено почти на любом хостинге:

bash
<?php

$apiKey = getenv('ZUKKO_API_KEY');

$payload = json_encode([
    'model'      => 'claude-sonnet-5',
    'max_tokens' => 500,
    'messages'   => [
        ['role' => 'user', 'content' => 'Объясни в трёх предложениях, что такое REST API'],
    ],
], JSON_UNESCAPED_UNICODE);

$ch = curl_init('https://api.zukko.pro/v1/chat/completions');
curl_setopt_array($ch, [

curl_exec возвращает false при сетевой ошибке (DNS, таймаут, TLS) — это нужно проверять отдельно от HTTP-статуса. Текст ошибки API лежит в поле error.message.

Обработка ошибок в Guzzle

Коды ответа подсказывают, что делать: 401 — неверный ключ, 402 — закончился баланс, 403 — модель не разрешена для ключа, 413 — слишком большой запрос, 429 — слишком часто, 503 — провайдер модели временно недоступен. Повторять имеет смысл только 429 и 503.

bash
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\ConnectException;

function askModel(Client $client, string $prompt, int $attempts = 3): string
{
    for ($i = 1; $i <= $attempts; $i++) {
        try {
            $response = $client->post('chat/completions', [
                'headers' => ['Authorization' => 'Bearer ' . getenv('ZUKKO_API_KEY')],
                'json'    => [

BadResponseException — общий родитель ClientException (4xx) и ServerException (5xx), поэтому один блок ловит обе группы. ConnectException — это сетевые сбои, когда ответа от сервера нет вовсе.

Стриминг ответа

Чтобы показывать текст по мере генерации, передайте "stream": true в теле запроса и опцию Guzzle 'stream' => true, чтобы он не ждал конца ответа. Сервер присылает строки data: {...} и в конце data: [DONE]; последний чанк может прийти с пустым choices.

bash
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.zukko.pro/v1/', 'timeout' => 120]);

$response = $client->post('chat/completions', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('ZUKKO_API_KEY')],
    'json'    => [
        'model'    => 'claude-sonnet-5',
        'stream'   => true,
        'messages' => [['role' => 'user', 'content' => 'Напиши короткое стихотворение про кота']],
    ],

Куски сети не совпадают с границами строк, поэтому данные копятся в $buffer и разбираются только целыми строками. Если скрипт отдаёт поток в браузер через PHP-FPM и nginx, проверьте, что буферизация вывода отключена, иначе текст придёт одним куском в конце.

Интеграция в Laravel

Ключ кладём в .env:

bash
ZUKKO_API_KEY=fetch_ваш_ключ
ZUKKO_API_URL=https://api.zukko.pro/v1

Дополняем config/services.php:

bash
'zukko' => [
    'key' => env('ZUKKO_API_KEY'),
    'url' => env('ZUKKO_API_URL', 'https://api.zukko.pro/v1'),
],

Контроллер на встроенном HTTP-клиенте Laravel (он работает поверх Guzzle):

bash
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class ChatController extends Controller
{
    public function chat(Request $request)
    {
        $validated = $request->validate(['message' => 'required|string|max:4000']);

        $response = Http::withToken(config('services.zukko.key'))

Ключ остаётся на сервере, а фронтенд получает только текст ответа. Не возвращайте клиенту весь ответ API целиком — в нём служебные поля, которые браузеру не нужны.

Сколько это стоит

Цена в Zukko считается по токенам. Пример: 1000 запросов, в каждом около 500 входных и 300 выходных токенов, всего 0,5M входных и 0,3M выходных:

МодельЦена вход/выход за 1MРасчётИтого
claude-sonnet-5$0.3 / $0.30,5 × 0,3 + 0,3 × 0,3$0.24
gpt-5.6-luna$0.1 / $0.10,5 × 0,1 + 0,3 × 0,1$0.08
glm-5.3-flash$0.04 / $0.040,5 × 0,04 + 0,3 × 0,04$0.032

Реальное число токенов смотрите в поле usage каждого ответа. Какая модель справится с вашей задачей, проверьте на своих данных: прогоните одинаковый набор запросов через две-три модели и сравните.

Частые вопросы

Где взять API-ключ?

Создайте ключ в кабинете. Полный ключ показывается один раз при создании — сразу сохраните его в .env или переменную окружения. Подробнее о заголовке авторизации — в разделе Authentication.

Как ограничить время ожидания?

В Guzzle — опции timeout (весь запрос) и connect_timeout (установка соединения). В curl — CURLOPT_TIMEOUT и CURLOPT_CONNECTTIMEOUT. В Laravel — метод timeout() у HTTP-клиента. Для длинных ответов ставьте 60–120 секунд или используйте стриминг.

Ошибка «SSL certificate problem»?

Значит, PHP не находит актуальный набор корневых сертификатов. Обновите пакет ca-certificates в системе или укажите путь к файлу сертификатов в php.ini (параметры curl.cainfo и openssl.cafile). Отключать проверку сертификата не стоит даже на тестовом сервере — так проще всего утащить ключ.

Можно ли использовать другие модели?

Да, поменяйте поле model: claude-opus-5-5, gpt-5.6-sol, gpt-5.6-luna, glm-5.3, glm-5.3-flash. Актуальный список — GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете, справка — в описании Chat Completions.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.