Skip to content

Cloud Print API

The Cloud Print API sends labels to printers connected through the LabelZoom Print Agent. A user-installed agent holds a persistent connection to the cloud and prints jobs pushed from these endpoints — from the dashboard, this API, or your own integrations.

All endpoints are hosted on the LabelZoom API:

https://api.labelzoom.com

Authenticate with your LabelZoom access token — the same Authorization bearer token used by the conversion API:

Authorization: Bearer <your-access-token>

When you send a document whose format differs from a printer’s configured native format, LabelZoom converts it before the job reaches the agent. For example, sending a PDF to a printer configured for ZPL converts the PDF to ZPL automatically — you don’t need to convert first. Declare what you’re sending with the request Content-Type (or the sourceFormat query parameter for the text printer languages) — see Send a label to a printer.

You can also print a label you’ve already designed and published as a print template, sending only the values rather than the label code. That path takes the template and printer as path parameters and a JSON payload keyed by field name — see Merge fields. Copies and idempotency work the same way there.

Add the copies query parameter to send the whole job to the printer more than once: copies=25 sends it 25 times. It works on both print endpoints — raw documents and published templates — and with every printer language and document type:

POST /api/v3/printers/{id}/print?sourceFormat=zpl&copies=25
  • A whole number from 1 to 9999, written as plain digits. Anything else — 0, 10000, 1.5, 1e3, a negative number, an empty value, text, or copies given more than once — returns 400:

    { "message": "copies must be a whole number from 1 to 9999" }
  • A quantity already in the document multiplies. A ZPL label carrying ^PQ3 sent with copies=5 prints 15 labels. To print exactly copies labels, send a document that prints one of each.

  • A document holding several labels prints collated. For a document holding labels A and B, copies=2 prints A, B, A, B — as one job with one status.

  • copies=1 is the same as leaving it off. Without copies, the job is sent once, exactly as before, and prints whatever quantity the document sets.

While the copies of a job are being sent, its status can be printing. It moves to completed once every copy has been sent, or to failed if the printer fails partway through — the job’s error then says how many of the copies were sent before the failure.

Printing copies needs an up-to-date LabelZoom Print Agent on every computer that serves the printer — for every printer, whatever its connection type. If any of them runs an older agent, a request with copies greater than 1 returns 422 with code agent_update_required and nothing prints — it never falls back to a single copy. Update the agent and send the request again. A request without copies, or with copies=1, prints on any agent.

A printer set to the Windows driver print mode prints images through its installed Windows driver rather than a printer language. For those printers the LabelZoom Print Agent sets the driver’s copy count, and the copies print collated, as on any other printer.

Some Windows drivers limit how many copies they print at once. If the driver can’t print the number you asked for, the job fails with an error giving the driver’s maximum. It never prints a different number.

To make a print safe to retry, send an Idempotency-Key request header — a unique value you generate, such as a UUID or an order number. It works on both print endpoints.

Idempotency-Key: 3f1c9a2e-1b7d-4e6a-9c2f-8a5b1d4e7c90

A key names one print request:

  • Same key, same request — returns the original job, with "idempotent": true and the job’s current status, and nothing prints again. Retrying after a timeout or a dropped connection is always safe.
  • Same key, different request — returns 422 with code idempotency_key_reused, and nothing prints. A request is different if anything that decides what prints has changed: the document, the merge data, the printer, the template, sourceFormat, the transform options, or copies. The order of query parameters, and of the keys in JSON merge data, doesn’t matter.
{
"message": "Idempotency-Key was already used with different request parameters. Use a new key for a different print request.",
"code": "idempotency_key_reused"
}

So give a deliberate reprint — or the same label at a different quantity — a new key. Keys are scoped to your account.

A template print with an array of two or more entries derives one key per entry from yours — key#0, key#1, and so on — and checks them all before printing anything. If any entry conflicts, the whole batch is refused and nothing prints.

A key first used before this check was introduced keeps replaying its original job, as it always did.

During the beta each account may have up to 3 print agents (an unredeemed enrollment code holds a slot until it expires), 10 printers, and 500 print jobs per UTC day. A document may be up to 4 MB as sent and 8 MB after conversion; a template batch may hold up to 25 labels. Print requests are also throttled per account.

GET /api/v3/print/quota reports where you stand:

{
"agents": { "used": 2, "pending": 0, "limit": 3, "remaining": 1 },
"printers": { "used": 4, "limit": 10, "remaining": 6 },
"jobsToday": { "used": 137, "limit": 500, "remaining": 363, "resetAt": "2026-09-11T00:00:00.000Z" },
"limits": { "maxPayloadBytes": 4194304, "maxStoredPayloadBytes": 8388608, "maxBatchSize": 25 }
}

When a limit is hit the response is 409 (agents, printers), 413 (document or batch size), or 429 (daily jobs, or throttling — with a Retry-After header), and the body names the limit:

{ "message": "Daily print limit of 500 jobs reached; it resets at 00:00 UTC.", "code": "daily_job_limit_reached", "limit": 500, "used": 500, "remaining": 0, "resetAt": "2026-09-11T00:00:00.000Z" }

code is stable — branch on it rather than on the message. Need more during the beta? Contact us.

GET /api/v3/printers

Returns the printers configured for your account, each with its live status and the agents that can serve it.

{
"printers": [
{
"id": "b2c3…",
"name": "Warehouse Zebra",
"connection_type": "network",
"address": "192.168.1.50:9100",
"status": "ready",
"config": { "nativeFormat": "zpl", "driver": "zebra" }
}
]
}
POST /api/v3/printers/{id}/print

The request body is the raw document you want to print — exactly like the conversion API. Send the bytes directly (ZPL/EPL text, or a PDF/PNG binary); there is no JSON envelope. If the document’s format differs from the printer’s native format, it is converted automatically before printing (e.g. a PDF sent to a ZPL printer is converted to ZPL).

Declaring the format. LabelZoom determines the document’s format from the request Content-Type (e.g. application/pdf, image/png). Because the thermal-printer languages (ZPL, EPL, IPL, TSPL, DPL, SBPL) all share text/plain, declare those explicitly with a sourceFormat query parameter:

POST /api/v3/printers/{id}/print?sourceFormat=zpl

Transformations. Any conversion parameter — rotation, dpi, label.width, scaling, and so on — can be added as a query parameter and is applied on the way to the printer, even when no format change is needed:

POST /api/v3/printers/{id}/print?sourceFormat=zpl&rotation=90

Copies. Add copies to send the job to the printer more than once; a quantity already in the document multiplies. See Printing copies.

Idempotency. Send an Idempotency-Key header to make the print safe to retry. Repeating a key with the same request returns the original job; reusing it for a different request returns 422. See Idempotency.

Terminal window
# Send ZPL to a ZPL printer (raw body; declare the format with sourceFormat)
curl -X POST "https://api.labelzoom.com/api/v3/printers/b2c3.../print?sourceFormat=zpl" \
-H "Authorization: Bearer <your-access-token>" \
-H "Content-Type: text/plain" \
-H "Idempotency-Key: 3f1c9a2e-1b7d-4e6a-9c2f-8a5b1d4e7c90" \
--data-binary '^XA^FO50,50^A0N,50,50^FDHello^FS^XZ'
Terminal window
# Send a PDF to a ZPL printer — converted to ZPL automatically, rotated 90°
curl -X POST "https://api.labelzoom.com/api/v3/printers/b2c3.../print?rotation=90" \
-H "Authorization: Bearer <your-access-token>" \
-H "Content-Type: application/pdf" \
--data-binary @label.pdf
Terminal window
# Send the job to the printer 25 times (a ^PQ in the ZPL would multiply it)
curl -X POST "https://api.labelzoom.com/api/v3/printers/b2c3.../print?sourceFormat=zpl&copies=25" \
-H "Authorization: Bearer <your-access-token>" \
-H "Content-Type: text/plain" \
-H "Idempotency-Key: 7d2e5b91-4c3a-4f08-b6e1-0a9c8d7f6e52" \
--data-binary @price-tag.zpl

The response tells you whether the job went straight to an online agent:

{ "jobId": "9f8e…", "status": "dispatched", "copies": 25 }

status is dispatched when the job was pushed to an online agent, or queued when every bound agent is offline — a queued job prints as soon as an agent reconnects.

copies echoes what you asked for, or null when you didn’t send it — the job is then sent once. A request that replays an earlier one with the same idempotency key also carries "idempotent": true.

Status Meaning
200 Job accepted (dispatched or queued), or an earlier job replayed for the same idempotency key.
400 Missing or empty document body, or a copies value that isn’t a whole number from 1 to 9999.
403 The printer does not belong to your account.
404 No printer with that id.
422 The request can’t be carried out as sent; the body’s code says why. agent_update_required: copies is greater than 1 and an agent serving the printer needs updating to print copies. idempotency_key_reused: the key was already used for a different request.
413 The document is larger than the limit, as sent or after conversion.
429 Daily job limit reached, or too many requests — see Retry-After.
502 The document could not be converted to the printer’s native format.
GET /api/v3/printers/{id}/status

Returns a printer’s live status, the decoded detail behind it, and the printer’s identity and configuration — plus each bound agent’s own observation.

For a Zebra printer on the network, the agent queries the device directly: ~HS (host status) for the live condition, ~HI (host identification) for model and firmware, and ^HH for the printer’s configuration label. A printer attached over USB reports what the Windows print spooler knows, plus the same ~HS detail when its driver supports bidirectional communication.

Status is refreshed roughly every 60 seconds. Identity and configuration change only when the printer is reconfigured, so they are re-read every 30 minutes — or immediately, via Refresh.

{
"id": "b2c3…",
"name": "Warehouse Zebra",
"connectionType": "network",
"address": "192.168.1.50:9100",
"status": "ready",
"lastStatusDate": "2026-08-16T15:04:12Z",
"statusDetail": {
"source": "zpl",
"probedAt": "2026-08-16T15:04:12Z",
"hostStatus": {
"paperOut": false,
"ribbonOut": false,
"headOpen": false,
"paused": false,
"overTemp": false,
"underTemp": false,
"bufferFull": false,
"labelLength": 1240,
"labelsRemaining": 0,
"formatsInBuffer": 0,
"graphicImagesStored": 8,
"printMode": "tear-off",
"thermalTransfer": false
}
},
"deviceInfo": {
"probedAt": "2026-08-16T14:41:03Z",
"device": {
"model": "ZTC GK420d",
"firmware": "V61.17.17Z",
"dpi": 203,
"dotsPerMm": 8,
"memory": "8154KB"
},
"config": {
"entries": [
{ "key": "darkness", "label": "DARKNESS", "value": "+10" },
{ "key": "print_speed", "label": "PRINT SPEED", "value": "4.0 IPS" },
{ "key": "media_type", "label": "MEDIA TYPE", "value": "GAP/NOTCH" }
]
}
},
"agents": [
{
"agentId": "a1b2…",
"agentName": "Warehouse PC",
"priority": 0,
"isPrimary": true,
"agentOnline": true,
"status": "ready",
"lastStatusDate": "2026-08-16T15:04:12Z"
}
]
}

status is the roll-up across every agent bound to the printer — a printer is as healthy as its best-placed agent sees it. agents[] is what each agent individually observed, which is what tells you why a failover printer is reachable from one site and not another.

statusDetail.source records how the status was determined:

Value Meaning
zpl The printer answered a ~HS query over the network.
tcp Socket reachability only — the printer isn’t configured as a Zebra (config.driver).
winspool Reported by the Windows print spooler.
winspool-bidi The printer answered a ~HS query through its Windows driver.

statusDetail is null until a probe has succeeded, and deviceInfo until a full inspection has. Only a printer’s primary agent collects deviceInfo, which is why it carries its own probedAt.

Refresh a printer’s status and configuration

Section titled “Refresh a printer’s status and configuration”
POST /api/v3/printers/{id}/refresh

Asks the agents bound to this printer to re-inspect it immediately, instead of waiting for their next scheduled probe.

This is asynchronous: the call returns as soon as the request has been sent, and the result arrives a second or two later through the agent’s normal status reporting. Poll GET /api/v3/printers/{id}/status for the updated values.

{ "requested": true }

requested: false means every agent bound to this printer is currently offline, so nothing will change until one reconnects.

GET /api/v3/printers/{id}/jobs?status={status}&limit={limit}

Returns recent print jobs for a printer, most recent first. Optional status filter (queued, dispatched, printing, completed, failed) and limit.

{
"jobs": [
{ "id": "9f8e…", "status": "completed", "source_format": "zpl", "copies": 25, "retry_count": 0, "created_date": "2026-07-29T15:00:00Z", "completed_date": "2026-07-29T15:00:02Z" }
]
}

Each job’s copies is the value its request sent, or null when the request didn’t set it.

GET /api/v3/jobs/{id}

Returns a single job, including its current status, its copies (as in the job list above), and any error. Poll this to track a job to completion. A job with copies can be printing while its copies are being sent — see Printing copies.

POST /api/v3/jobs/{id}/retry

Re-enqueues a failed job, replaying its stored payload. Capped so a permanently broken job cannot loop.

Retention. A job’s document is stored while the job is in flight so an offline agent can pick it up, and is deleted the moment the agent confirms the print. A failed job keeps its document for about a day so it can be retried; after that the document is deleted (retry then answers 409) and only the job’s status history remains, for 30 days.

GET /api/v3/agents

Returns the print agents enrolled for your account, with each agent’s online status.

POST /api/v3/agents/enroll

Issues a short-lived, single-use code to pair a newly installed agent with your account. Expires in 15 minutes.

{ "enrollmentCode": "LZ-7F3K-9QW2" }

Provide it to the agent when you start it — see the Print Agent guide.

Method Path Purpose
GET /api/v3/agents/{id} One agent, with its online status.
PATCH /api/v3/agents/{id} Rename an agent: { "name": "…" }.
DELETE /api/v3/agents/{id} Remove an agent. Its key stops working at once; its printers are removed with it.
GET /api/v3/agents/{id}/printers The printers under an agent.
POST /api/v3/agents/{id}/printers Add a printer: { "name", "connectionType": "tcp" | "usb", "address", "config": { "nativeFormat", "driver", "dpi" } }.
PATCH /api/v3/printers/{id} Rename, re-address, or reconfigure a printer.
DELETE /api/v3/printers/{id} Remove a printer and its job history.
POST /api/v3/printers/{id}/refresh Ask the agent to re-read the printer’s status and configuration now.
POST /api/v3/printers/{id}/agents Bind a second agent as a failover ({ "agentId": "…" }): when the primary is offline, the standby prints instead.
DELETE /api/v3/printers/{id}/agents/{agentId} Unbind a failover agent. The primary cannot be unbound — delete the printer instead.

These take a user token. An API key with the print scope can list printers and jobs and submit prints, but not change configuration.

If you publish a label as a print template (from LabelZoom Studio, or the Print templates page), you can print it by sending only the data:

POST /api/v3/printers/{printerId}/templates/{templateId}/print
POST /api/v3/templates/{templateId}/convert

The body is a JSON object of merge-field values — or an array of objects for a batch, one label per element, up to the batch limit. The template’s ${field} placeholders are filled by name, the label is converted to the printer’s native language, and one job is enqueued per element. convert returns the rendered label instead of printing it: raw bytes for a single object, { "labels": [ …base64… ] } for a batch, with ?target= choosing the output format. Add ?validate=true to reject a payload that is missing a merge field. An Idempotency-Key header makes the request safe to repeat; in a batch each element gets its own key derived from it.

print needs the print scope, convert the convert scope (or a user token for either). GET /api/v3/templates lists your templates with their merge fields; GET /api/v3/templates/{id} returns one.

A batch that cannot finish inside the request’s time budget returns 202 with the jobs enqueued so far and a remaining count — resubmit the rest.