ippx
Sync and async IPP/IPPS client for Python. Send print jobs and monitor network printers, including printers exposed over the internet behind TLS with source IP allowlisting.
Built on httpx with a pure sans-IO codec for the IPP binary protocol (RFC 8010) and the RFC 8011 required operation set.
ippx is a plain library: the only runtime dependency is httpx. No framework, no server, no Docker required. Use it from a script, a CLI, a cron job, a serverless function, or an async web app.
Why
pyipp is monitoring only. ippx can actually print, in both async (FastAPI-native) and sync code, with first-class support for the realities of network printers: self-signed certificates, legacy cipher suites, HTTP Basic auth, and flaky WAN links.
Install
pip install ippx
Python 3.11+. Single runtime dependency: httpx. Fully typed (PEP 561).
Quick start (async, e.g. inside FastAPI)
from ippx import AsyncIppClient, BasicAuth, TlsConfig
async def print_invoice(pdf_bytes: bytes) -> None:
async with AsyncIppClient(
"ipps://printer.example.com:631/ipp/print",
auth=BasicAuth("user", "password"),
tls=TlsConfig(fingerprint="sha256:AB:CD:..."),
) as printer:
job = await printer.print_job(
pdf_bytes,
document_format="application/pdf",
job_name="invoice-123",
job_attributes={"copies": 1, "sides": "two-sided-long-edge"},
)
result = await printer.wait_for_job(job.job_id, timeout=120)
print(result.state, result.state_reasons)
Quick start (sync)
from ippx import IppClient
with IppClient("ipps://203.0.113.10:631/ipp/print") as printer:
caps = printer.get_printer_attributes()
print(caps.make_and_model, caps.state, caps.document_formats)
job = printer.print_job(pdf_bytes, document_format="application/pdf")
Operations
The RFC 8011 required operation set, supported by every conformant printer:
| Method | IPP operation |
|---|---|
print_job(document, ...) |
Print-Job |
validate_job(...) |
Validate-Job (pre-flight without printing) |
cancel_job(job_id) |
Cancel-Job |
get_printer_attributes(...) |
Get-Printer-Attributes |
get_job_attributes(job_id, ...) |
Get-Job-Attributes |
get_jobs(...) |
Get-Jobs |
wait_for_job(job_id, timeout=...) |
polling helper over Get-Job-Attributes |
wait_for_job polls with exponential backoff (1s doubling to a 15s cap by
default) until the job reaches a terminal state (completed, canceled,
aborted) and raises JobTimeoutError otherwise.
A printer's full operations-supported list decodes to the named Operation
enum (the complete IANA IPP registry, not just the implemented set), so you
can introspect capabilities like Operation.IDENTIFY_PRINTER.
Note that for get_jobs most printers only return job-id and job-uri by
default, per RFC 8011; pass
requested_attributes=["job-id", "job-state", "job-name"] to get more.
Authentication
- HTTP Basic:
auth=ippx.BasicAuth("user", "pw")(or a plain("user", "pw")tuple) - HTTP Digest:
auth=ippx.DigestAuth("user", "pw") - TLS client certificate:
tls=TlsConfig(client_cert=("cert.pem", "key.pem")) - requesting-user-name: set via
requesting_user_name=, sent on every request (identification only, not authentication)
TLS
Printers almost universally ship self-signed certificates. TlsConfig gives
you four options, strongest first:
TlsConfig() # system CA validation (default)
TlsConfig(verify="/path/to/printer-ca.pem") # custom CA bundle
TlsConfig(fingerprint="sha256:AB:CD:...") # pin the exact certificate
TlsConfig(verify=False) # no validation (last resort)
Fingerprint pinning verifies the SHA-256 digest of the server certificate during the TLS handshake itself, so there is no window between checking and using the connection. Get a printer's fingerprint with:
openssl s_client -connect printer:631 < /dev/null 2>/dev/null \
| openssl x509 -fingerprint -sha256 -noout
For an internet-exposed printer (port forward locked to a source WAN IP), the recommended setup is fingerprint pinning plus HTTP Basic auth.
Two behaviours worth knowing:
- Printers very commonly offer only plain-RSA key exchange (no ECDHE), which
stock OpenSSL policy rejects with a bare handshake-failure alert. Every
context ippx builds therefore uses
DEFAULT:@SECLEVEL=1ciphers so real hardware is reachable and genuine certificate problems surface as truthful certificate errors; override withTlsConfig(ciphers=...)to tighten or loosen. - With a custom CA bundle, hostname verification is disabled (printers are usually addressed by IP), so any certificate issued by that CA is accepted.
Job attributes
Pass common Job Template attributes as a plain dict; tags are inferred for
copies, sides, media, print-quality, print-color-mode,
orientation-requested, output-bin, number-up, printer-resolution and
others. For anything else, pass ippx.Attribute objects with explicit tags.
Verified hardware
| Printer | Print-Job | Status | Notes |
|---|---|---|---|
| HP Color LaserJet Pro M283fdw | yes | verified | Full IPPS path with fingerprint pinning: Get-Printer-Attributes (128 attrs incl. nested media-col collections), Validate-Job, Print-Job to completion, job polling, Get-Jobs, sync and async. Offers RSA-key-exchange ciphers only, handled by the default TlsConfig cipher policy. |
Also verified end-to-end against a live CUPS 2.4 server (Get-Printer-Attributes, Validate-Job, Print-Job, job polling to completion, Get-Jobs, sync and async clients). Contributions to this table are welcome.
Not in scope (yet)
- Document conversion: bring your own PDF/PCL/PWG raster bytes
- Create-Job / Send-Document multi-document jobs
- IPP event subscriptions (RFC 3995): printer firmware support is too rare
to rely on; use
wait_for_jobpolling - Encoding IPP collections in requests (decoding is fully supported)
- mDNS/driverless discovery
Development
pip install -e .[dev]
ruff check src tests
pytest
License
MIT
Metadata
Release files for ippx 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ippx-0.1.1.tar.gz | 23.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ippx-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.6 kB
Release files / ippx-0.1.1.tar.gz
| Download URL | ippx-0.1.1.tar.gz |
|---|---|
| Size | 23.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ce7a6555f27beeecb8ad09defd56f124ea660dc0de49f4203fe757c1bf50e94c
|
|
BLAKE2b-256 checksum How to use checksums |
aaaa2865b63cb172fca3fdfb13671918e8dc6a86cf768fb048f75630f0b678d8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 23, 2026.
Transparency logRelease files / ippx-0.1.1-py3-none-any.whl
| Download URL | ippx-0.1.1-py3-none-any.whl |
|---|---|
| Size | 20.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1e9f7cc26167a160d438d134412b7eb55408ac6f8ac5d8401a50c3b0ff187fc9
|
|
BLAKE2b-256 checksum How to use checksums |
4d3c61acd5a99c7e1b404dad4971f966a1fbf2553ba9d01660e36b4e72ad78af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 23, 2026.
Transparency log