The official PHP client for the API2Convert file-conversion API. Convert, compress and transform images, documents, audio, video, ebooks, archives and CAD — and run operations like OCR, merge, thumbnail and website capture — in one line of code.
$client = new Api2Convert\Api2Convert('YOUR_API_KEY');
$client->convert('invoice.docx', 'pdf')->save('invoice.pdf');That single call creates a job, uploads your file, starts it, waits for it to finish and gives you back a result you can save. No polling loops, no manual upload handling.
composer require api2convert/sdk guzzlehttp/guzzleGet an API key from the API2Convert dashboard / documentation.
require 'vendor/autoload.php';
use Api2Convert\Api2Convert;
// Reads the API2CONVERT_API_KEY environment variable when no key is passed.
$client = new Api2Convert('YOUR_API_KEY');
// 1) From a local file
$client->convert('photo.png', 'jpg')->save('photo.jpg');
// 2) From a URL
$client->convert('https://example.com/photo.png', 'jpg')->save('photo.jpg');
// 3) With conversion options (discover them via $client->options('jpg'))
$client->convert('photo.png', 'jpg', [
'quality' => 85, 'width' => 1280, 'height' => 720,
])->save('out/'); // the processed file directory convert($input, $to, $options = []) — $input is a local path, a public URL, or an open
stream/resource; $to is the target format; $options are the conversion options for
that target. Less-common controls are named arguments: category, timeout, outputIndex,
filename, downloadPassword. The returned ConversionResult lets you:
$result = $client->convert('report.docx', 'pdf');
$result->save('report.pdf'); // stream to a file
$result->save('downloads/'); // ...or a directory (keeps the server filename)
$content = $result->contents(); // ...or get the raw bytes
$url = $result->url(); // ...or just the download URLPass downloadPassword and the output is locked behind it. The SDK remembers the password and
sends it automatically when you download — you don't pass it again:
$result = $client->convert('statement.docx', 'pdf', downloadPassword: 'hunter2');
$result->save('statement.pdf'); // the password is applied for youThe download URL still needs the password from anywhere else (a browser, cURL, another process),
via the X-Oc-Download-Password header. When you already hold an OutputFile — e.g. from the Jobs
API — hand the password to download():
$client->download($output, 'hunter2')->save('out/');For long-running jobs, start the conversion and get notified via a webhook instead of waiting:
$job = $client->convertAsync('movie.mov', 'mp4', callback: 'https://your-app.example.com/webhooks/api2convert');In your webhook handler, verify and parse the callback:
use Api2Convert\Api2Convert;
use Api2Convert\Exception\SignatureVerificationException;
$payload = file_get_contents('php://input'); // the RAW body
$signature = $_SERVER['HTTP_X_OC_SIGNATURE'] ?? null;
try {
$event = Api2Convert::webhooks()->constructEvent($payload, $signature, 'YOUR_WEBHOOK_SECRET');
$job = $event->job;
// … react to $job->status->code …
} catch (SignatureVerificationException $e) {
http_response_code(400);
}Signed webhooks are being rolled out. Until they are enabled for your account no signature is sent — call
Api2Convert::webhooks()->parse($payload)(or pass an empty secret) to deserialize the callback without verifying.
Every failure is a typed exception extending Api2Convert\Exception\Api2ConvertException:
use Api2Convert\Exception\ConversionFailedException;
use Api2Convert\Exception\RateLimitException;
use Api2Convert\Exception\ValidationException;
use Api2Convert\Exception\AuthenticationException;
try {
$client->convert('photo.png', 'jpg')->save('photo.jpg');
} catch (ValidationException $e) {
// bad target / option — $e->getMessage() explains
} catch (AuthenticationException $e) {
// bad or missing API key
} catch (RateLimitException $e) {
// too many requests — retry after $e->retryAfter seconds
} catch (ConversionFailedException $e) {
// the job failed — inspect $e->errors()
}| Exception | When |
|---|---|
AuthenticationException |
401 / 403 — bad or missing key |
PaymentRequiredException |
402 — no remaining quota |
ValidationException |
400 — invalid request (e.g. unknown target) |
NotFoundException |
404 — resource doesn't exist |
RateLimitException |
429 — exposes ->retryAfter |
ServerException |
5xx |
ConversionFailedException |
the job reached failed; exposes ->job and ->errors() |
TimeoutException |
the job didn't finish within the poll timeout |
SignatureVerificationException |
a webhook payload failed verification |
Transient failures (429, 5xx, network errors) are retried automatically with exponential backoff.
convert() is sugar over the Jobs API. Drop down to it for compound jobs, merges, presets, custom
polling or job chaining:
$job = $client->jobs()->create([
'process' => false,
'conversion' => [['target' => 'pdf', 'options' => ['pdf_a' => true]]],
]);
$client->jobs()->upload($job, 'contract.docx'); // local file
$client->jobs()->addInput($job->id, [ // ...or a URL
'type' => 'remote', 'source' => 'https://example.com/appendix.docx',
]);
$client->jobs()->start($job->id);
$done = $client->jobs()->wait($job->id, timeoutSeconds: 120);
foreach ($done->output as $output) {
$client->download($output)->save('out/');
}Available resources: jobs(), conversions() (the catalog + option discovery), presets(),
stats(), contracts().
Discover the valid options for any target:
$options = $client->options('jpg'); // → { quality: {...}, width: {...}, ... }$client = new Api2Convert('YOUR_API_KEY', [
'timeout' => 30, // per-request network timeout (seconds)
'maxRetries' => 2, // automatic retries for transient failures
'pollInterval' => 1.0, // first poll interval when waiting (seconds)
'pollMaxInterval' => 5.0, // backoff cap (seconds)
'pollTimeout' => 300, // give up waiting after this many seconds
]);Bring your own PSR-18 client (e.g. Symfony HttpClient) by passing it as the third argument.
- Never hard-code or commit your API key. Load it from the environment (
API2CONVERT_API_KEY) or a secrets manager. - In CI, store it as a masked & protected variable (this repo's pipeline reads
$API2CONVERT_API_KEY) and never print it to logs. - Treat the per-job upload token and your webhook signing secret with the same care.
- The SDK never logs your key/token and never puts them in exception messages.
- If a key is ever exposed, revoke and rotate it in the API2Convert dashboard immediately.
composer install
composer check # phpcs + phpstan + phpunitLive conformance tests run against the real API when API2CONVERT_API_KEY is set:
API2CONVERT_API_KEY=... vendor/bin/phpunit --testsuite liveThis SDK is hand-written and kept in sync with the API by an AI agent — see AGENTS.md
and docs/SDK_CONTRACT.md. Notable changes are recorded in
docs/CHANGELOG.md.
MIT — see LICENSE.