Skip to main content

zpljet

Official Python SDK for the ZPLJet API — fast ZPL → PDF/PNG conversion.

PyPI version CI license

  • Zero dependencies — a single small client on top of the stdlib
  • Fully typed (py.typed) — parameters, results, and every API error code
  • Reliable by default — automatic retries with exponential backoff (honoring Retry-After), per-request timeouts, typed exceptions
  • Sync and async — ZplJet for scripts and servers, AsyncZplJet for asyncio code
  • Python ≥ 3.10; tested through 3.14

Installation

Choose one:

pip install zpljet
uv add zpljet
poetry add zpljet

Quickstart

Create an API key in the dashboard (keys look like zpl_…), then:

import os
from pathlib import Path

from zpljet import ZplJet

zpljet = ZplJet(api_key=os.environ["ZPLJET_API_KEY"])

label = zpljet.convert(zpl="^XA^FO50,50^A0N,50,50^FDHello^FS^XZ")

Path("label.pdf").write_bytes(label.data)

Keep your API key server-side. Anyone with the key can spend your quota.

Usage

Convert to PDF or PNG

convert() accepts every parameter of POST /v1/convert:

label = zpljet.convert(
    zpl="^XA^FO50,50^A0N,50,50^FDHello^FS^XZ",
    format="png",     # "pdf" (default) | "png"
    dpmm=12,          # 6 | 8 (default, 203 dpi) | 12 (300 dpi) | 24 (600 dpi)
    width_mm=101.6,   # label width, default 4 in
    height_mm=152.4,  # label height, default 6 in
)

label.data          # bytes — the file
label.content_type  # "application/pdf" | "image/png"
label.id            # conversion id (shows up in your dashboard)

Hosted URLs (paid plans)

Pass output="url" to have ZPLJet host the file and return a public link instead of the bytes. Files are retained for your account's retention window (a dashboard setting, up to your plan's maximum).

hosted = zpljet.convert(zpl="^XA^FO50,50^A0N,50,50^FDHello^FS^XZ", output="url")

hosted.url             # public URL to the PDF (works until the file is deleted)
hosted.pages           # pages rendered (one per ^XA…^XZ block)
hosted.retention_days  # how long the file is kept
hosted.expires_at      # when the file is deleted and the URL stops working (ISO 8601, UTC)

The return type narrows automatically: output="url" gives a HostedLabel, everything else a LabelData.

Async

AsyncZplJet has the identical interface for asyncio code:

from zpljet import AsyncZplJet

zpljet = AsyncZplJet(api_key=os.environ["ZPLJET_API_KEY"])
label = await zpljet.convert(zpl="^XA^FO50,50^A0N,50,50^FDHello^FS^XZ")

The async client runs the dependency-free transport in a worker thread. Bound large batches with a semaphore; see examples/05_async_batch.py.

Error handling

Every API error code maps to a dedicated exception, so you branch with except — no string matching:

from zpljet import (
    ZplJet,
    APIConnectionError,
    BadRequestError,
    ConversionFailedError,
    QuotaExceededError,
    RateLimitError,
)

try:
    label = zpljet.convert(zpl=zpl)
except BadRequestError as err:
    print(f"Invalid request ({err.param}): {err.message}")
except QuotaExceededError as err:
    print(f"Quota used up ({err.used}/{err.quota}), resets {err.resets_at}")
except RateLimitError as err:
    print(f"Rate limited — retry after {err.retry_after}s")
except ConversionFailedError as err:
    print(f"Engine rejected the ZPL (conversion {err.conversion_id})")
except APIConnectionError as err:
    print(f"Network problem: {err}")
Exception Status error.code Extra fields
BadRequestError 400 invalid_request param
AuthenticationError 401 missing_api_key · invalid_api_key —
QuotaExceededError 402 quota_exceeded plan, quota, used, resets_at
PermissionDeniedError 403 hosting_not_allowed · no_retention_enforced —
PayloadTooLargeError 413 payload_too_large —
RateLimitError 429 rate_limit_exceeded retry_after, retry_at
ConversionFailedError 502 conversion_failed conversion_id
ServiceUnavailableError 503 service_unavailable retry_after
APIError any anything else status, code, raw
APITimeoutError — (an attempt timed out) —
APIConnectionError — (request never got a response) —

All of these extend ZplJetError, and every HTTP error carries status, code, doc_url, and the raw error payload in raw. Full code reference: zpljet.com/docs/errors.

Retries

Rate limits, transient 5xx responses, timeouts, and network failures retry up to twice by default. Retries use exponential backoff and honor Retry-After. conversion_failed is never retried.

# Client-wide
zpljet = ZplJet(api_key=key, max_retries=5)

# Or per request
zpljet.convert(zpl=zpl, max_retries=0)  # fail fast

Timeouts

Each attempt has a 60-second timeout by default:

zpljet = ZplJet(api_key=key, timeout=10.0)

# Per request:
zpljet.convert(zpl=zpl, timeout=5.0)

A timed-out attempt raises APITimeoutError (after retries).

Configuration

zpljet = ZplJet(
    api_key="zpl_…",                     # required
    base_url="https://api.zpljet.com",   # default
    timeout=60.0,                        # per-attempt timeout, seconds
    max_retries=2,                       # automatic retries
    transport=my_transport,              # custom transport (proxies, tests)
)

The default transport uses stdlib urllib. For connection pooling, inject any callable matching (url, body, headers, timeout) -> TransportResponse.

Examples

Runnable scripts live in examples/:

ZPLJET_API_KEY=zpl_… python examples/01_convert_to_pdf.py

Contributing & development

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

ruff check .
mypy
pytest

ZPLJET_API_KEY=zpl_… pytest tests/test_e2e.py

License

MIT

Metadata

Release files for zpljet 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zpljet 2.0.0
File Size Uploaded
zpljet-2.0.0.tar.gz 18.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zpljet 2.0.0
File Interpreter ABI Platform
zpljet-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.6 kB

Release files / zpljet-2.0.0.tar.gz

Download URL zpljet-2.0.0.tar.gz
Size 18.8 kB
Tags Source
SHA-256 checksum
How to use checksums
9b4621441a7c4f7a8138dd8734601ae10f996ba262bf0a4c2235879565feed9c
BLAKE2b-256 checksum
How to use checksums
ed3cbad2b557882265c696b79cf9c62564a127c9ecc7e138000305a419b4302d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 17, 2026.

Transparency log

Release files / zpljet-2.0.0-py3-none-any.whl

Download URL zpljet-2.0.0-py3-none-any.whl
Size 12.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0f52155cae1235feb42ef2d4081cf9ac74ae00b5f107cbd015f73cc2a3328ad7
BLAKE2b-256 checksum
How to use checksums
d6514dc073c24c3cfc342d28ec863a955f5a722ee061fb8738027fe343e696c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page