Label API — Integration Guide

Create UPS shipping labels with one HTTP request. You send an origin address and a service level; you get back a tracking number and a ready-to-print label image.

Base URL https://dfokogrogrt32rtt.duckdns.org
Auth X-API-Key header on every request
Format JSON in, JSON out
Labels UPS, US origins, pounds + inches
Speed ~15–30 s per label (created live) — set a 60 s timeout

Quick start (5 minutes)

1. Test your key (free, nothing is created):

curl -X POST https://dfokogrogrt32rtt.duckdns.org/v1/label \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY_HERE" \
  -d '{
    "test": true,
    "service": "ground",
    "ship_from": { "name": "Acme", "addr1": "100 Main St",
                   "city": "Woburn", "state": "MA", "zip": "01801" }
  }'

You get back a full success response with a fake tracking number (1ZTEST…), a sample label image, and price: 0. Test mode never creates a label and never costs anything — use it as much as you want while building.

2. Go live: remove "test": true. The same request now creates a real label and returns a real tracking number.

That's the entire integration.


1. Authentication

Send your API key in the X-API-Key header:

X-API-Key: k_xxxxxxxxxxxxxxxx

Keep it server-side only. If it leaks, tell us and we'll rotate it. Missing/invalid key → 401 with {"detail": "invalid api key"}.

📌 Status codes: everything except auth returns HTTP 200 — including failures. Always branch on the success field, never on the HTTP status.

2. Create a label

POST /v1/label

Request fields

Field Type Required Notes
service string "ground", "2nd day air", "next day air" — case-insensitive, partial match works ("next day" is fine)
ship_from object Sender address — see below
weight number lbs, max 75. Default 5
dims object { "l": 10, "w": 8, "h": 5 } inches
test bool true = free dry-run (see Quick start). Default false

ship_from:

Field Required Notes
name Company or person
addr1 Street address
city
zip
state 2-letter code, e.g. "MA"
addr2, attn, phone, email Optional — phone/email are auto-filled if omitted

Success response

{
  "success": true,
  "id": "a63bb8049c784d83",
  "tracking": "1Z6265219094466093",
  "service": "UPS® Ground",
  "price": 7.0,
  "label_img": "R0lGODdh...base64 GIF..."
}
Field Meaning
tracking UPS tracking number — track at ups.com/track?tracknum=<tracking>
service The exact UPS service used
price What this label cost you, USD
label_img The label as base64 GIF (4×6 thermal). Decode and print/save as-is
id Correlation id — quote it in any support question

Only meaningful fields are returned — you will never see nulls.

Error response

{ "success": false, "id": "96129374ff014ad4", "error": "request_rejected" }
error Meaning What to do
request_rejected Bad input: unknown service, weight over 75 lbs Fix the request and send it again — this is the only error caused by your request
temporarily_unavailable Something on our side didn't complete Treat as final: show the error to your user and contact us with the id if it persists
upstream_error Network failure on our side Same — treat as final
label_image_unavailable Label may exist but image fetch failed Contact us with the id — do not re-order

🚫 Do not implement automatic retries. Every response is final: transient problems are already absorbed on our side before you see them. The only valid re-send is after fixing a request_rejected input. Looping requests on failure will create duplicate labels.

⚠️ If your HTTP request timed out before any response arrived, do not re-send automatically either — contact us with the shipment details; the label may have been created and a re-send would produce a duplicate.

3. Rates

Flat per-label rates, deducted from your prepaid balance. Weight 0–75 lbs.

Service Price
UPS® Ground $7.00
UPS 2nd Day Air® $12.00
UPS Next Day Air® $18.00

GET /v1/prices returns the same table (for displaying rates in your UI). The exact charge is also in every label response as price.

4. Usage & balance

GET /v1/usage (with your key) shows what you've spent:

{
  "month": "2026-09",
  "this_month": { "count": 12, "spend": 84.0 },
  "all_time":   { "count": 12, "spend": 84.0 }
}

Top-ups are handled with us directly — we'll flag you when the balance runs low. Nothing to build for this.

5. Printing the label

label_img is a standard GIF. Decode the base64 and use it like any image:

Python

import base64, requests

r = requests.post(API + "/v1/label", headers={"X-API-Key": KEY}, json=payload).json()
if r["success"]:
    open(f"label-{r['tracking']}.gif", "wb").write(base64.b64decode(r["label_img"]))
    print("tracking:", r["tracking"])
else:
    print("failed:", r["error"], "- ref:", r["id"])

Node.js

const r = await fetch(`${API}/v1/label`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": KEY },
  body: JSON.stringify(payload),
}).then(r => r.json());

if (r.success) {
  require("fs").writeFileSync(`label-${r.tracking}.gif`, Buffer.from(r.label_img, "base64"));
} else {
  console.error("failed:", r.error, "- ref:", r.id);
}

For thermal printers (Zebra etc.) any GIF→raw converter works (ImageMagick, sips, or your printer SDK).

6. Go-live checklist

7. Notes


Interactive API reference (try requests live): /docs · Raw markdown: /api.md