We use cookies. Cookies and browser storage keep you signed in and remember your preferences. With your permission we also measure how the site is used. You can any time. See our cookie policy.

PHP

SDK guides

PHP

Use api14 from PHP with the openai-php client package, with setup, chat completion, streaming and error handling examples you can copy.

Use openai-php/client with api14. Build the client with the factory, set the base URI to api14, and use model ids in provider/model format.

Install

composer require openai-php/client guzzlehttp/guzzle
export API14_API_KEY="sk-api14-..."

Configure

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

$client = OpenAI::factory()
    ->withApiKey(getenv('API14_API_KEY'))
    ->withBaseUri(getenv('API14_BASE_URL') ?: 'https://api14.com/api/v1')
    ->withHttpClient(new \GuzzleHttp\Client(['timeout' => 120, 'connect_timeout' => 5]))
    ->make();

Keep the key in the environment (or your framework's .env), never in source. The Guzzle timeout is in seconds and covers the whole request; raise it for long reasoning or streaming calls.

Chat

$result = $client->chat()->create([
    'model' => 'openai/gpt-5-mini',
    'messages' => [
        ['role' => 'user', 'content' => 'Explain vector databases in two sentences.'],
    ],
]);

echo $result->choices[0]->message->content;

Streaming

$stream = $client->chat()->createStreamed([
    'model' => 'anthropic/claude-sonnet-4.5',
    'messages' => [['role' => 'user', 'content' => 'Write a haiku about APIs.']],
]);

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

See Streaming.

Tool calling

$tools = [[
    'type' => 'function',
    'function' => [
        'name' => 'get_weather',
        'description' => 'Current weather for a city',
        'parameters' => [
            'type' => 'object',
            'properties' => ['city' => ['type' => 'string']],
            'required' => ['city'],
        ],
    ],
]];
$messages = [['role' => 'user', 'content' => "What's the weather in Istanbul?"]];

while (true) {
    $response = $client->chat()->create(['model' => 'openai/gpt-5-mini', 'messages' => $messages, 'tools' => $tools]);
    $message = $response->choices[0]->message;
    if (empty($message->toolCalls)) {
        echo $message->content;
        break;
    }
    $messages[] = $message->toArray();
    foreach ($message->toolCalls as $call) {
        $args = json_decode($call->function->arguments, true);
        $messages[] = [
            'role' => 'tool',
            'tool_call_id' => $call->id,
            'content' => json_encode(getWeather($args['city'])), // your function
        ];
    }
}

See Tool calling.

Structured output

$result = $client->chat()->create([
    'model' => 'openai/gpt-5-mini',
    'messages' => [['role' => 'user', 'content' => 'Extract: Ada Lovelace, born 1815 in London.']],
    'response_format' => [
        'type' => 'json_schema',
        'json_schema' => [
            'name' => 'person',
            'strict' => true,
            'schema' => [
                'type' => 'object',
                'properties' => [
                    'name' => ['type' => 'string'],
                    'born' => ['type' => 'integer'],
                    'city' => ['type' => 'string'],
                ],
                'required' => ['name', 'born', 'city'],
                'additionalProperties' => false,
            ],
        ],
    ],
]);

$person = json_decode($result->choices[0]->message->content, true, flags: JSON_THROW_ON_ERROR);

See Structured outputs.

Image input

$image = base64_encode(file_get_contents('photo.png'));

$result = $client->chat()->create([
    'model' => 'google/gemini-2.5-flash',
    'messages' => [[
        'role' => 'user',
        'content' => [
            ['type' => 'text', 'text' => 'What is in this picture?'],
            ['type' => 'image_url', 'image_url' => ['url' => "data:image/png;base64,{$image}"]],
        ],
    ]],
]);

See Vision & files.

Embeddings

$result = $client->embeddings()->create([
    'model' => 'openai/text-embedding-3-small',
    'input' => ['The quick brown fox', 'Lorem ipsum'],
]);

echo count($result->embeddings[0]->embedding);

Speech

$audio = $client->audio()->speech([
    'model' => 'openai/gpt-4o-mini-tts',
    'input' => 'Hello from api14!',
    'voice' => 'alloy',
]);

file_put_contents('speech.mp3', $audio);

Errors

Gateway errors use the OpenAI shape, so the client raises OpenAI\Exceptions\ErrorException with the status and the machine code:

use OpenAI\Exceptions\ErrorException;

try {
    $client->chat()->create([
        'model' => 'openai/gpt-5-mini',
        'messages' => [['role' => 'user', 'content' => 'Hi']],
    ]);
} catch (ErrorException $e) {
    match ($e->getStatusCode()) {
        402 => error_log('Balance too low, add funds: ' . $e->getErrorCode()), // insufficient_balance
        429 => error_log('Rate limited, retry in 60s: ' . $e->getErrorCode()),  // rate_limited
        default => throw $e,
    };
}

The gateway sends Retry-After: 60 with rate_limited. See Rate limits and Errors.

Retries with backoff

openai-php/client does not retry on its own. Wrap calls in a small loop that retries rate_limited and 5xx, and gives up on everything else:

use OpenAI\Exceptions\ErrorException;
use OpenAI\Exceptions\TransporterException;

function withRetry(callable $fn, int $attempts = 5): mixed
{
    for ($i = 0; ; $i++) {
        try {
            return $fn();
        } catch (ErrorException $e) {
            $retryable = $e->getErrorCode() === 'rate_limited' || $e->getStatusCode() >= 500;
            if (!$retryable || $i + 1 >= $attempts) throw $e;
        } catch (TransporterException $e) { // network error or timeout
            if ($i + 1 >= $attempts) throw $e;
        }
        usleep((min(60, 2 ** $i) * 1000 + random_int(0, 1000)) * 1000); // backoff + jitter
    }
}

$result = withRetry(fn () => $client->chat()->create([
    'model' => 'openai/gpt-5-mini',
    'messages' => [['role' => 'user', 'content' => 'Hi']],
]));

daily_limit_reached and insufficient_balance are not retried, because waiting a few seconds does not fix them.

All capabilities

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api14.com/api/v1",
  apiKey: process.env.API14_API_KEY,
});

const result = await client.chat.completions.create({
  "model": "openai/gpt-5-mini",
  "messages": [
    {
      "role": "user",
      "content": "Explain vector databases in two sentences."
    }
  ]
});
console.log(result.choices[0].message.content);