Sending Chat Requests to OpenRouter.ai with the Simplified OpenRouter PHP SDK
![]() |
Learn how the Simplified OpenRouter PHP SDK lets you tap hundreds of AI models through one unified API. Install with Composer, send a chat request in a few lines, and switch models—GPT, Claude, and more—by changing a single string. Shameless Plug: check out my new book, co-authored with the real-world knowledgeable author Rainier Sarabia: https://www.packtpub.com/en-us/product/php-programming-in-the-ai-era-9781835889930 |
Providing Context
In the processing of reviewing my (far too many!) subscriptions, I happened across openerouter.ai. Please bear in mind that I'm not paid by them, nor am I endorsing them. However, what drew me to this platform was due to the increasing restrictions of my current AI platforms. A phrase commonly used for what I've experienced it vendor lock-in. What I was specifically looking for was a good way to cut costs without sacrificing quality. In short, I wanted to gain access to the spectrum of new Chinese AI models.
But, to my horror, I noted that openrouter.ai lacks a PHP SDK. The nerve of them! Leaving out a programming language that's mature and stable, and runs on around 75% of the servers on the Internet! So, like any good PHP developer, hyped on AI-assistants, took the challenge to heard, and had Claude Code help me write an SDK. (Only to belatedly discover that one (well, several, actually), already existed on packagist.org. Ah well (mental shrug of the shoulders), at least the good news is that my SDK, introduced in this blog, is slim and trim, and doesn't try to do everything that the "official" (Python -- GAG) SDK does.
One Gateway, Every Model
If you build with large language models, you have probably felt the friction of provider lock-in. You wire your application to OpenAI, then a client asks for Anthropic's Claude, and later your team wants to experiment with a cheaper open-weight model from DeepSeek or Alibaba. Each vendor has its own SDK, its own authentication scheme, and its own quirks.
OpenRouter.ai solves this problem via its unified API gateway that sits in front of hundreds of models from dozens of providers. You authenticate once, you learn one request format, and you gain access to the entire catalog through a single endpoint. OpenRouter handles routing, fallbacks, and billing, so switching from one model to another becomes a matter of changing a single string in your request. For PHP developers, though, there has historically been a gap: OpenRouter's officially generated SDKs don't include PHP.
Enter the Simplified OpenRouter PHP SDK
That gap is what the simplified_openrouter_php_sdk by Doug Bierer (i.e. me -- but let us not forget Claude!) fills. It is an unofficial, hand-written, idiomatic PHP client modeled after OpenRouter's official Python SDK — but deliberately trimmed down. Where the Python SDK is auto-generated from OpenRouter's OpenAPI spec and covers roughly 90 endpoint groups (TTS/STT, video generation, OAuth, workspaces, datasets, guardrails, and more), this PHP SDK focuses on the subset most developers actually reach for:
- Chat Completions: including streaming
- Models: list, get, and count
- Endpoints: see which providers serve a given model
- Generations: usage and cost metadata lookup
- Credits: account balance
- API Keys: full CRUD (management key required)
- Embeddings: foundation for Retrieval Augmented Generation (RAG)
The SDK leverages the Guzzle HTTP client
and produces PSR-7 compliant requests and responses. Its minified
architecture includes a single Transport class plus one resource
class per endpoint group, making it easy to extend if you later need
something more exotic.
One important caveat: at the time of writing, this is an
Alpha release. It is excellent for learning,
prototyping, and internal tooling. If you need something battle-hardened
for production, the README itself points you toward the more mature
eatzy/openrouter-php-sdk as an alternative.
Requirements and Installation
The SDK requires PHP 8.1+ and Composer. Installation is a one-liner:
composer require unlikelysource/simplified-openrouter-php-sdk
You will also need an OpenRouter API key, which you can generate from
your OpenRouter dashboard. (The key looks somethihg like this: sk-or-....)
The SDK will read it automatically from the
OPENROUTER_API_KEY environment variable, or you can pass it
explicitly when constructing the client. The latter is convenient for
quick scripts, while the environment variable is safer for anything
committed to a repository.
Sending Your First Chat Request
Here is the complete flow for a single text chat request. Notice how little ceremony is involved:
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();Three things are worth highlighting. First,
$client->chat->create() accepts the request body as a
plain associative array that maps directly to the
OpenRouter Chat Completions API. Nothing is hidden behind a rigid data
object, so any parameter the API supports — temperature,
max_tokens, tools, tool_choice,
provider, reasoning,
response_format, and so on — can be passed straight
through.
Second, the messages array can contain either plain
arrays or the expressive ChatMessage helpers:
ChatMessage::system(), ::user(),
::assistant(), and ::tool(). These make your
intent readable at a glance.
Third, $response->getContent() gives you the model's
reply as a string, so you can echo it, store it, or return it from an
API endpoint immediately.
Switching Models
This is where OpenRouter and the SDK truly shine. To swap models, you
change exactly one value — the model string. Everything
else about your code stays identical:
// GPT-4o mini (fast, inexpensive)
$response = $client->chat->create([
'model' => 'openai/gpt-4o-mini',
'messages' => [ChatMessage::user('Summarize PHP 8.3 in one sentence.')],
]);
// Switch to Claude — same code, different model string
$response = $client->chat->create([
'model' => 'anthropic/claude-3.5-sonnet',
'messages' => [ChatMessage::user('Summarize PHP 8.3 in one sentence.')],
]);Because the model identifier is just a string, you can drive it from configuration, a database, or a user-facing dropdown. If you are not sure which models are available, query using the SDK as follows:
$models = $client->models->list(['limit' => 20, 'category' => 'programming']);
foreach ($models as $model) {
echo "{$model->id}: {$model->contextLength} tokens\n";
}Streaming Responses
For chat UIs, you rarely want users staring at a spinner. The SDK supports token-by-token streaming with the same simple shape:
$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 that lazily parses OpenRouter's
text/event-stream response and yields one chunk per server-sent event, stopping automatically at the [DONE] sentinel.
Robust by Default
The client is configurable for real-world conditions. You can set a custom baseUrl
(for example, https://eu.openrouter.ai/api/v1 for EU in-region routing),
pass an HTTP-Referer and X-Title for
OpenRouter's app rankings, tune timeouts, and configure automatic
retries via RetryConfig. Error handling is idiomatic too:
failed requests throw a subclass of ApiException, such as
UnauthorizedException,
PaymentRequiredException, or
TooManyRequestsException, allowing you to catch exactly what you need.
Wrapping Up
The Simplified OpenRouter PHP SDK delivers the 20% of OpenRouter's surface area that covers 80% of real work: chat, models, embeddings, and account management. Install it, drop in your API key, send a chat request, and switch models by editing one string. For prototyping and internal tools, it's a low-friction way to bring the entire OpenRouter model catalog into your PHP applications.
Here's the Amazon link to my new book, co-authored with the inscrutable and ever-knowledgeable author Rainier Sarabia: https://www.amazon.com/PHP-Programming-Era-applications-production-ready/dp/1835889921
