Receipt Printer API: the Complete Guide

Every way software prints receipts in 2026 — ESC/POS, port 9100, vendor SDKs, cloud relays — and how a local HTTP API on the LAN compares.

Published 2026-07-24 · Updated 2026-09-29 · For developers →

There is no single "receipt printer API." What exists is a stack of seven approaches — OS drivers, raw TCP on port 9100, direct ESC/POS over USB or serial, vendor SDKs like Epson ePOS and Star CloudPRNT, cloud relays like PrintNode, local agents like QZ Tray, and LAN HTTP APIs — each with different latency, hardware lock-in, and failure modes. This guide covers all of them with working code, so you can pick the right one before you write a line of integration.

The seven ways software prints receipts

Every receipt that has ever printed traveled one of these paths:

#ApproachTransportStatus feedbackWorks offlineHardware lock-in
1OS driver / OPOS / JavaPOSOS print spoolerDriver-dependentYesPer-model drivers
2Raw TCP port 9100LAN socketDLE EOT (if you implement it)YesNone — any ESC/POS printer
3Direct ESC/POS over USB/serial/BTLocal busDLE EOT / ASBYesNone, but per-OS transport code
4Epson ePOSHTTP to the printerRich, built inYes (LAN)Epson intelligent printers only
5Star CloudPRNTPrinter polls your serverReported on pollNo — needs your server reachableStar printers only
6Cloud relay (PrintNode hosted, BizPrint)Internet round-tripJob-levelNoNone, but needs a PC agent on site
7LAN HTTP API (print server / node)HTTP on the LANDepends on implementationYesNone

The rest of this guide walks each row: what it is, code where code is useful, and where it breaks.

1. OS drivers, OPOS, and JavaPOS

The oldest path: install a Windows driver (or CUPS on Linux/macOS), and the printer appears as a system printer. OPOS and JavaPOS are 1990s-era standards that wrap the driver in a POS-flavored object model — Printer.PrintNormal(), CashDrawer.OpenDrawer().

It still works, and for classic fat-client Windows POS software it is often the path of least resistance. The costs show up over time: a driver per printer model per OS version, spooler stalls that present as "printer offline" tickets, and no story at all for web apps, mobile apps, or Linux kiosks. Modern POS development has largely moved off this path, which is why the rest of this list exists.

2. Raw TCP on port 9100

Almost every networked receipt printer listens on TCP port 9100 and prints whatever bytes arrive. No handshake, no header, no acknowledgment — you open a socket, write ESC/POS, and close it. This is the same "JetDirect" convention office printers use, and it is the closest thing POS printing has to a universal API.

// Node.js — print a receipt to any network ESC/POS printer, zero dependencies
import net from "node:net";
 
const receipt = Buffer.concat([
  Buffer.from([0x1b, 0x40]),            // ESC @  — initialize
  Buffer.from("COFFEE CORNER\n"),
  Buffer.from("1x Flat white     4.50\n"),
  Buffer.from([0x1b, 0x64, 0x05]),      // ESC d 5 — feed 5 lines
  Buffer.from([0x1d, 0x56, 0x00]),      // GS V 0 — full cut
]);
 
const socket = net.createConnection(9100, "192.168.1.87", () => {
  socket.end(receipt);
});

That is a complete, working integration. It is also the whole problem: the socket accepts your bytes whether or not the printer has paper, has its cover open, or is on fire. Getting real feedback means interleaving DLE EOT status queries into the stream and parsing single-byte replies — see our guides to port 9100 printing and the DLE EOT status commands for the full picture.

3. Direct ESC/POS over USB, serial, or Bluetooth

Same bytes, different pipe. For a USB or serial printer you talk to the device node directly, usually through a library that hides the transport. In Python, python-escpos is the mature option and maintains the community's database of per-printer quirks:

from escpos.printer import Usb
 
# VID/PID from lsusb; profile picks the right code page + feature set
p = Usb(0x04b8, 0x0e28, profile="TM-T88III")
p.text("COFFEE CORNER\n")
p.barcode("4006381333931", "EAN13")
p.cut()

The catch is that "ESC/POS" is a standard every manufacturer forks. Code that renders perfectly on an Epson garbles on a $60 generic — wrong code page, unsupported cut variant, different image commands. That fragmentation is documented enough that python-escpos needed an entire capabilities database (escpos-printer-db) to cope. Our ESC/POS command reference covers the commands and the quirks in byte-level detail.

You also inherit the transport: USB permissions on Linux, driver interference on Windows, Bluetooth pairing flows on Android. Each one is per-OS code you now own.

4. Vendor SDKs: Epson ePOS, StarPRNT, Star CloudPRNT

The printer vendors solved fragmentation by selling you a nicer API that only works on their hardware.

Epson ePOS — Epson's "intelligent" TM printers run an embedded web service. Your app POSTs ePOS-Print XML (or uses the JS/mobile SDK) over HTTP straight to the printer. No drivers, rich status built in. Epson's SDK overview documents browser printing on supported models. We have not qualified current browser permissions, CORS or certificate behavior. The SDK is free; the monetization is the hardware: a TM-m30III runs $275–541 and an OmniLink TM-T88VII $370–571, versus $40–160 for a generic ESC/POS printer.

StarPRNT — Star's equivalent command language plus SDKs for iOS, Android, and desktop. Same shape: good tooling, Star hardware only.

Star CloudPRNT — inverts the flow. The printer polls a URL on your server every few seconds asking "anything to print?" and pulls the job down. Brilliant for online ordering (no inbound firewall holes at the restaurant), but you must build and host a compliant server, and in an HTTP-poll deployment every ticket waits on the poll interval. Star also documents an MQTT method for newer models; which mode a given printer and server run is theirs to state.

The full architectural trade-offs — push vs poll, latency, status — are in ESC/POS vs ePOS vs CloudPRNT vs StarPRNT.

5. Cloud print relays: PrintNode and friends

A cloud relay gives you a true REST API: POST a job to their servers, and a desktop client running on a PC at the venue pulls it down and prints via local drivers.

curl -u "$PRINTNODE_API_KEY:" https://api.printnode.com/printjobs \
  -H "Content-Type: application/json" \
  -d '{ "printerId": 4212, "contentType": "raw_base64", "content": "G0BIZWxsbwodVgA=" }'

PrintNode is the category leader: free for 50 prints/month, then $9/$29/$99 monthly tiers by volume, with integrator plans at $60–$500/month. It is genuinely convenient for e-commerce order printing across many sites you do not control.

For PrintNode hosted plans, a client computer must remain available and new delivery requires connectivity to the hosted service. Already downloaded or OS-spooled jobs may behave differently. PrintNode also offers a separately quoted Standalone Server for private deployment; the hosted connectivity assumptions do not establish its behavior. These options and the listed USD prices were checked September 19, 2026 on the linked pricing page.

6. Local agents: QZ Tray

QZ Tray bridges browser printing through a Java application. A workstation installation uses a local WebSocket; its dedicated print-server deployment also supports mobile-client arrangements, including iOS and Android, without an agent installed on every tablet. Client connection and certificate setup still need testing on the intended browsers.

Operational work includes keeping the workstation or print server running and maintaining signing and trust. Published first-year packages are $749 Premium Support and $3,499 Company Branded, checked September 19, 2026; renewal and multi-year rates differ. Silent operation requires correctly configured message signing, not merely a purchase. Our guide to printing from a web app compares this path against WebUSB and LAN alternatives.

7. A LAN HTTP API

The gap in the list so far: nothing gives you a modern JSON API that is local. Cloud relays have the API but not the locality; port 9100 has the locality but no API, no discovery, and no honest status unless you implement a binary protocol yourself.

Proxy Nodes closes that gap. A node is a palm-size device that sits next to the printer and serves one HTTP/JSON API on the LAN — Nodeware 1.2.55, driving a real thermal printer end-to-end: USB enumeration, print, cut, live paper/cover/drawer state. A native desktop app, a mobile app or an on-site backend on that network prints with a plain HTTP call. A browser page prints through the node-served UI or through a backend on the page's own origin — being on the same LAN is not by itself enough, and printing from a web app sets out why:

curl http://proxynodes-pn-a1b2.local/print \
  -H "Content-Type: application/json" \
  -d '{
    "type": "print",
    "id": "job-42",
    "endpoint": "receipt",
    "lines": [
      { "type": "text", "value": "COFFEE CORNER", "align": "center", "bold": true },
      { "type": "text", "value": "1x Flat white     4.50", "align": "left" },
      { "type": "feed", "lines": 2 },
      { "type": "cut" }
    ]
  }'

The core routes are POST /print, GET /status, a server-sent-events stream at GET /events, GET/PUT /config, POST /drawer/kick, POST /raw, GET /peers, and GET /scans; networking, auth, updates and diagnostics routes sit alongside them, and the API docs link the full table. Status is one request away, and it is honest: when the node genuinely cannot take a reading — say, a write-only printer that ignores status queries — the driver layer reports that reading as unknown rather than a confident guess. A healthy node answers:

{
  "online": true,
  "printers": [
    { "endpoint": "receipt", "online": true, "paperOut": false, "coverOpen": false, "drawerOpen": false }
  ]
}

Nodes advertise over mDNS (_proxynodes._tcp, proxynodes-<id>.local), and a printer compatibility profile (epson-tm-m30, epson-tm-t88, star-tsp100, custom) re-advertises live, without a reboot — though no point-of-sale system has yet accepted one of those identities in our presence. So that legacy systems need no code change to print, every node also accepts raw ESC/POS on port 9100 — DLE EOT status replies included — at the same time as the HTTP API. Your new stack and the incumbent POS print through the same device on the same day. Printing is the part that migrates by address alone: raw drawer-pulse bytes (ESC p, DLE DC4) are dropped on that port, so a POS that opens the till inside its print job must call POST /drawer/kick instead, or record the drawer as unavailable. Nothing in the print path touches the internet.

The numbers come from real hardware, not a datasheet: the cash-drawer kick pulse fires in 0.15 s; a printer and a barcode scanner have bound together behind a powered USB hub on our bench, the printer in 1,012 ms; and the node wraps text itself against a measured 48-column default (printWidthCols), overridable per receipt or over PUT /config on the LAN — tuning a node is a curl, not a reflash. The shipping hardware is Proxy Node Raw; a scanner beside the printer needs the powered hub accessory, which is not yet qualified with it. ePOS-XML emulation is in development, and so is a CloudPRNT client that would let a node poll a CloudPRNT server and print plain text or print-lines JSON on its ESC/POS printer. Neither has shipped.

For software-only tests, request simulator evaluation access to the developer workspace first. There is no standalone public download. The twin models printing and paper/cover faults; it does not enforce API authentication, deliver configured webhooks or reproduce USB and power failures. Its offline toggle leaves listeners running. Run applicable checks separately on your physical printer and firmware.

Choosing an approach

A compressed decision guide:

  • Legacy Windows POS, printers already installed — stay on drivers/OPOS until something forces the move.
  • You control the app and the printer is networked — raw port 9100 is the simplest thing that works; budget for status handling.
  • All-Epson fleet, budget approved — ePOS is genuinely pleasant; you are paying $200–450 per lane for it.
  • Online ordering into restaurants you don't control, Star hardware — CloudPRNT was built for exactly this.
  • Printing across many remote sites, latency-tolerant — a cloud relay like PrintNode earns its fee.
  • Web POS, managed workstation or print server — evaluate QZ Tray's appropriate deployment; WebUSB where the browser matrix allows.
  • You want a local JSON printing API — evaluate the LAN-node approach against your exact ESC/POS printer and POS. Raw port 9100 is also available, but drawer pulses require the separate HTTP endpoint. Outage operation requires your application and order data to remain available locally. Request simulator evaluation access, then qualify the actual hardware; a simulated pass is not compatibility evidence.

More depth on the developer stack lives at the developers hub.

Frequently asked questions

Is there a standard REST API for receipt printers?
No industry standard exists. Epson ePOS accepts XML over HTTP but only on Epson intelligent printers, and cloud relays like PrintNode expose REST endpoints that route through an on-site agent. Proxy Nodes publishes a documented, vendor-neutral HTTP/JSON protocol with a 146-spec conformance suite — it is an open protocol you can verify, though not an industry standard.
What is the simplest way to print to a network receipt printer from code?
Open a TCP socket to the printer's IP on port 9100 and write ESC/POS bytes. It works in any language with sockets and requires no drivers. The trade-off is zero feedback: you must implement DLE EOT status queries yourself to know whether the print physically happened.
Can I print receipts directly from a browser?
Not to a socket — browsers block raw TCP. Working options are a local agent like QZ Tray, WebUSB or Web Serial for directly attached printers on Chromium browsers, a cloud relay, or an HTTP print service on the LAN reached from a permitted origin. With Proxy Nodes that origin is the node-served UI or an on-site backend on your page's own origin; a hosted page calling a node across origins is not qualified on shipping Nodeware, so a LAN address alone is not enough.
Do cloud print relays work when the internet is down?
New hosted delivery requires service connectivity; already downloaded or spooled jobs may behave differently. PrintNode also offers a private Standalone Server, which needs separate evaluation. Test the actual installation rather than inferring its failure behavior from an unrelated outage.
Can a LAN HTTP API and port 9100 coexist on the same printer?
Yes — that is how Proxy Nodes handles mixed environments. A node serves the HTTP/JSON API on port 80 and accepts raw ESC/POS on port 9100 with DLE EOT status replies at the same time, so a legacy POS keeps printing after an address change and nothing else, while new software uses JSON. Printing only: raw drawer-pulse bytes are dropped on port 9100, so a POS that kicks the till inside its print job needs POST /drawer/kick.
Why not just use the printer manufacturer's SDK?
Vendor SDKs are free and well documented, but they only drive that vendor's hardware, which costs $275 and up per printer versus $40 to 160 for generics. Building on one also means your software inherits the whitelist problem: every new hardware model is an engineering project.

Related reading