unlikelysource/simplified-openrouter-php-sdk

Unofficial PHP SDK for the OpenRouter.ai API, modeled after the official Python SDK.

Maintainers

Package info

github.com/dbierer/simplified_openrouter_php_sdk

Homepage

pkg:composer/unlikelysource/simplified-openrouter-php-sdk

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.0.1 2026-07-30 06:01 UTC

This package is auto-updated.

Last update: 2026-07-30 06:04:07 UTC


README

An unofficial PHP SDK for the OpenRouter.ai API, modeled after the official Python SDK. This is a hand-written, idiomatic PHP client, not a generated 1:1 port — covering the core of the OpenRouter API:

  • Chat Completions — including streaming
  • Models — list, get, count
  • Endpoints — list provider endpoints for a model
  • Generations — request/usage metadata lookup
  • Credits — account balance
  • API Keys — full CRUD (management key required)
  • Embeddings

The Python SDK is auto-generated from OpenRouter's OpenAPI spec and covers ~90 endpoint groups (TTS/STT, video generation, OAuth, workspaces, BYOK, datasets, guardrails, analytics, and more). This simplified PHP SDK, by design, covers only the subset most developers need. The architecture (a Transportclass plus one resource class per endpoint group) makes it straightforward to add more resources later if you need them. Contributions welcome!

This SDK leverages the Guzzle HTTP client, and produces PSR-7 compliant requests and responses. IMPORTANT: this is an Alpha release. If you need something more stable and production-ready, consider using eatzy/openrouter-php-sdk instead.

Requirements

Installation

composer require unlikelysource/simplified-openrouter-php-sdk

Quick start

use OpenRouter\Client;
use OpenRouter\DTO\ChatMessage;

// Reads OPENROUTER_API_KEY from the environment if not passed explicitly.
$client = new Client(apiKey: 'sk-or-...');

$response = $client->chat->create([
    'model' => 'openai/gpt-4o-mini',
    'messages' => [
        ChatMessage::system('You are a helpful assistant.'),
        ChatMessage::user('Say hello in three languages.'),
    ],
]);

echo $response->getContent();

Streaming

$stream = $client->chat->createStreamed([
    'model' => 'openai/gpt-4o-mini',
    'messages' => [ChatMessage::user('Count to 5.')],
]);

foreach ($stream as $chunk) {
    echo $chunk->getContentDelta();
}

createStreamed() returns a ChatCompletionStream, an IteratorAggregate that lazily parses the API's text/event-stream response and yields one ChatCompletionChunk per server-sent event, stopping automatically at the [DONE] sentinel.

Request parameters

chat->create() / chat->createStreamed() accept the request body as a plain associative array, matching the OpenRouter Chat Completions API directly (model, messages, temperature, max_tokens, tools, tool_choice, provider, reasoning, response_format, etc.) — nothing is hidden behind a rigid DTO, so any parameter the API supports can be passed through as-is. messages entries may be plain arrays or OpenRouter\DTO\ChatMessage instances (ChatMessage::system(), ::user(), ::assistant(), ::tool()).

Resources

// Models
$models = $client->models->list(['limit' => 20, 'category' => 'programming']);
foreach ($models as $model) {
    echo "{$model->id}: {$model->contextLength} tokens\n";
}
$model = $client->models->get('openai', 'gpt-4o');
$count = $client->models->count();

// Endpoints (which providers serve a given model)
$endpoints = $client->endpoints->forModel('openai', 'gpt-4o'); // raw decoded array

// Generations
$generation = $client->generations->get('gen-abc123');
echo $generation->totalCost;

// Credits (requires a management API key)
$credits = $client->credits->get();
echo $credits->getRemaining();

// API keys (requires a management API key)
$key = $client->apiKeys->create(['name' => 'my-app-key', 'limit' => 10.0]);
echo $key->key; // plaintext key — only ever available on the create() response
$client->apiKeys->update($key->hash, ['disabled' => true]);
$client->apiKeys->delete($key->hash);
foreach ($client->apiKeys->list() as $k) { /* ... */ }

// Embeddings
$result = $client->embeddings->create([
    'model' => 'openai/text-embedding-3-small',
    'input' => 'Hello, world!',
]);
$vector = $result->data[0]->vector;

See examples/ for complete runnable scripts.

Configuration

use OpenRouter\Client;
use OpenRouter\Http\RetryConfig;

$client = new Client(
    apiKey: 'sk-or-...',
    baseUrl: 'https://openrouter.ai/api/v1', // e.g. https://eu.openrouter.ai/api/v1 for EU in-region routing
    httpReferer: 'https://myapp.example',    // sent as HTTP-Referer, used for OpenRouter app rankings
    title: 'My App',                        // sent as X-Title
    timeoutSeconds: 60.0,
    retryConfig: new RetryConfig(maxAttempts: 4, initialIntervalMs: 500, maxIntervalMs: 60000),
);

You can also inject your own Guzzle client (e.g. with custom middleware or a mock handler for testing) via the httpClient constructor argument.

Error handling

Requests that receive an HTTP error status throw a subclass of OpenRouter\Exceptions\ApiException, mapped by status code (BadRequestException, UnauthorizedException, PaymentRequiredException, ForbiddenException, NotFoundException, TooManyRequestsException, InternalServerException, etc.):

use OpenRouter\Exceptions\ApiException;

try {
    $client->chat->create(['model' => 'openai/gpt-4o', 'messages' => [...]]);
} catch (ApiException $e) {
    echo $e->getStatusCode();  // e.g. 429
    echo $e->getMessage();     // the API's error message
    echo $e->getMetadata();    // provider-specific error metadata, if any
}

Connection-level failures (DNS, timeouts, refused connections) that persist after retries are exhausted throw OpenRouter\Exceptions\TransportException instead.

5xx responses are retried automatically with exponential backoff (defaults: up to 4 attempts, starting at 500ms, capped at 60s, 1.5x multiplier) before an exception is raised.

Development

composer install
composer test

License

MIT. Not affiliated with or endorsed by OpenRouter.