zpl.tools
Migrate from Labelary

Side-by-side examples

Labelary calls next to their zpl.tools equivalents, for the three most common requests

Each example below shows the Labelary call and the zpl.tools call for the same request, in curl, Python, and Node. The only changes are the base URL and the added X-API-Key header — everything else (path shape, Accept negotiation, request body) is unchanged.

1. Single label → PNG

The default case: one ^XA...^XZ label, rendered to PNG.

curl

# Labelary
curl -X POST "https://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" \
  --data "^XA^FO50,50^FDHello World^FS^XZ" \
  -o label.png

# zpl.tools
curl -X POST "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6/0" \
  -H "X-API-Key: your_api_key_here" \
  --data "^XA^FO50,50^FDHello World^FS^XZ" \
  -o label.png

Python

import requests

zpl = "^XA^FO50,50^FDHello World^FS^XZ"

# Labelary
response = requests.post(
    "https://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/",
    data=zpl,
)

# zpl.tools
response = requests.post(
    "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6/0",
    headers={"X-API-Key": "your_api_key_here"},
    data=zpl,
)

with open("label.png", "wb") as f:
    f.write(response.content)

Node

const zpl = "^XA^FO50,50^FDHello World^FS^XZ";

// Labelary
let response = await fetch(
  "https://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/",
  { method: "POST", body: zpl }
);

// zpl.tools
response = await fetch(
  "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6/0",
  {
    method: "POST",
    headers: { "X-API-Key": "your_api_key_here" },
    body: zpl,
  }
);

const buffer = Buffer.from(await response.arrayBuffer());

2. Single label → PDF

Same request, Accept: application/pdf instead of the default PNG.

curl

# Labelary
curl -X POST "https://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/" \
  -H "Accept: application/pdf" \
  --data "^XA^FO50,50^FDHello World^FS^XZ" \
  -o label.pdf

# zpl.tools
curl -X POST "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6/0" \
  -H "X-API-Key: your_api_key_here" \
  -H "Accept: application/pdf" \
  --data "^XA^FO50,50^FDHello World^FS^XZ" \
  -o label.pdf

Python

import requests

zpl = "^XA^FO50,50^FDHello World^FS^XZ"

# zpl.tools (Labelary call is identical minus the base URL / API key)
response = requests.post(
    "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6/0",
    headers={
        "X-API-Key": "your_api_key_here",
        "Accept": "application/pdf",
    },
    data=zpl,
)

with open("label.pdf", "wb") as f:
    f.write(response.content)

Node

const zpl = "^XA^FO50,50^FDHello World^FS^XZ";

// zpl.tools (Labelary call is identical minus the base URL / API key)
const response = await fetch(
  "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6/0",
  {
    method: "POST",
    headers: {
      "X-API-Key": "your_api_key_here",
      "Accept": "application/pdf",
    },
    body: zpl,
  }
);

const buffer = Buffer.from(await response.arrayBuffer());

3. Multi-label ZPL → all-labels PDF

Several ^XA...^XZ pairs in one submission, rendered as a multi-page PDF (omit the {index} path segment). This is the batch case — one request instead of N.

curl

zpl='^XA^FO50,50^FDLabel 1^FS^XZ^XA^FO50,50^FDLabel 2^FS^XZ^XA^FO50,50^FDLabel 3^FS^XZ'

# Labelary
curl -X POST "https://api.labelary.com/v1/printers/8dpmm/labels/4x6/" \
  -H "Accept: application/pdf" \
  --data "$zpl" \
  -o labels.pdf

# zpl.tools
curl -X POST "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6" \
  -H "X-API-Key: your_api_key_here" \
  -H "Accept: application/pdf" \
  --data "$zpl" \
  -o labels.pdf

Python

import requests

zpl = (
    "^XA^FO50,50^FDLabel 1^FS^XZ"
    "^XA^FO50,50^FDLabel 2^FS^XZ"
    "^XA^FO50,50^FDLabel 3^FS^XZ"
)

# zpl.tools — no page-count cap; Labelary caps at 50 labels per request
response = requests.post(
    "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6",
    headers={
        "X-API-Key": "your_api_key_here",
        "Accept": "application/pdf",
    },
    data=zpl,
)

print(response.headers["X-Total-Count"])  # "3"
with open("labels.pdf", "wb") as f:
    f.write(response.content)

Node

const zpl =
  "^XA^FO50,50^FDLabel 1^FS^XZ" +
  "^XA^FO50,50^FDLabel 2^FS^XZ" +
  "^XA^FO50,50^FDLabel 3^FS^XZ";

const response = await fetch(
  "https://api.zpl.tools/compatibility/labelary/v1/printers/8dpmm/labels/4x6",
  {
    method: "POST",
    headers: {
      "X-API-Key": "your_api_key_here",
      "Accept": "application/pdf",
    },
    body: zpl,
  }
);

console.log(response.headers.get("X-Total-Count")); // "3"
const buffer = Buffer.from(await response.arrayBuffer());

Endpoint map

Labelaryzpl.toolsNotes
GET /v1/printers/{dpmm}/labels/{w}x{h}/{index}/{zpl}GET /compatibility/labelary/v1/printers/{dpmm}/labels/{w}x{h}/{index}/{zpl}Avoid for large ZPL — our URL-length cap (16 KB) is friendlier than Labelary's (~3 KB), but POST is still the better default.
POST /v1/printers/{dpmm}/labels/{w}x{h}/{index}POST /compatibility/labelary/v1/printers/{dpmm}/labels/{w}x{h}/{index}Form-urlencoded or multipart file/zpl field, same as Labelary.
POST /v1/printers/{dpmm}/labels/{w}x{h}POST /compatibility/labelary/v1/printers/{dpmm}/labels/{w}x{h}All-labels endpoint — PDF only; PNG here returns 404 (an index is required for PNG).
POST /v1/graphics (image → ZPL/EPL/IPL/DPL/SBPL/PCL)Not implemented.
POST /v1/fonts (TTF → ZPL font)Not implemented.

See the full API reference for path parameters and every supported header.