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 |
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.
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
successfield, never on the HTTP status.
POST /v1/label
| 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": 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.
{ "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_rejectedinput. 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.
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.
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.
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).
success: true and a 1ZTEST… trackingsuccess: false and log the id for support/docs on the same host lets you try requests
in the browser. Raw markdown of this guide: /api.md.Interactive API reference (try requests live): /docs · Raw markdown: /api.md