Skip to main content

LabelZoom Logo

LabelZoom Python SDK

Official Python client for the LabelZoom API. Converts barcode labels between ZPL, EPL, TSPL, DPL, PDF, LabelZoom XML/JSON, and raster images.

Python 3.10+. Fully typed (py.typed), sync and async clients, one dependency (httpx).

Install

pip install labelzoom-sdk

The distribution is labelzoom-sdk; the import is labelzoom.

Pre-1.0. The public API is stable in practice and covered by a shared conformance suite, but it stays on 0.x until all seven language SDKs have validated the same contract — two contract-level corrections have already come out of that process.

Quick start

An API key is optional. Without one you get the free tier — watermarked output, first label only, a 1 MB request cap, and no multi-page, JSON-target, or image-to-image conversion.

from pathlib import Path
from labelzoom import LabelZoomClient

with LabelZoomClient() as client:                # anonymous; this works
    result = client.convert(
        "zpl", "png",
        "^XA^FO20,20^A0N,28^FDHello^FS^XZ",
        dpi=300, label_width=4, label_height=6,
    )

Path("label.png").write_bytes(result.content)

result.content is the authoritative payload — five of the eight targets are binary. result.text decodes it using the response charset for the textual ones.

With a key — passed explicitly, or picked up from LABELZOOM_API_KEY:

client = LabelZoomClient("lz_live_...")
from_env = LabelZoomClient()                     # reads LABELZOOM_API_KEY
anonymous = LabelZoomClient(None)                # forces anonymous, ignoring the env

Omitting the argument and passing None mean different things on purpose: omitting it consults the environment, and None (or "") suppresses that fallback.

Filling variable fields — each record produces one label:

result = client.convert(
    "zpl", "pdf", template,
    data=[{"name": "ACME Corp", "sku": "12345"},
          {"name": "Globex", "sku": "67890"}],
)                                                # a 2-page PDF

Async

AsyncLabelZoomClient is a mirror, not a wrapper — same arguments, same behaviour, awaited. The test suite asserts the two signatures are identical, so they cannot drift.

import asyncio
from labelzoom import AsyncLabelZoomClient

async def main() -> None:
    async with AsyncLabelZoomClient() as client:
        result = await client.convert("zpl", "png", zpl, dpi=300)
        print(result.status, result.content_type, len(result.content))

asyncio.run(main())

Formats

SourceFormat = Literal["zpl", "epl", "tspl", "dpl", "xml", "json",
                       "pdf", "png", "bmp", "gif", "jpeg", "jpg", "url"]

TargetFormat = Literal["zpl", "xml", "json", "pdf", "png", "bmp", "gif", "jpeg"]

epl, tspl and dpl are source-only on the server, and the type system says so:

client.convert("pdf", "epl", body)
#                     ^^^^^ error: Argument 2 has incompatible type "Literal['epl']";
#                            expected "Literal['zpl', 'xml', 'json', 'pdf', ...]"

A mypy error, not a runtime 404. Run mypy and you find it before you ship; the SDK also raises locally rather than round-tripping to the server.

"url" as the source has the server fetch a URL you supply and convert what it finds. Validate the URL first if it came from untrusted input.

Options

Options are keyword arguments with the API's nesting flattened by underscores — label.width becomes label_width, pdf.pageNumber becomes pdf_page_number.

Keyword Notes
dpi server default 203
rotation must be a multiple of 90; rejected locally otherwise
scaling percent, server default 100
color_mode "BW", "GRAYSCALE" (default), "COLOR"
darkness 0–100, server default 70
position_x, position_y pixel offset of the extracted region
watermark forced on for the free tier regardless
dialect e.g. "moca"; paid
label_width, label_height inches, not dots
pdf_conversion_mode "IMAGE" (default) or "NATIVE"
pdf_page_number 0-based; omit to convert every page
zpl_commands_to_ignore e.g. ["^PQ"]
zpl_image_compression "Z64" (default) or "COMPRESSED_HEX"
data one label per record
extra anything not modeled yet; merged in as-is

Only options you actually set are sent. The SDK never fills in a client-side default, so a change to a server default reaches you without an SDK upgrade. That is why unset options use an UNSET sentinel rather than Nonewatermark=False is a value you can deliberately send.

For building a request programmatically, ConversionOptions is the same field set as a frozen dataclass:

from labelzoom import ConversionOptions

preset = ConversionOptions(dpi=300, label_width=4, label_height=6)
result = client.convert("zpl", "png", zpl, options=preset, rotation=90)

Keyword arguments passed alongside options win.

Errors

Every non-2xx raises a typed error carrying the server's own message, the raw body, and the X-LZ-Request-Id support handle. The body is never discarded.

from labelzoom import ForbiddenError, LabelZoomError

try:
    client.convert("zpl", "json", zpl)
except ForbiddenError as error:
    if error.is_paid_feature:
        # "JSON export is a paid feature" — the most common free-tier failure.
        print(f"{error.message} (request {error.request_id})")
except LabelZoomError as error:
    print(f"{error.status}: {error.message}")

BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, PayloadTooLargeError, RateLimitedError and ServerError all subclass LabelZoomError.

LabelZoomValidationError deliberately does not — it means the calling code is wrong, it never reaches the network, and it should not be swallowed by a handler written for server failures. It subclasses ValueError.

Retries

429, 5xx and transport failures are retried automatically: 3 attempts, 1s/2s/4s with full jitter, honouring a longer Retry-After. Other 4xx responses raise immediately — a malformed request will not become valid on a second attempt.

client = LabelZoomClient(max_retries=0, timeout=30.0)

Testing your own code

The transport and the sleep function are both injectable, so retry logic can be tested without spending the wall-clock time:

import httpx
from labelzoom import LabelZoomClient

slept: list[float] = []
client = LabelZoomClient(
    None,
    transport=httpx.MockTransport(lambda request: httpx.Response(200, text="^XA^XZ")),
    use_jitter=False,
    sleep=slept.append,
)

Pass http_client=httpx.Client(...) instead to share a connection pool with the rest of your application. The SDK will not close a client it did not create.

Development

pip install -e ".[dev]"
pytest            # offline; no key, no network
mypy
ruff check

The test suite runs the shared conformance fixtures that every LabelZoom SDK is checked against, executes each one through both the sync and async clients, and asserts it ran all of them. See docs/CONFORMANCE.md.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

labelzoom_sdk-0.1.0.tar.gz (16.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

labelzoom_sdk-0.1.0-py3-none-any.whl (19.5 kB view details)

Uploaded Python 3

File details

Details for the file labelzoom_sdk-0.1.0.tar.gz.

File metadata

  • Download URL: labelzoom_sdk-0.1.0.tar.gz
  • Upload date:
  • Size: 16.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for labelzoom_sdk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c2b50abdc3c794a294a9b376d23fb467a8b9a414ee7d17b52f123ea59b3ae002
MD5 7d75a292d37928934e0d9cfb633f3a42
BLAKE2b-256 b94c3bb4d20be41549b161fec78528983d7a06da407a2c93c84ed6b5d6b1c42c

See more details on using hashes here.

Provenance

The following attestation bundles were made for labelzoom_sdk-0.1.0.tar.gz:

Publisher: release-python.yml on labelzoom/labelzoom-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file labelzoom_sdk-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: labelzoom_sdk-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for labelzoom_sdk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 eda04639f903fbe2d5a08bf73143bd2b83eeb40de3559970967965421e4ef6e3
MD5 059d3a252f86d15b16b4b97ca77e8583
BLAKE2b-256 a5e51c064cea4929a5efbeb05d075b1d3e4490d7fc8feff3ecbdf731ee21679b

See more details on using hashes here.

Provenance

The following attestation bundles were made for labelzoom_sdk-0.1.0-py3-none-any.whl:

Publisher: release-python.yml on labelzoom/labelzoom-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page