Public API
api.zplcloud.com for rendering designs, converting and checking ZPL, enriching carrier labels, running batch jobs and reading printers, agents and quota. The rendering, tool, enrichment, batch, printer, agent and account endpoints can call one of your webhooks when they have finished - signed, retried and, if you ask for it, with the result.
Base URL and authentication
| Item | Detail |
|---|---|
| Base URL | https://api.zplcloud.com - paths start with /v1, there is no /api prefix. |
| Header | X-API-Key: sk_zplcloud_… |
| Basic Auth | API key as user name, empty password: curl -u sk_zplcloud_…: |
| Key prefixes | sk_zplcloud_ for normal keys, sk_sandbox_ for sandbox keys. |
| Creating keys | Platform → API Keys (/platform/api). Keys are always generated server-side. The full key is shown only once, right after creating it - zplCloud stores only a SHA-256 hash, so a lost key cannot be recovered: create a new one and delete the old one. A key belongs to the user who created it and, if that user is in a company, to the company. |
| Language | Always English: error messages, hints and field names. |
| Reference | api.zplcloud.com/scalar - interactive reference with request builder for every public endpoint, grouped by topic (see the endpoint index below). |
What a key can reach follows its owner: designs, printers, agents, webhooks and batch jobs of the key's company - or of the owner's e-mail for keys without a company. Anything outside that scope is treated as not found.
Sandbox keys
Sandbox keys (sk_sandbox_…) are free and consume no renders, but they only work for /v1/virtual-printers, whose output is a watermarked PNG. Every other /v1 path answers 403 with a hint. Use a normal key for everything on this page.
Conventions
- Requests: JSON bodies with camelCase field names and
Content-Type: application/json. Unknown fields are ignored. - Binary input: PDFs and images are base64 strings inside the JSON body. Data URIs such as
data:application/pdf;base64,JVBERi0x…are accepted. - Responses: files (PDF, PNG, JPG, SVG, raw ZPL) come back as the response body with the matching
Content-Type; everything else is JSON. - Request ID: every response of a
/v1endpoint carriesX-ZplCloud-Request-Id(GUID) - log it and quote it in support requests. On the endpoints with webhook support the same ID also appears asrequestIdin error bodies and in webhook callbacks. - Timestamps are UTC. Values read from the database are written without a trailing
Z(2026-09-11T08:12:44.193) - treat them as UTC. - Webhook callback: the query parameters
webhookIdandwebhookResultwork on design rendering as PDF and PNG, the ZPL tools, label enrichment, batch jobs, the printer and agent lists, quota and webhooks - not on the endpoints under Other endpoints. See Webhook callback.
Errors
Errors are JSON with a readable error, usually a hint, and - on the endpoints with webhook support - the requestId. Some errors add fields: limit, used and requested on 429, plan on 403 when an endpoint needs the Pro plan, tool on tool errors, jobId on failed batch jobs.
Requests rejected before any processing - missing or invalid API key, unknown or disabled webhookId, sandbox key - return error and hint only, without requestId in the body. The 403 for a sandbox key is sent before the endpoint runs and has no X-ZplCloud-Request-Id header either.
Status codes
| Code | Meaning |
|---|---|
| 200 | Success. POST /v1/batch/design/{designId} with "async": true answers 202 Accepted. |
| 400 | Invalid JSON or field values, invalid designId, unknown or disabled webhookId, webhookResult=true without webhookId. |
| 401 | API key missing or invalid. |
| 403 | Sandbox key used outside /v1/virtual-printers, or the key owner is not on the Pro plan, which batch jobs and label enrichment require. |
| 404 | Design, batch job or printer not found in the key owner's scope; label enrichment not enabled. |
| 413 | Body or input too large: body size, number of records, characters, file size, pages. |
| 429 | Label quota used up - only design renders (ZPL, PDF, PNG) and batch jobs. |
| 499 | The client closed the connection before the request finished (shows up in logs and statistics, not in your client). |
| 503 | Temporarily unavailable (database), also when the plan cannot be read for a Pro endpoint. Retry with backoff. |
Label quota and render counting
Labels are counted per account - the e-mail of the API key's owner - per calendar month in UTC. It is the same counter the platform uses, so labels from the designer, print views and the API add up. The counter starts again at 0 on the 1st of each month at 00:00 UTC.
| Plan | Labels per month | Batch jobs and label enrichment |
|---|---|---|
| Developer (free) | 100 | No - 403 |
| Starter | 10,000 | No - 403 |
| Pro | 100,000 | Yes |
| Render top-ups | Added to the plan limit (bought under Billing in the platform). | - |
Everything else - design rendering as ZPL, PDF and PNG, the ZPL tools, printer and agent lists, quota and webhooks - works on every plan. POST /v1/batch/design/{designId} and POST /v1/enrich/zpl first check the plan of the key owner (GET /v1/quota → plan) and answer other plans with 403, or with 503 if the plan cannot be read. Status, output and pick list of a batch job (GET /v1/batch/jobs/…) are not plan-checked.
What is limited and what is counted
| Endpoint | Quota check | Renders counted |
|---|---|---|
POST /v1/zpl/render/design/{designId} | Yes - 429 | 1 per record (at least 1) |
POST /v1/pdf/render/design/{designId} | Yes - 429 | 1 per page (= record, at least 1) |
POST /v1/png/render/design/{designId} | Yes - 429 | 1 |
POST /v1/batch/design/{designId} | Yes - 429 (Pro plan only) | 1 per record |
POST /v1/tools/zpl-to-pdf | Never blocked | 1 per label in the ZPL |
POST /v1/tools/pdf-to-zpl | Never blocked | 1 per converted page |
All other /v1/tools/{slug} | Never blocked | 1 per call |
POST /v1/enrich/zpl | Never blocked (Pro plan only) | 1 per enriched label |
| Batch job status, output and pick list; printers, agents, quota, webhooks | No | 0 |
- The check runs before anything is rendered: if
used + requestedexceeds the limit, the request gets429and nothing is counted. - ZPL tools and label enrichment have no rate limit and keep working with the quota used up. Their renders still count - they can push the counter over the limit, and the next design render or batch job then gets
429. - Failed requests (status 400 and above) count nothing.
- Counting is buffered per account: limit and usage are cached for 60 seconds, new renders count in memory at once and are written to the database every 5 seconds.
GET /v1/quotaand the429check therefore see new API renders immediately and labels rendered in the platform after at most 60 seconds; the platform dashboard can lag a few seconds behind. - API usage also appears in the API statistics on the dashboard.
Current numbers: GET /v1/quota.
Webhook callback
The endpoints in the index - all except Other endpoints - accept two optional query parameters. With them, zplCloud calls one of your stored webhooks when the request has finished - no polling, and for async batch jobs no open connection. The callback runs through the regular webhook system: signed with the webhook secret, retried on failure and listed in the delivery log in the platform.
Parameters
| Parameter | Value | Meaning |
|---|---|---|
webhookId | Number | ID of a stored webhook (where to find it). It must be active and belong to the key owner: the key's company, or the owner's e-mail for keys without a company. |
webhookResult | true / false (default false) | true puts the result - PDF, PNG, ZPL, JSON - into the callback. Requires webhookId. |
When the callback fires
- After the request has finished - on success and on failure. Validation errors (400), missing Pro plan (403), not found (404), too large (413) and quota (429) are reported with
status: "failed", the HTTP status and the error message. - Only after authentication and webhook validation. An invalid key (401), a sandbox key (403), an unknown or disabled
webhookIdorwebhookResult=truewithoutwebhookId(400) are answered immediately, before any work, and trigger no callback. Neither do aborted requests (499) or a503during authentication. - Independent of the webhook's event subscriptions -
webhookIdaddresses the webhook directly. - Response header
X-ZplCloud-Webhook:queued= the callback is stored for delivery (usually delivered within seconds),failed= it could not be queued, e.g. because the webhook was disabled in the meantime. The header is only set whenwebhookIdwas given. - Async batch jobs (
"async": true): the202response has noX-ZplCloud-Webhookheader but awebhookobject (id,includeResult,event). The callbackapi.batch.completedfollows when the job has finished; itsrequestIdis thejobId.
Payload
zplCloud sends a POST with a JSON body. Example: design rendered as PDF with webhookResult=true:
Without webhookResult=true, and on every failure, there is no result: resultIncluded is false. Example: batch job rejected because the quota is used up:
| Field | Meaning |
|---|---|
event | Event name (table below), also sent as header X-ZplCloud-Event. |
timestamp | UTC time the request finished (ISO 8601, seconds). |
requestId | Same GUID as the X-ZplCloud-Request-Id response header (async batch jobs: the jobId). Match callbacks to calls with it and drop duplicates. |
operation | HTTP method and route template, e.g. POST /v1/tools/zpl-to-pdf or GET /v1/batch/jobs/{jobId}. |
status | succeeded (HTTP status below 400) or failed. |
httpStatus | Status code of the API response. Async batch jobs: the status the job finished with (500 if it was aborted). |
durationMs | Processing time in milliseconds. |
renders | Labels counted for this call; always 0 on failure. |
summary | Short, endpoint-specific facts (see event table). On failure: the extra error fields, e.g. { tool } or { hint, limit, used, requested }, or null. |
error | Error message on failure, otherwise null. |
resultIncluded | true when result is present. |
result | Only with webhookResult=true and success: { contentType, encoding, size, data }. |
resultOmitted | Only with webhookResult=true when the result is too large: explains why result is missing. |
Result encodings
| encoding | Used for | data / size |
|---|---|---|
base64 | File responses: PDF, PNG, JPG and the files from /v1/batch/jobs/{jobId}/output and /picklist (also when the output is ZPL). | Base64 string of the file; size = file size in bytes. |
utf-8 | Text responses: SVG from zpl-to-svg, raw ZPL from /v1/enrich/zpl?format=zpl. | The text itself; size = UTF-8 bytes. |
json | JSON responses: converters, linter, analyser, lists, quota, batch jobs. | The complete response object as in the HTTP response; size = serialized bytes. |
Limit 10 MB. Larger results are left out: resultIncluded is false and resultOmitted says why - take the result from the API response instead (batch jobs: GET /v1/batch/jobs/{jobId}/output within one hour). For files the limit applies to the base64 size, so files up to about 7.5 MB fit.
Event names
| Event | Endpoint | summary |
|---|---|---|
api.design.pdf | POST /v1/pdf/render/design/{designId} | design, format, pages, bytes, widthMm, heightMm, dpi |
api.design.png | POST /v1/png/render/design/{designId} | design, format, record, bytes, widthMm, heightMm, dpi |
api.tool.completed | POST /v1/tools/{slug} | tool (the slug) plus tool-specific values, e.g. labels, bytes, zplBytes, errorCount |
api.enrich.completed | POST /v1/enrich/zpl | labels, fields, records, dpi, zplBytes |
api.batch.completed | POST /v1/batch/design/{designId} (sync and async) | jobId, jobStatus, design, labels, output, outputBytes, printer, printedLabels, printError, error |
api.batch.status | GET /v1/batch/jobs/{jobId} | as api.batch.completed |
api.batch.output | GET /v1/batch/jobs/{jobId}/output | as api.batch.completed |
api.batch.picklist | GET /v1/batch/jobs/{jobId}/picklist | as api.batch.completed |
api.printers.listed | GET /v1/printers | scope, count, weblink, remote |
api.company.printers.listed | GET /v1/company/printers | scope, count, weblink, remote, virtual |
api.agents.listed | GET /v1/agents | scope, count, online |
api.company.agents.listed | GET /v1/company/agents | scope, count, online |
api.quota.checked | GET /v1/quota | plan, limit, used, remaining |
api.webhooks.listed | GET /v1/webhooks | count |
Delivery and retries
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | zplcloud-webhooks/1.0 |
X-ZplCloud-Event | Event name, e.g. api.design.pdf. |
X-ZplCloud-Delivery | Numeric ID of the delivery; stays the same across retries. |
X-ZplCloud-Timestamp | Time of this attempt, ISO 8601 UTC, e.g. 2026-09-11T10:15:02.4817731Z. |
X-ZplCloud-Signature | Lowercase hex HMAC-SHA256 of the raw body, keyed with the webhook secret. No sha256= prefix. |
- Your endpoint must answer with any
2xxwithin 10 seconds. Other status codes, timeouts and connection errors count as failed. - Retries after 1 minute, 5 minutes and 30 minutes; after the fourth failed attempt the delivery is marked failed. The body is identical in every attempt.
- Only active webhooks receive deliveries; disabling a webhook pauses them.
- Every attempt is listed in the webhook's delivery log in the platform (Webhooks → Log), where failed deliveries can be retried by hand.
- A callback can arrive more than once, e.g. when your 2xx arrives after the timeout. Deduplicate on
requestId.
Answer first, then process
Verify the signature, store the payload and return 2xx right away. Long work inside the request - printing, uploading the result, calling your ERP - risks the 10-second timeout and triggers retries of a callback you already handled.
Verifying the signature
Compute HMAC-SHA256 over the raw request body bytes - before any JSON parsing - with the webhook secret as key (shown in the platform under Webhooks), hex-encode it in lowercase and compare it with X-ZplCloud-Signature in constant time.
Node.js (Express):
C# (ASP.NET Core minimal API):
Finding webhook IDs
- Platform → Webhooks (
/platform/webhooks): create a webhook with your receiver URL; every webhook shows its ID as a badge, e.g. ID 12. The secret for the signature is shown there too. GET /v1/webhookslists ID, URL, status and last delivery of all stored webhooks - see Printers, agents & quota.- The events a webhook is subscribed to do not matter for the callback.
Endpoint index
The interactive reference lists the whole public API, grouped into Rendering (Design rendering, Batch jobs, Label enrichment, Fonts), ZPL tools (Render ZPL, Convert to ZPL, Barcodes & GS1, Validate & analyse), Printing (Printers, Agents & watch folders, Remote printers, Virtual printers, Printer profiles & cookbook, Weblink certificates), Account (Quota & webhooks) and Integrations (DHL). This index is sorted by webhook support: all endpoints below accept webhookId and webhookResult, except those under Other endpoints. designId is <name>.<id>, e.g. shipping-label.42.
Rendering
| Endpoint | Body | Result | Event |
|---|---|---|---|
POST /v1/pdf/render/design/{designId} | JSON array of records (optional, max 2,000, 5 MB) | PDF, one page per record | api.design.pdf |
POST /v1/png/render/design/{designId}?record=0 | JSON array of records (optional) | PNG of one label | api.design.png |
Batch jobs
| Endpoint | Purpose | Event |
|---|---|---|
POST /v1/batch/design/{designId} | Records → ZPL or PDF, optional pick list, optional printing in blocks of 100 labels; sync 200 or "async": true → 202 | api.batch.completed |
GET /v1/batch/jobs/{jobId} | Job status, timings, print progress | api.batch.status |
GET /v1/batch/jobs/{jobId}/output | Generated ZPL or PDF (up to 20 MB, kept 1 hour) | api.batch.output |
GET /v1/batch/jobs/{jobId}/picklist | Pick list PDF | api.batch.picklist |
Starting a job requires the Pro plan (403 otherwise); status, output and pick list of an existing job are not plan-checked.
Label enrichment
| Endpoint | Body | Result | Event |
|---|---|---|---|
POST /v1/enrich/zpl | Carrier or Amazon zpl, fields, records, dpi | JSON { zpl, labels } or raw ZPL with ?format=zpl | api.enrich.completed |
Adds your own fields (SKU, bin location, PO) to every label without touching the original commands. Only available when label enrichment is enabled (404 otherwise) and on the Pro plan (403 otherwise).
ZPL tools
All tools are POST /v1/tools/{slug} with a JSON body; event api.tool.completed. Never blocked by the quota.
| Tool | Main input | Result |
|---|---|---|
zpl-to-pdf | zpl, dpi, widthMm, heightMm | PDF with all labels |
zpl-to-pngzpl-to-jpgzpl-to-svg | zpl, dpi, widthMm, heightMm | PNG, JPG or SVG of the first label |
zpl-linter | zpl, profile (amazon-fba) | JSON: errors, warnings and hints |
zpl-analyser | zpl, networkMbit | JSON: commands, label size, speed, darkness, time per label |
html-to-zpl | pngBase64 (HTML rendered as PNG), dpi, widthMm, heightMm | JSON with ZPL |
pdf-to-zpl | pdfBase64, pages, scale, rotate, darkness | JSON with ZPL (max 100 pages) |
image-to-zpl | imageBase64 (PNG, JPG, GIF, BMP) | JSON with ZPL and GRF |
svg-to-zpl | svg, dpi, sizeMm | JSON with ZPL |
epl2-to-zpldpl-to-zpltspl-to-zpl | code | JSON with ZPL |
zpl-to-tsplzpl-to-epl2zpl-to-dplzpl-to-sbplzpl-to-cpclzpl-to-escposzpl-to-brotherzpl-to-pclzpl-to-easyplugzpl-to-tpclzpl-to-jscript | zpl, dpi, compress (plus hex, cut, paper, model per language) | Raw print job for TCP 9100 |
barcode-to-zpl | symbology, data | JSON with ZPL |
qr-code-to-zpl | type (url, wifi, vcard …), fields | JSON with ZPL |
gs1-ai-128-to-zplgs1-ai-datamatrix-to-zplgs1-ai-qr-code-to-zplgs1-ai-databar-to-zpl | ai in bracket notation, e.g. (01)04006381333931(10)L-2026-0417 | JSON with ZPL and parsed AI elements |
check-barcode | imageBase64 (photo or scan) or data | JSON: decoded and validated codes |
dpi-calculator | dpi, lengthMm, dots, fontPt … | JSON: conversions and reference table |
Printers & agents
| Endpoint | Purpose | Event |
|---|---|---|
GET /v1/printers | Printers bound to the API key | api.printers.listed |
GET /v1/company/printers | All printers of the company: Weblink, remote, virtual - with target for batch jobs | api.company.printers.listed |
GET /v1/agents | zplCloud CLI agents connecting with the API key | api.agents.listed |
GET /v1/company/agents | All agents of the company across all keys | api.company.agents.listed |
Account & quota
| Endpoint | Purpose | Event |
|---|---|---|
GET /v1/quota | Plan, monthly label limit, used and remaining labels, period | api.quota.checked |
GET /v1/webhooks | Stored webhooks with their IDs (no secrets) | api.webhooks.listed |
Other endpoints
These endpoints do not take the webhook parameters. They are part of the interactive reference as well.
| Endpoint | Purpose |
|---|---|
POST /v1/zpl/render/design/{designId} | Render a saved design to ZPL (max 500 records) - counts one render per record against the label quota, 429 when it is used up. |
GET /v1/fontsGET /v1/fonts/platform/{name} | System fonts; download a platform printer font such as ZPLCLOUD.TTF. |
GET /v1/printer-profiles, /{id} | Printer profiles with their ordered commands (used by zplcloud profiles apply). |
GET /v1/zpl-cookbook, /{id} | ZPL cookbook commands. |
GET /v1/remote-printersPOST /v1/remote-printers/{id}/send, /file, /raw | Remote printers behind a zplCloud CLI agent: list, send ZPL/SGD, upload a ~DY file, print a job in another printer language byte-exact (finished or converted from ZPL - see printing the job). |
GET /v1/virtual-printersPOST /v1/virtual-printers/{id}/printGET /v1/virtual-printers/prints/{id}, .png | Virtual test printers - the only endpoints for sandbox keys. |
POST /v1/weblink/certificates/generateGET /v1/weblink/certificates/domains/… | Weblink certificates (used by zplcloud weblink setup). |
POST /v1/integrations/dhl/stampGET /v1/integrations/dhl/stamps, /status | DHL Internetmarke - see Integrations. |
POST /v1/print/folderGET /v1/agents/folders | Watch-folder printing through an agent: ZPL, or jobs in other printer languages with language or base64 - see zplCloud CLI. |
Quick start
1. Check the quota - also a quick test of the key:
2. Render ZPL as PNG - a ZPL tool, counts one render and is never blocked:
3. Render a design as PDF and get called back - two records, two pages, two renders; webhook 12 receives api.design.pdf:
Add &webhookResult=true to receive the PDF as base64 in the callback as well. Next steps: Rendering, ZPL tools, Printers, agents & quota.