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.xuntil 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 None — watermark=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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2b50abdc3c794a294a9b376d23fb467a8b9a414ee7d17b52f123ea59b3ae002
|
|
| MD5 |
7d75a292d37928934e0d9cfb633f3a42
|
|
| BLAKE2b-256 |
b94c3bb4d20be41549b161fec78528983d7a06da407a2c93c84ed6b5d6b1c42c
|
Provenance
The following attestation bundles were made for labelzoom_sdk-0.1.0.tar.gz:
Publisher:
release-python.yml on labelzoom/labelzoom-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
labelzoom_sdk-0.1.0.tar.gz -
Subject digest:
c2b50abdc3c794a294a9b376d23fb467a8b9a414ee7d17b52f123ea59b3ae002 - Sigstore transparency entry: 2519254446
- Sigstore integration time:
-
Permalink:
labelzoom/labelzoom-sdk@25ab942f7b661a179f0a9d5f9bf0e526ff9aa4a9 -
Branch / Tag:
refs/tags/python/v0.1.0 - Owner: https://github.com/labelzoom
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@25ab942f7b661a179f0a9d5f9bf0e526ff9aa4a9 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eda04639f903fbe2d5a08bf73143bd2b83eeb40de3559970967965421e4ef6e3
|
|
| MD5 |
059d3a252f86d15b16b4b97ca77e8583
|
|
| BLAKE2b-256 |
a5e51c064cea4929a5efbeb05d075b1d3e4490d7fc8feff3ecbdf731ee21679b
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
labelzoom_sdk-0.1.0-py3-none-any.whl -
Subject digest:
eda04639f903fbe2d5a08bf73143bd2b83eeb40de3559970967965421e4ef6e3 - Sigstore transparency entry: 2519254481
- Sigstore integration time:
-
Permalink:
labelzoom/labelzoom-sdk@25ab942f7b661a179f0a9d5f9bf0e526ff9aa4a9 -
Branch / Tag:
refs/tags/python/v0.1.0 - Owner: https://github.com/labelzoom
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@25ab942f7b661a179f0a9d5f9bf0e526ff9aa4a9 -
Trigger Event:
release
-
Statement type: