The ML Platform Behind Uber's AI
Michelangelo AI: Uber's end-to-end machine learning platform.
Translation pending
A fast, accurate ZPL rendering API, hosted in the EU. Real vector PDFs, not images. Free test keys, €0.002 per live label.
Translation pending
Developer toolsPHPGitHub @jaapieaapie1OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Read-only mirror of clients/php from jaapieaapie1/zpl: PHP client for the Caretly API (caretly/caretly on Packagist)
Loading the full README.
Read the full README ↗Client for the Caretly API: render ZPL labels to PDF or PNG.
composer require caretly/caretly
If your project has no PSR-18 client yet, add one, for example:
composer require guzzlehttp/guzzle
# or
composer require symfony/http-client nyholm/psr7
use Caretly\Client;
$caretly = new Client(apiKey: 'zpl_test_…'); // or set CARETLY_API_KEY
$result = $caretly->render("^XA^FO50,50^A0N,50,50^FDHello^FS^XZ");
$result->saveTo('label.pdf');
echo $result->pageCount; // 1
echo $result->labelsBilled; // 0 for a test key
Get an API key in the console. Keys starting with
zpl_test_ are free and watermark every label with TEST; zpl_live_ keys are billed per label.
use Caretly\Format;
use Caretly\RenderOptions;
$png = $caretly->render($zpl, new RenderOptions(format: Format::Png, page: 2, dpmm: 12));
$png->saveTo('page-2.png');
// or the shorthand
$png = $caretly->renderPng($zpl, page: 2);
Each file starts from a fresh printer state; pages follow the order of the array.
$result = $caretly->renderFiles([
'shipping.zpl' => file_get_contents('shipping.zpl'),
'returns.zpl' => file_get_contents('returns.zpl'),
]);
$result->filePageCounts; // [2, 1]
$caretly = new Client(
apiKey: null, // falls back to getenv('CARETLY_API_KEY'); optional for self-hosted servers
baseUrl: 'https://caretly.dev',
maxRetries: 2, // retries for 429, 503 and connection errors (not timeouts)
timeout: 30.0, // seconds; see below
httpClient: null, // any Psr\Http\Client\ClientInterface
requestFactory: null, // Psr\Http\Message\RequestFactoryInterface
streamFactory: null, // Psr\Http\Message\StreamFactoryInterface
sleep: null, // Closure(float $seconds): void, used between retries
);
PSR-18 has no standard way to set a timeout. When you do not inject httpClient, the client
creates a Guzzle or Symfony client itself (whichever is installed) with timeout applied, and
otherwise falls back to php-http/discovery. When you inject your own client, configure its
timeout there.
| Option | Values | Default (server) | Meaning |
|---|---|---|---|
format | Format::Pdf, Format::Png | A PDF holds every page; a PNG is one page. | |
page | int ≥ 1 | 1 | The page shown in a PNG. |
dpmm | 6, 8, 12, 24 | 8 | Printer resolution in dots per millimetre. |
timings | bool | false | Return a Server-Timing header ($result->serverTiming). |
idempotencyKey | string, 1–255 chars | generated | A retry with the same key and content is billed once. |
compress | bool | false | Gzip the request body (needs ext-zlib). |
Invalid page, dpmm or idempotencyKey values throw \InvalidArgumentException before any request.
RenderResult is read-only:
| Property | Type | Meaning |
|---|---|---|
content | string | The PDF or PNG bytes. |
contentType | string | application/pdf or image/png. |
pageCount | int | Total pages across the input. |
filePageCounts | list<int> | Pages per file for renderFiles(), else empty. |
labelsBilled | ?int | Labels billed (0 for test keys); null without keys. |
rateLimit | ?RateLimit | limit and remaining of the burst; null when absent. |
serverTiming | ?string | Raw Server-Timing header, with timings. |
saveTo(string $path) writes the bytes to a file.
$caretly->health() returns true when the server answers GET /health with ok, and
false otherwise (it never throws).
Every runtime exception extends Caretly\Exception\CaretlyException.
This is the beginning of the README. Read the rest at the original. Read the full README ↗