Authentication and conventions
Everything on this page is true of every endpoint: how you authenticate, what a request and a response look like, how lists page, and what an error actually tells you. Reading it once is what keeps the resource pages short.
Two things here are worth knowing before you write any code. Test mode gives you a virtual device and printer that runs the full job lifecycle, so an integration can be built and verified end to end before any hardware exists. And every response carries a Request-Id, which is the one value support needs to find your call in the logs.
Client libraries
The API is plain HTTP, so any language works. There are official clients for Node.js (printsocket on npm), PHP (printsocket/printsocket on Packagist), and Python (printsocket on PyPI). All of them cover every endpoint on these pages and handle the conventions below for you: retries with idempotency keys, cursor pagination, typed errors, and webhook signature verification. The source lives in the print-socket organization on GitHub: printsocket-node, printsocket-php, and printsocket-python.
npm install printsocket
composer require printsocket/printsocket
pip install printsocket
Node.js:
import PrintSocket from "printsocket";
const ps = new PrintSocket({ apiKey: process.env.PRINTSOCKET_API_KEY });
const job = await ps.jobs.create({
printer_id: "prn_8f2k1",
title: "Order #12345 label",
content: { format: "pdf", url: "https://example.com/label.pdf" },
});
PHP:
use PrintSocket\PrintSocketClient;
$ps = new PrintSocketClient(['api_key' => getenv('PRINTSOCKET_API_KEY')]);
$job = $ps->jobs->create([
'printer_id' => 'prn_8f2k1',
'title' => 'Order #12345 label',
'content' => ['format' => 'pdf', 'url' => 'https://example.com/label.pdf'],
]);
Python:
import os
import printsocket
ps = printsocket.PrintSocket(api_key=os.environ["PRINTSOCKET_API_KEY"])
job = ps.jobs.create({
"printer_id": "prn_8f2k1",
"title": "Order #12345 label",
"content": {"format": "pdf", "url": "https://example.com/label.pdf"},
})
From any other language, use the conventions on this page directly; nothing in the API requires a client library.
Machine-readable spec
Every endpoint on these pages is described by an OpenAPI 3.1 document:
https://www.printsocket.com/openapi.jsonhttps://api.printsocket.com/v1/openapi.json(the same bytes, beside the API)
Point a code generator at it to get a client in a language we do not publish one for, import it into Postman or Insomnia to get a request collection, or read it to see the exact shape of every request and response, including which fields can be null.
The document covers the customer-facing API only. The endpoints under /agent are the protocol between PrintSocket and its own agent, they are authenticated by a device token rather than an API key, and they are deliberately left out: they are free to change.
Keys and scopes
Authorization: Bearer <key>on every request except/ping.- Keys look like
sk_live_...orsk_test_.... The prefix identifies the mode. Secrets are shown once, at creation. - Each key carries a scope:
read(GET only),print(read + create/cancel jobs), ormanage(everything, including key and webhook management).
Test mode
sk_test_ keys get a virtual device and printer that simulates the full job lifecycle: queued → sent → printing → succeeded. You can build and test an integration end to end before any hardware is enrolled. Jobs sent to the virtual printer never count toward plan limits, on any plan.
Test-mode data is completely separate from live data: a test-mode key cannot see, list, or print to anything in the live pool, or the reverse. That makes test mode the right place to enroll a real printer you are experimenting with, since it can never appear in the fleet your production code enumerates. Real hardware bills the same in either pool, though: a job to a real printer counts whichever mode it was created in, because the paper is real either way. Your first two real devices in the test pool are exempt from the device limit, so checking an integration against hardware costs you nothing.
Utility
| Method | Path | Notes |
|---|---|---|
| GET | /ping | Unauthenticated liveness check. { "ok": true } |
| GET | /me | Introspect the calling key: account, key id, scopes, and mode (live/test) |
Requests and responses
- JSON only (
application/json),snake_casefields, timestamps in RFC 3339 UTC (2026-07-29T14:02:11Z). - Durations are always explicit-unit fields (
expire_after_seconds), never bare numbers. - Every response carries a
Request-Idheader, and every error echoes it in the body. Quote it in support requests. - IDs are opaque and prefixed by type:
dev_device,prn_printer,job_job,doc_document,whk_webhook,key_API key,scl_scale. Treat them as strings; don't parse them.
Pagination
All list endpoints are cursor-based, newest first by default.
Request: ?limit=50&cursor=<opaque>&order=asc|desc (limit default 25, max 100)
{
"data": [ ... ],
"has_more": true,
"next_cursor": "eyJpZCI6..."
}
Pass next_cursor back as cursor to fetch the next page. Stop when has_more is false.
Errors
One shape everywhere:
{
"error": {
"type": "invalid_request | authentication | permission | not_found | conflict | rate_limited | billing | server",
"code": "printer_offline",
"message": "Printer prn_8f2k1 is offline; the job was not accepted because queue_if_offline=false.",
"param": "printer_id",
"request_id": "req_9x4..."
}
}
Branch on type for broad handling and code for specific cases. message is human-readable and may change; param names the offending field on validation errors.
Status codes: 200/201, 400 (validation), 401 (auth), 402 (plan limit, type billing), 403 (scope), 404, 409 (state conflict), 413 (payload over 50 MB), 422 (semantically invalid, e.g. an option the printer doesn't support), 429 (with Retry-After), 5xx.
Plan limits return 402 with type billing, in either mode: creating a job to a real printer past your plan's monthly job cap gives plan_limit_reached (the message names the plan and the reset date), and enrolling past the device cap gives device_limit_reached.
Idempotency
Send Idempotency-Key: <string> on any POST. A replay of the same key within 24 h returns the stored original response (the same status and the same body), so a network retry can't create a duplicate job. There is no conflict to recover from; just retry with the same key.
Rate limiting
- Standard headers on every response:
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset. - Sustained excess returns 429 with
Retry-After. The default is 20 requests/second per account, burstable.
Metadata
Every mutable resource accepts "metadata": { "order_id": "12345", ... }, with up to 20 keys and string values of 500 characters or less. Attach your own identifiers, then filter by them on any list endpoint: ?metadata[order_id]=12345. This is the intended way to correlate PrintSocket resources with records in your own system.
Versioning
The major version is in the path (/v1). Additive changes (new fields, new endpoints, new enum values) are not breaking and can land at any time, so parse defensively. Breaking changes would ship as /v2.