API Documentation
A REST API for creating shipping labels from your own software. Verify addresses, get live rates, and buy labels instantly — paid from a prepaid credit balance you fund with Bitcoin or Monero.
Sign in and create a key under Account → API keys. A verified email is required.
Fund your balance with BTC or XMR via the top-up endpoint. Credits are USD-denominated.
Purchases debit your balance and return the label in the same request — no per-order payment wait.
Authentication
Every request must include your API key as a Bearer token. Keys start with cp_live_ and are shown exactly once at creation. Keep them secret — anyone with your key can spend your credits. Revoke a compromised key immediately from your account page.
curl https://www.cryptopostage.xyz/api/v1/credits \
-H "Authorization: Bearer cp_live_YOUR_API_KEY"Request and response bodies are JSON. Errors use a consistent envelope with a stable machine-readable code and an HTTP status (400 validation, 401 auth, 402 insufficient credits, 404 not found, 422 unprocessable, 429 rate limited):
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Insufficient credits: this label costs $8.99 but your balance is $2.50. Top up via POST /api/v1/credits/topup."
}
}Idempotency
POST /api/v1/purchase and POST /api/v1/credits/topup accept an optional Idempotency-Key header (any unique string up to 200 characters, e.g. a UUID). If a request times out or your connection drops, retry with the same key and body: instead of creating and charging a second order, the API returns the original result with status 200 and an Idempotency-Replayed: true header. Reusing a key with a different body returns 409. Strongly recommended for all purchase requests.
curl -X POST https://www.cryptopostage.xyz/api/v1/purchase \
-H "Authorization: Bearer cp_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-8271-attempt" \
-d '{ ...purchase body... }'Endpoints
POST /api/v1/verify-address— validate and normalize an addressPOST /api/v1/rates— get available services and pricesPOST /api/v1/purchase— buy a label with creditsGET /api/v1/orders— list your ordersGET /api/v1/orders/{order_id}— retrieve one orderGET /api/v1/credits— credit balance and recent activityPOST /api/v1/credits/topup— fund credits with crypto
Verify Address
POSThttps://www.cryptopostage.xyz/api/v1/verify-address
Checks deliverability and returns the standardized form of an address. The body is a single address object.
curl -X POST https://www.cryptopostage.xyz/api/v1/verify-address \
-H "Authorization: Bearer cp_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe", "street1": "456 Oak Ave", "city": "Los Angeles",
"state": "CA", "zip": "90001", "country": "US"
}'Response: { valid, normalized, issues }.
Get Rates
POSThttps://www.cryptopostage.xyz/api/v1/rates
Returns available services with customer prices (postage + service fee) in USD cents. Optional carriers array (e.g. ["USPS"]) restricts results; customs is required for international shipments (see below). Weight is in ounces, dimensions in inches. For carrier flat-rate packaging, set parcel.predefinedPackage (e.g. FlatRateEnvelope) and omit dimensions — the same codes as the CSV guide.
curl -X POST https://www.cryptopostage.xyz/api/v1/rates \
-H "Authorization: Bearer cp_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fromAddress": {
"name": "John Smith", "street1": "123 Main St", "city": "New York",
"state": "NY", "zip": "10001", "country": "US"
},
"toAddress": {
"name": "Jane Doe", "street1": "456 Oak Ave", "city": "Los Angeles",
"state": "CA", "zip": "90001", "country": "US"
},
"parcel": { "weightOz": 16, "lengthIn": 10, "widthIn": 8, "heightIn": 4 }
}'{
"rates": [
{
"carrier": "USPS",
"service": "Priority",
"service_name": "USPS Priority Mail",
"amount_usd_cents": 899,
"estimated_delivery_days": 2,
"international": false
},
{
"carrier": "USPS",
"service": "GroundAdvantage",
"service_name": "USPS Ground Advantage",
"amount_usd_cents": 649,
"estimated_delivery_days": 3,
"international": false
}
]
}Note: UPS, FedEx, and DHL require a funded Adjustment Reserve on your account before purchase.
Create Purchase
POSThttps://www.cryptopostage.xyz/api/v1/purchase
Buys a label using your credit balance. Send the same shipment fields as /rates plus carrier and service (the service code from the rates response). The price is re-quoted server-side at purchase time; your balance is debited the exact live price. Returns 402 when the balance can't cover the label.
curl -X POST https://www.cryptopostage.xyz/api/v1/purchase \
-H "Authorization: Bearer cp_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fromAddress": { ...same as rates... },
"toAddress": { ...same as rates... },
"parcel": { "weightOz": 16, "lengthIn": 10, "widthIn": 8, "heightIn": 4 },
"carrier": "USPS",
"service": "Priority"
}'{
"order_id": "CP-A1B2C3D4E5",
"type": "SHIPPING",
"status": "COMPLETED",
"total_usd_cents": 899,
"credit_applied_cents": 899,
"created_at": "2026-09-02T18:00:00.000Z",
"shipments": [
{
"id": "...",
"status": "LABEL_READY",
"carrier": "USPS",
"service": "Priority",
"tracking_number": "9400100000000000000000",
"tracking_url": "https://tools.usps.com/...",
"label_url": "https://www.cryptopostage.xyz/api/labels/download?..."
}
],
"credit_balance_usd_cents": 4101
}For international shipments, include a customs declaration and phone numbers for both addresses:
"customs": {
"contentsType": "merchandise",
"signer": "John Smith",
"certify": true,
"customsItems": [
{
"description": "Cotton T-shirt",
"quantity": 2,
"valueUsdCents": 4000,
"weightOz": 12,
"hsTariffNumber": "6109.10",
"originCountry": "US"
}
]
}The label is normally generated in the same request (status: "COMPLETED", label_url set). If generation is still in progress or held for review, poll GET /api/v1/orders/{order_id} until the shipment reaches LABEL_READY. Label download links are signed and expire; fetch a fresh one from the order endpoint any time.
List Orders
GEThttps://www.cryptopostage.xyz/api/v1/orders
Lists your orders, newest first. Query params: limit (1–100, default 20) and cursor (from next_cursor in the previous page).
Retrieve Order
GEThttps://www.cryptopostage.xyz/api/v1/orders/{order_id}
Returns one order with shipments, tracking, and fresh signed label URLs. Accepts the public order id (CP-…). For unpaid top-up orders the response includes the active payment details.
Get Credits
GEThttps://www.cryptopostage.xyz/api/v1/credits
Returns balance_usd_cents and the last 20 credit events (top-ups, purchases, adjustments).
Top Up Credits
POSThttps://www.cryptopostage.xyz/api/v1/credits/topup
Creates a crypto payment for the requested USD amount ($5–$10,000). Send the exact amount_crypto to the returned address before it expires; the balance is credited automatically once the payment confirms on-chain.
curl -X POST https://www.cryptopostage.xyz/api/v1/credits/topup \
-H "Authorization: Bearer cp_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "amount_usd_cents": 5000, "coin": "BTC" }'{
"order_id": "CP-F6G7H8I9J0",
"status": "awaiting_payment",
"amount_usd_cents": 5000,
"coin": "BTC",
"payment": {
"address": "bc1q...",
"amount_crypto": "0.00043210",
"payment_uri": "bitcoin:bc1q...?amount=0.00043210",
"expires_at": "2026-09-02T18:20:00.000Z"
}
}Webhooks
Configure a webhook endpoint on your account page to receive events instead of polling. We POST JSON to your https URL for:
order.completed— every shipment on an order has its label (fires for held-for-review orders once approved)order.failed— a label purchase failed; thedata.errorfield explains whytopup.confirmed— a credit top-up payment confirmed and your balance was credited
{
"event": "order.completed",
"created_at": "2026-09-02T18:00:05.000Z",
"data": {
"order_id": "CP-A1B2C3D4E5",
"status": "COMPLETED",
"total_usd_cents": 899,
"shipments": [
{
"status": "LABEL_READY",
"carrier": "USPS",
"service": "Priority",
"tracking_number": "9400100000000000000000",
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=..."
}
]
}
}Every delivery is signed. Compute HMAC-SHA256 of <timestamp>.<body> with your signing secret (shown once when you save the URL) and compare it to the X-Webhook-Signature header. Reject deliveries whose timestamp is older than a few minutes to prevent replays.
// Node.js — verify a delivery
const crypto = require("crypto");
const signature = req.headers["x-webhook-signature"]; // "sha256=..."
const timestamp = req.headers["x-webhook-timestamp"];
const expected =
"sha256=" +
crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET) // whsec_...
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const valid = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));Respond with a 2xx within 10 seconds. Failed deliveries are retried with exponential backoff (roughly 1 minute to 1 hour between attempts) for up to 8 attempts. Deliveries are visible on your account page. Events may occasionally arrive out of order — use created_at and the order status as the source of truth.
Rate limits & fair use
Per key: 30 rate requests/min, 20 purchases/min, 60 reads/min, 5 top-ups/min. Responses include Retry-After when limited. Need more? Contact support.
