wavespeedai / wavespeed-php
WaveSpeed PHP Client โ Official PHP SDK for the WaveSpeed inference platform
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-23 10:09:15 UTC
README
WaveSpeed PHP SDK
PHP SDK for the WaveSpeed inference platform
๐ Visit wavespeed.ai โข ๐ Documentation โข ๐ฌ Issues
A port of the official WaveSpeed Python SDK
to PHP, with the same API surface and the same retry semantics. Zero runtime
dependencies โ just ext-curl and ext-json.
Requirements
- PHP 8.1 or newer
ext-curl,ext-json
Installation
composer require wavespeedai/wavespeed-php
API Client
Run WaveSpeed AI models with a simple API:
use WaveSpeed\WaveSpeed; $output = WaveSpeed::run( 'wavespeed-ai/z-image/turbo', ['prompt' => 'Cat'], ); echo $output['outputs'][0]; // Output URL
Authentication
Set your API key through the environment (get one at wavespeed.ai/accesskey):
export WAVESPEED_API_KEY="your-api-key"
Or pass it directly:
use WaveSpeed\Client; $client = new Client(apiKey: 'your-api-key'); $output = $client->run('wavespeed-ai/z-image/turbo', ['prompt' => 'Cat']);
Or configure it once during bootstrap, so every client picks it up:
use WaveSpeed\Config; Config::set(Config::API_KEY, $_ENV['WAVESPEED_API_KEY']);
Options
$output = $client->run( 'wavespeed-ai/z-image/turbo', ['prompt' => 'Cat'], timeout: 300.0, // Max wait for completion; null (default) = no local limit pollInterval: 1.0, // Status check interval (default: 1.0) enableSyncMode: false, // Best-effort sync result attempt (default: false) maxRetries: null, // Task-level retries (default: the client's setting) );
timeout caps how long run() blocks. Left null it waits as long as the task
takes, while each individual HTTP request stays bounded by Config::TIMEOUT
(36000s by default). A local timeout never cancels the task โ see
Recovering a Task.
Sync Mode
Pass enableSyncMode: true to ask the API to wait for the result in the initial
request. If the server-side wait runs out, the SDK throws
SyncModeTimeoutException carrying the task ID and result URL โ the task keeps
processing and can be collected later.
Note: Not all models support sync mode. Check the model documentation for availability.
use WaveSpeed\Exception\SyncModeTimeoutException; try { $output = $client->run( 'wavespeed-ai/z-image/turbo', ['prompt' => 'Cat'], enableSyncMode: true, ); } catch (SyncModeTimeoutException $e) { // Not a failure โ the task is still running. $state = $client->getResult((string) $e->taskId); }
Handling Failure Without Exceptions
runNoThrow() reports the outcome instead of throwing, which suits queue
workers that want to record a task ID and move on:
$result = $client->runNoThrow('wavespeed-ai/z-image/turbo', ['prompt' => 'Cat']); // ['status' => 'completed'|'failed'|'processing', // 'outputs' => list|null, 'task_id' => string, 'error' => string|null] if ($result['outputs'] !== null) { echo $result['outputs'][0]; } else { error_log("{$result['status']} ({$result['task_id']}): {$result['error']}"); }
status is processing when a sync-mode wait expired: the task is alive
server-side, so getResult($result['task_id']) will pick it up.
Recovering a Task
A local timeout does not cancel anything โ the job keeps running. Fetch its current state by id:
$state = $client->getResult('task-id-from-earlier'); echo $state['data']['status']; // e.g. "processing" or "completed" print_r($state['data']['outputs'] ?? []);
Retry Configuration
use WaveSpeed\Client; $client = new Client( apiKey: 'your-api-key', maxRetries: 0, // Replacement task attempts (default: 0) maxConnectionRetries: 5, // Result-query GET retries; POST is never retried retryInterval: 1.0, // Base delay between retries in seconds (default: 1.0) );
A submission POST is never retried automatically. It is not idempotent: if
it fails without a response, the task may already exist, and replaying it would
risk paying for the same job twice. Result-query GETs are idempotent and are
retried freely. Setting maxRetries above 0 opts into submitting a
replacement task after a confirmed transient failure.
Diagnostics
Retries are silent by default โ a library should not write to your output uninvited. Opt in with a logger:
use WaveSpeed\Client; use WaveSpeed\Support\CallableLogger; use WaveSpeed\Support\StderrLogger; $client = new Client(logger: new StderrLogger()); // Or bridge to PSR-3 / any framework logger: $client = new Client( logger: new CallableLogger(fn (string $m) => $psrLogger->warning($m)), );
Upload Files
Upload images, videos, or audio files. Large files stream straight from disk, so memory use stays flat:
use WaveSpeed\WaveSpeed; $url = WaveSpeed::upload('/path/to/image.png'); echo $url; // A stream works too โ including one you did not open from disk. $url = WaveSpeed::upload($stream);
Errors
Every exception implements WaveSpeed\Exception\WaveSpeedException, so one
catch covers the SDK without swallowing unrelated errors:
| Exception | Meaning | Retried by the SDK |
|---|---|---|
ConfigurationException |
No API key, or an unusable argument | โ |
FileNotFoundException |
Upload path missing or unreadable | โ |
SubmissionException |
The submit POST failed | Never (not idempotent) |
PredictionFailedException |
Task reached failed / cancelled / timeout |
Never (a verdict, not a hiccup) |
SyncModeTimeoutException |
Sync wait expired; task still running | Never โ poll instead |
TimeoutException |
Local wait budget ran out | Yes, at the task level |
ConnectionException |
No response obtained | Yes, for idempotent requests |
ApiException |
Any other API-level failure | Yes for 429/5xx |
Custom HTTP Transport
Client talks to the API only through the HttpClient interface, so you can
substitute your own stack (a proxy-aware handler, a shared connection pool, a
test double):
use WaveSpeed\Client; use WaveSpeed\Http\CurlHttpClient; $client = new Client( http: new CurlHttpClient([CURLOPT_PROXY => 'http://proxy.internal:3128']), );
Examples
Runnable scripts live in examples/:
WAVESPEED_API_KEY=your-key php examples/run.php "a lighthouse at dusk"
WAVESPEED_API_KEY=your-key php examples/upload.php /path/to/image.png
WAVESPEED_API_KEY=your-key php examples/sync_mode.php
WAVESPEED_API_KEY=your-key php examples/run_no_throw.php
WAVESPEED_API_KEY=your-key php examples/configuration.php
Local Development
composer install # Run all tests composer test # Run one file, or one test vendor/bin/phpunit tests/ClientTest.php vendor/bin/phpunit --filter testRunReturnsOutputs # Static analysis (level 8) composer stan # Tests that hit the real API (excluded by default, costs credits) WAVESPEED_API_KEY=your-key vendor/bin/phpunit --group integration
The default suite never touches the network: MockHttpClient scripts the
responses and FakeClock makes polling and backoff instant.
Environment Variables
| Variable | Description |
|---|---|
WAVESPEED_API_KEY |
WaveSpeed API key |
WAVESPEED_CLIENT_NAME |
Channel-attribution name sent as the X-Client-Name header (overrides the clientName argument; defaults to wavespeed-php) |
Mapping From the Python SDK
| Python | PHP |
|---|---|
wavespeed.run(...) |
WaveSpeed::run(...) |
wavespeed.run_no_throw(...) |
WaveSpeed::runNoThrow(...) |
wavespeed.get_result(id) |
WaveSpeed::getResult($id) |
wavespeed.upload(file) |
WaveSpeed::upload($file) |
wavespeed.Client(api_key=...) |
new WaveSpeed\Client(apiKey: ...) |
wavespeed.config.api.timeout = 60 |
Config::set(Config::TIMEOUT, 60.0) |
with wavespeed.config.patch(...) |
Config::patch([...], fn () => ...) |
RuntimeError / ValueError |
Typed exceptions (see the table above) |
License
MIT
WaveSpeed AI โ AI image & video generation platform. Try it in the browser: Image generator ยท Video generator