POST /print/{target}
Send a print job to a specific printer. Returns a job ID for tracking.
Endpoint:
POST /api/v1/print/{target}Path Parameter
Section titled “Path Parameter”| Parameter | Type | Description |
|---|---|---|
target |
string |
The printer’s path value from GET /printers, or a tag alias (e.g. tag_LABELS). |
Request Body
Section titled “Request Body”Three options for sending print data:
Option 1: Raw binary data
Section titled “Option 1: Raw binary data”Send the raw print data (ZPL, PDF, etc.) directly as the request body. Always synchronous — blocks until the job completes.
POST /api/v1/print/tcp_192-168-86-250_9100Content-Type: application/octet-streamX-API-Key: ULA9WHXMFCHF
^XA^FO50,50^A0N,50,50^FDHello World^FS^XZOption 2: JSON with URL
Section titled “Option 2: JSON with URL”Send a JSON body with a content field containing a URL to fetch the document from.
POST /api/v1/print/tcp_192-168-86-250_9100Content-Type: application/jsonX-API-Key: ULA9WHXMFCHF
{ "content": "https://example.com/label.zpl", "contentType": "raw_url"}Option 3: JSON with base64
Section titled “Option 3: JSON with base64”Send a JSON body with base64-encoded content and print options.
POST /api/v1/print/tag_LABELSContent-Type: application/jsonX-API-Key: ULA9WHXMFCHF
{ "content": "XlhBXkZPNTAsNTBeQTBOLDUwLDUwXkZESGVsbG8gV29ybGReRlNeWFo=", "contentType": "raw_base64", "title": "Shipping Label #1234", "source": "my-erp", "options": { "copies": 2 }}JSON Body Fields
Section titled “JSON Body Fields”| Field | Type | Required | Description |
|---|---|---|---|
content |
string |
Yes | The document to print — a URL, base64-encoded data, or a local file path. |
contentType |
string |
Recommended | One of: pdf_url, pdf_base64, raw_url, raw_base64, zpl, zpl_base64. When omitted, inferred from the content and printer type. New integrations should always set this. See content type support by printer family. |
title |
string |
No | Job name. Passed to CUPS. Defaults to "Untitled". |
source |
string |
No | Identifier for the submitting application or user (e.g. "my-erp", "jsmith"). |
wait |
boolean |
No | true (default): block until job completes. false: return immediately, poll GET /jobs/{jobId} for status. |
options |
object |
No | Print options (see below). Unsupported options for a given printer are silently ignored. |
idempotencyKey |
string |
No | Unique key to prevent duplicate submissions. If a job with this key already exists (within 1-hour TTL), the existing job is returned with HTTP 200. |
Content Type Support by Printer Family
Section titled “Content Type Support by Printer Family”Raw content types (raw_url, raw_base64, zpl, zpl_base64, and raw binary bodies) are delivered to the printer byte-for-byte, unmodified — even if the bytes happen to be a PDF. PDF content types (pdf_url, pdf_base64) are rendered on the device to the printer’s native format.
| Printer family | Raw types | PDF types |
|---|---|---|
Zebra ZPL ("content": "zpl") |
✅ ZPL passthrough | ✅ Rendered to ZPL — see PDF printing |
| Office / driverless IPP | ✅ Passthrough | ✅ Rendered by the printer’s driver |
| Brother label and Dymo | ✅ Passthrough | ✅ Rendered — the PDF page size must match the loaded media |
| Epson ESC/POS and other raw devices | ✅ Passthrough | ❌ Rejected with 400 (see below) |
A PDF content type sent to a printer that can’t render returns 400 with the accepted content types — nothing is sent to the printer:
{ "status": "error", "statusCode": 400, "result": { "module": "/print/tcp_192-168-86-250_9100", "message": "This printer accepts raw data only; supported contentTypes: zpl, zpl_base64, raw, raw_base64, raw_url (or a raw request body)", "content": { "jobId": "pj_b7c8d9e0", "printer": "tcp_192-168-86-250_9100", "target": "tcp_192-168-86-250_9100", "state": "failed", "error": "This printer accepts raw data only; supported contentTypes: zpl, zpl_base64, raw, raw_base64, raw_url (or a raw request body)", "createdAt": "2026-03-28T14:30:00.000Z", "completedAt": "2026-03-28T14:30:00.100Z" } }}Print Options
Section titled “Print Options”| Option | Type | Description |
|---|---|---|
copies |
number |
Number of copies. Default 1. |
duplex |
string |
"one-sided", "long-edge", or "short-edge". |
colorMode |
string |
"color", "grayscale", or "auto". |
paperSize |
string |
CUPS media name (e.g. "letter", "a4"). Must be one of the printer’s capabilities.mediaSupported values from GET /printers. Brother P-touch printers ignore this option: USB P-touch uses the loaded tape and PDF dimensions to select the page size. |
pageRange |
string |
Page range (e.g. "1-5", "1,3,7-9"). PDF only. |
bin |
string |
Paper tray / input slot name. |
fitToPage |
boolean |
Scale content to fit the page. PDF only. |
orientation |
string |
"portrait", "landscape", "reverse-landscape", or "reverse-portrait". |
quality |
string |
"draft", "normal", or "high". |
Success Response (200 — synchronous)
Section titled “Success Response (200 — synchronous)”Returned when wait is true (default) or for raw binary requests.
{ "status": "success", "statusCode": 200, "result": { "module": "/print/tag_LABELS", "message": "Printed successfully.", "content": { "jobId": "pj_a8f3e1b2", "printer": "tcp_192-168-86-250_9100", "target": "tag_LABELS", "title": "Shipping Label #1234", "source": "my-erp", "state": "completed", "error": null, "createdAt": "2026-03-28T14:30:00.000Z", "completedAt": "2026-03-28T14:30:08.500Z" } }}Accepted Response (201 — non-waiting)
Section titled “Accepted Response (201 — non-waiting)”Returned when wait is false. Poll GET /jobs/{jobId} for the final state.
{ "status": "success", "statusCode": 201, "result": { "module": "/print/tag_LABELS", "message": "Print job accepted.", "content": { "jobId": "pj_a8f3e1b2", "printer": "tcp_192-168-86-250_9100", "target": "tag_LABELS", "title": "Shipping Label #1234", "source": "my-erp", "state": "queued", "error": null, "createdAt": "2026-03-28T14:30:00.000Z", "completedAt": null } }}Error: 400 — Label Media Mismatch (Brother label printers)
Section titled “Error: 400 — Label Media Mismatch (Brother label printers)”P-touch (USB)
Section titled “P-touch (USB)”The PDF must match the detected tape, within 2pt in either orientation, with a label length of 36–2834pt. Non-PDF data, an unmatched width, or a length outside that range returns 400 before queueing. result.content is { message, loadedTape }; use loadedTape.widthPt to resize the page. See Sizing your labels.
{ "status": "error", "statusCode": 400, "result": { "module": "/print/usb_sn_000F5G234070", "message": "PDF is sized for 24 mm tape (68 pt wide); the printer has 12 mm tape loaded (34 pt)", "content": { "message": "PDF is sized for 24 mm tape (68 pt wide); the printer has 12 mm tape loaded (34 pt)", "loadedTape": { "media": "tz-12", "label": "12 mm tape", "widthMm": 12, "widthPt": 34, "mediaType": 1, "readAt": "2026-03-28T14:30:00.000Z" } } }}If the width matches but the length is 3000pt, the message is PDF length 3000 pt is outside 36–2834 pt. Either page dimension can match the loaded width: a 68 × 51pt page on 18mm tape prints rotated as 51 × 68pt.
QL printers match the PDF against the supported media table. A non-PDF payload or an unmatchable page size returns 400 with validMedia. Each entry includes a human-readable label. The example below shows one entry; the response contains the full family table. Network P-touch printers also use the supported-media table rather than tape detection.
{ "status": "error", "statusCode": 400, "result": { "module": "/print/usb_sn_000F5G234070", "message": "PDF page size 612x792pt does not match any media this printer supports.", "content": { "message": "The PDF page size declares the label media. Size the page to a supported width (continuous tape accepts any length >= 36pt).", "validMedia": [ { "name": "62mm", "label": "62 mm continuous roll", "width": 176, "height": 284, "continuous": true, "mmLabel": "62mm" } ] } }}Error: 409 — Tape Problem (P-touch)
Section titled “Error: 409 — Tape Problem (P-touch)”USB P-touch printers return 409 for no-tape, unsupported-tape, or printer-error. No job is queued. A missing cassette returns:
{ "status": "error", "statusCode": 409, "result": { "module": "/print/usb_sn_000F5G234070", "message": "No tape cassette is loaded in Brother PT-P900W", "content": { "message": "No tape cassette is loaded in Brother PT-P900W", "loadedTape": { "reason": "no-tape", "readAt": "2026-03-28T14:30:00.000Z" } } }}A decoded printer error names the condition:
{ "status": "error", "statusCode": 409, "result": { "module": "/print/usb_sn_000F5G234070", "message": "Brother PT-P900W reports: cover open", "content": { "message": "Brother PT-P900W reports: cover open", "loadedTape": { "reason": "printer-error", "readAt": "2026-03-28T14:30:00.000Z" } } }}Unsupported media is named in the message, for example Brother PT-P900W: Unsupported tape: heat-shrink tube (2:1), 24 mm.
Error: 503 — Printer Not Ready
Section titled “Error: 503 — Printer Not Ready”Returned when the Zebra pre-flight status check detects an issue or the USB P-touch tape cannot be read.
Zebra pre-flight
Section titled “Zebra pre-flight”The response contains the failed print job. See Pre-flight Status Check in the Zebra guide for details.
{ "status": "error", "statusCode": 503, "result": { "module": "/print/tcp_192-168-86-250_9100", "message": "Printer is not ready", "content": { "jobId": "pj_c4d5e6f7", "printer": "tcp_192-168-86-250_9100", "target": "tcp_192-168-86-250_9100", "state": "failed", "error": "Printer not ready: Paper out, Head is open", "createdAt": "2026-03-28T14:30:00.000Z", "completedAt": "2026-03-28T14:30:00.500Z" } }}P-touch tape unreadable
Section titled “P-touch tape unreadable”The tape read has a 3-second bound. An unreadable status returns 503 before queueing. This includes USB load, lookup, open, claim, write, read, and timeout failures. A cleanup failure also makes an otherwise successful reading unreadable:
{ "status": "error", "statusCode": 503, "result": { "module": "/print/usb_sn_000F5G234070", "message": "Couldn't read the loaded tape from Brother PT-P900W: timeout: timed out", "content": { "message": "Couldn't read the loaded tape from Brother PT-P900W: timeout: timed out", "loadedTape": { "reason": "unreadable", "readAt": "2026-03-28T14:30:00.000Z" } } }}A busy queue reuses its last reading, including the 20 seconds after its last state change. If no reading exists, loadedTape is null and the message is Couldn't read the loaded tape from Brother PT-P900W: the printer is busy and has not been read yet.
ProxyBox Zero Docs v1.0.29
