Beta: the API is free for everyone until 2026-11-30. No card needed.

API reference

PrintHub follows PrintNode's REST API: the same paths, field names and auth. Code written for PrintNode works after changing the base URL. All requests and responses are JSON.

https://printhub.uulen.xyz/api/v1

Quick start

  • Sign up and create an API key under API keys.
  • Install PrintHub.exe on a Windows PC and pair it.
  • List your printers, then send a job to one of them:
curl -u "$PRINTHUB_API_KEY:" https://printhub.uulen.xyz/api/v1/printers

curl -u "$PRINTHUB_API_KEY:" https://printhub.uulen.xyz/api/v1/printjobs \
  -H "Content-Type: application/json" \
  -d '{"printerId": 34, "title": "Hello", "contentType": "pdf_uri",
       "content": "https://example.com/hello.pdf"}'

Node.js:

const auth = "Basic " + Buffer.from(process.env.PRINTHUB_API_KEY + ":").toString("base64");
const res = await fetch("https://printhub.uulen.xyz/api/v1/printjobs", {
  method: "POST",
  headers: { Authorization: auth, "Content-Type": "application/json" },
  body: JSON.stringify({
    printerId: 34,
    title: "Receipt",
    contentType: "raw_base64",
    content: Buffer.from("\x1b@Hello\n\n\n\x1dV\x00").toString("base64"),
  }),
});
const jobId = await res.json(); // 50731

Python:

import requests
r = requests.post("https://printhub.uulen.xyz/api/v1/printjobs",
                  auth=(API_KEY, ""),
                  json={"printerId": 34, "title": "Label", "contentType": "pdf_base64",
                        "content": base64.b64encode(open("label.pdf", "rb").read()).decode()})
print(r.json())

Authentication

HTTP Basic auth with the API key as the username and an empty password. Keep keys on your server; anyone with a key can print to your printers. Revoke a key in the console at any time.

Authorization: Basic base64("phk_yourkey:")

Errors

Errors return a 4xx or 5xx status and this body:

{ "uid": "8b1f…", "code": "NotFound", "message": "Printer 99 not found" }
StatusMeaning
400Bad request: a missing or invalid field
401API key missing, wrong or revoked
404No such endpoint, or the object is not in your account
409X-Idempotency-Key already used, or a duplicate email / creatorRef
413Job content larger than 30 MB

Sets and paging

Where a path takes {set}, pass one id or several separated by commas, e.g. /printers/34,35. Lists accept limit (default 100, max 1000), dir (asc or desc, by id) and after (an id to continue after).

Account

GET /whoamiYour account: id, email, connected computer ids, totals, sub-accounts.
GET /pingReturns "pong". Use it to check a key.

Computers

A computer is a PC running PrintHub.exe. state is connected while it is online.

GET /computers[/{set}]List computers.
GET /computers/{set}/printers[/{set}]Printers on those computers. The printer set may be ids or names.
DELETE /computers/{set}Remove computers, their printers and jobs. Returns the deleted ids.
{ "id": 12, "name": "FRONT-DESK", "hostname": "FRONT-DESK", "inet": "192.168.1.20",
  "version": "PrintHub-Client/1.0.0", "createTimestamp": "2026-11-02T08:00:00.000Z",
  "state": "connected" }

Printers

GET /printers[/{set}]List printers.
GET /printers/{set}/printjobs[/{set}]Jobs on those printers.
DELETE /printers/{set}/printjobs[/{set}]Cancel their jobs that have not been sent yet.
{ "id": 34, "name": "ZDesigner GK420d", "description": "ZDesigner GK420d", "default": false,
  "state": "online", "computer": { "id": 12, … },
  "capabilities": { "bins": ["Auto"], "collate": false, "color": false, "copies": 999,
                    "dpis": ["203x203"], "duplex": false,
                    "papers": { "4x6": [1016, 1524] }, … } }

Print jobs

POST /printjobsCreate a job. Returns its id with status 201.
GET /printjobs[/{set}]List jobs.
DELETE /printjobs[/{set}]Cancel jobs that have not been sent to the computer yet. Returns the cancelled ids.

POST /printjobs body

FieldDescription
printerIdRequired. The printer id.
contentTypeRequired. pdf_uri, pdf_base64, raw_uri or raw_base64.
contentRequired. A public http(s) URL for *_uri, or base64 data. Up to 30 MB.
titleShown in the console and the Windows print queue.
optionsSee print options.
qtyPrint the job this many times (default 1).
expireAfterSeconds before an unsent job expires (default 14 days).
authentication{"type":"BasicAuth","credentials":{"user":"…","pass":"…"}} for a protected *_uri.

Send an X-Idempotency-Key header to make retries safe: a second job with the same key returns 409. URI content is downloaded by PrintHub when the job is created, so the PC never needs access to your server.

Print options

OptionDescription
copiesNumber of copies.
duplexone-sided, long-edge or short-edge.
paperA paper name from the printer's capabilities.papers.
binA tray from capabilities.bins.
colortrue for color, false for monochrome.
dpiA value from capabilities.dpis, e.g. "600x600".
collateCollate copies.
pagesPDF only. Page ranges, e.g. "1,3-5".
fit_to_pagePDF only. Scale to the paper (default true).
rotatePDF only. 0, 90, 180 or 270.

Job states

GET /printjobs/{set}/states returns, for each job, its state changes oldest first.GET /printjobs/states does the same for all jobs.

StateMeaning
newAccepted, waiting for the computer.
sent_to_clientDownloaded by PrintHub.exe.
in_progressBeing spooled to the printer (state history only).
doneHanded to the Windows spooler successfully.
errorPrinting failed. message says why.
deletedCancelled before it was sent.
expiredNot delivered before expireAfter.
[[{ "printJobId": 50731, "state": "new", "message": null, "age": 0, … },
  { "printJobId": 50731, "state": "sent_to_client", "age": 412, … },
  { "printJobId": 50731, "state": "done", "clientVersion": "PrintHub-Client/1.0.0", "age": 1630, … }]]

Sub-accounts

Integrators manage an account per customer. Each sub-account has its own computers, printers, jobs and keys. Ask support@uulen.xyz to enable them, or manage them in the console under Integrators.

POST /accountCreate a sub-account. Body: {"Account":{"email","firstname","lastname","password","creatorRef"},"ApiKeys":["prod"]}. Only email is required.
PATCH /accountChange the sub-account named by the header.
DELETE /accountDelete the sub-account named by the header, with all its data.
POST /account/apikey/{name}Create a key for the acting account. Returns the key.
DELETE /account/apikey/{name}Revoke the acting account's keys with that name.

To act as a sub-account, send your own key plus exactly one of these headers on any request:

X-Child-Account-By-Id: clx…
X-Child-Account-By-Email: shop@example.com
X-Child-Account-By-CreatorRef: customer-1042

Pairing codes

POST /account/pairingcode returns {"code":"ABCD-EFGH","expiresAt":"…"} for the acting account. Run PrintHub.exe --pair ABCD-EFGH on the customer's PC to connect it without opening the console. Codes are single use and last 15 minutes.