Check labels from your own code

One POST, every finding, with the exact command and offset and a concrete fix. The same engine the web tool runs — there is only one.

Authentication

Send X-API-Key. Without a key you get the free tier: the same 43 rules, lower rate limits, no monthly render quota to exhaust. Keys are issued by us and shown once.

Check a label

POST /api/lint

curl -X POST https://labelproof.dev/api/lint \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: lp_...' \
  -d '{
    "zpl": "^XA^FO50,50^BY2^BCN,100,Y,N,N^FD>;12345^FS^XZ",
    "dpi": 203,
    "profile": "gs1_logistic",
    "render": true
  }'

dpi and profile are required and have no defaults. There is no universal minimum barcode size and every dot-to-millimetre conversion depends on the printhead, so guessing either one produces confident, wrong answers. The API refuses rather than assumes. profile is gs1_logistic, gs1_other or non_gs1.

render defaults to true. Send false and you get 39 of the 43 rules for no render quota at all — the four that need pixels are the geometry rules (quiet zones, overlap).

{
  "dpi": 203,
  "rulesChecked": 43,
  "renderUsed": true,
  "tier": "developer",
  "findings": [
    {
      "ruleId": "FLD-C128C-ODD-001",
      "severity": "error",
      "message": "Subset C encodes digits in pairs. This payload has an odd number of digits, so the last one is dropped.",
      "fix": "Pad to an even digit count, or switch to Subset B (>:).",
      "commandText": "^BC",
      "offset": 23
    }
  ]
}

severity is error (will fail), warning (will look wrong) or info (works, non-standard). An empty findings array means nothing was found — check rulesChecked to see what that silence covers.

The catalogue

These need no key. The rule pages are generated from the same data.

Regression suites Team

Pin labels and get told when a check changes — not because your template changed, but because ours did.

A new finding or a changed rendered image is a regression. A resolved finding is deliberately not — we ship catalogue changes constantly, and alerting on those would page you on every release. See the catalogue and the alerting rules in the repository’s SUITES.md.

Print delivery

Send a label to a printer on your own network. The agent polls outbound, so nothing needs a firewall port opened.

Always send an Idempotency-Key header. A retried request without one queues a second physical label, and clients retry on timeouts and deploys.

There is no status called “printed”. A printer on raw TCP 9100 accepts bytes and says nothing back, so sent is the honest ceiling: the printer took the data. Whether a label came out — ribbon, media, head latch — is not something that connection can tell you, and an API that claimed otherwise would be reporting a guess as a fact.

Rate limits and errors

Limits are per minute and per tier; see pricing. Over the limit returns 429 with a Retry-After header. A 402 or 403 names exactly what was exceeded and what it costs.

CLI and CI

The labelproof CLI wraps this API for shells and CI, and a GitHub Action wraps the CLI. Nothing to install:

npx labelproof labels/ --dpi 203 --profile gs1_logistic

On npm, MIT, no dependencies. Exit code 2 means the check could not be completed — never treat it as a pass.