Passer au contenu

Put several ZPL labels in one PDF

Send all the labels in one request, as consecutive ^XA … ^XZ blocks, with Accept: application/pdf and no label index in the URL: the API returns one PDF with one page per label. A request takes up to 50 labels, ^PQ copies included; split larger jobs.

Mis à jour le 2026-10-20 · Renders made with the OpenZPL engine

Open in the viewer

One file, one page per label

Each ^XA … ^XZ block is one label. This file has three:

First of three box labels: Box 1 of 3, order 10482 and a Code 128 barcode
4 × 2 in label at 203 dpi (8 dpmm) · 812 × 406 px · Open in the viewer
^XA
^FO30,40^A0N,40,40^FDBox 1 of 3^FS
^FO30,100^A0N,28,28^FDOrder 10482^FS
^BY2,3,110
^FO30,160^BCN,110,Y,N,N^FD10482-1^FS
^XZ
^XA
^FO30,40^A0N,40,40^FDBox 2 of 3^FS
^FO30,100^A0N,28,28^FDOrder 10482^FS
^BY2,3,110
^FO30,160^BCN,110,Y,N,N^FD10482-2^FS
^XZ
^XA
^FO30,40^A0N,40,40^FDBox 3 of 3^FS
^FO30,100^A0N,28,28^FDOrder 10482^FS
^BY2,3,110
^FO30,160^BCN,110,Y,N,N^FD10482-3^FS
^XZ

The render above is the first label. Send the whole file without an index and you get a three-page PDF:

curl https://api.openzpl.com/v1/printers/8dpmm/labels/4x2/ \
  -H "Accept: application/pdf" --data-binary @labels.zpl -o labels.pdf

The response carries X-Total-Count: 3. Every page has the size in the URL, here 4 × 2 in. Use --data-binary, not -d: -d strips the line breaks.

One label out of many

Add the index, starting at 0, to get one label. With a PNG you always get one label; with a PDF and an index you get a one-page PDF.

# The second label as PNG
curl https://api.openzpl.com/v1/printers/8dpmm/labels/4x2/1/ --data-binary @labels.zpl -o box2.png

# The third label as a one-page PDF
curl https://api.openzpl.com/v1/printers/8dpmm/labels/4x2/2/ \
  -H "Accept: application/pdf" --data-binary @labels.zpl -o box3.pdf

An index past the end returns 404 with ERROR: Label index 3 is out of range; the ZPL produces 3 label(s). Read X-Total-Count from a first response to know how many there are.

Copies with ^PQ

^PQ prints a label several times. Each copy is a page and counts as a label: ^PQ3 on a single label returns a three-page PDF and X-Total-Count: 3, and uses 3 of your daily labels.

^XA
^FO30,40^A0N,40,40^FDCopy^FS
^PQ3
^XZ

More than 50 labels

A request may produce up to 50 labels; more returns 413. Split the file between labels, never inside one, and count ^PQ copies when you group them. This Python script turned a 120-label file into three PDFs of 50, 50 and 20 pages:

import re

import requests

URL = "https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/"
MAX_LABELS = 50  # per request


def labels_in(block: str) -> int:
    """A ^XA...^XZ block prints once, or ^PQ times."""
    m = re.search(r"\^PQ(\d+)", block)
    return int(m.group(1)) if m else 1


def chunks(zpl: str):
    """Group whole labels into requests of at most 50 labels each."""
    blocks = re.findall(r"\^XA.*?\^XZ", zpl, flags=re.S | re.I)
    batch, count = [], 0
    for b in blocks:
        n = labels_in(b)
        if batch and count + n > MAX_LABELS:
            yield "\n".join(batch)
            batch, count = [], 0
        batch.append(b)
        count += n
    if batch:
        yield "\n".join(batch)


zpl = open("orders.zpl", encoding="utf-8").read()
with requests.Session() as http:
    for i, part in enumerate(chunks(zpl), start=1):
        res = http.post(URL, data=part.encode("utf-8"),
                        headers={"Accept": "application/pdf"}, timeout=(5, 60))
        res.raise_for_status()
        with open(f"orders-{i:03}.pdf", "wb") as f:
            f.write(res.content)
        print(f"orders-{i:03}.pdf: {res.headers['X-Total-Count']} labels")

To end up with a single file, merge the parts with a PDF library such as pypdf. A single label with ^PQ above 50 can't be split this way: lower the copy count and repeat the request.

A label that sets up a stored format (^DF) and the labels that recall it (^XF) must go in the same request: the API keeps nothing between requests.

Limites

  • Endpoint: POST https://api.openzpl.com/v1/printers/8dpmm/labels/4x6/ with the ZPL as the request body. The density is 6dpmm, 8dpmm, 12dpmm or 24dpmm; the size is in inches, up to 15 per side.
  • Accept: application/pdf without a label index returns every label, one per page. Add an index (.../4x6/0/) to get a single label.
  • Free plan: no key, 3 requests per second and 5,000 labels per day per IP address. Every label counts, ^PQ copies and PDF pages included. See pricing.
  • Per request: up to 50 labels and 1 MB of ZPL.

Questions

Does a 10-label PDF count as one request or ten?

Both, for different limits: it is one request against the per-second limit and ten labels against the daily allowance.

Can each page have a different size?

No. The size in the URL applies to every page. Send labels of different sizes in separate requests.

Why does my PDF have fewer pages than labels in the file?

Something between labels isn't a complete ^XA … ^XZ block, or a ^XA has no ^XZ. Check X-Total-Count against what you expect and open the file in the viewer.

Sources