ShipYesterday
Sign in

API docs

JSON over HTTPS. Every amount is an integer number of US cents. Send your key as a bearer token; make one on the Developers page.

Base URL       https://shipyesterday-side-projects-7607c0e9.vercel.app/api/v1
Authorization  Bearer sy_test_...   (sandbox keys start sy_test_, live keys sy_live_)

This deployment runs in sandbox. Sandbox keys buy carrier test labels marked SAMPLE (or simulated ones in a bare preview) and spend sandbox credit. Live keys cannot be made until live labels are switched on.

Get rates

POST/api/v1/rates

Creates a shipment and returns one quote per available service, cheapest first. Each quote shows the postage, our fee and the total. Weights default to ounces and dimensions to inches. International shipments need customs and a recipient phone. Optional address_return sets the return address (see What prints on the label), and the first 30 characters of reference print on USPS labels. Optional options: signature ("STANDARD"), insurance_cents (up to 500000; the cost is included in the quoted price) and label_format ("PDF_4x6" or "ZPLII"). Only USPS rates are returned today. Quotes expire after 15 minutes.

curl https://shipyesterday-side-projects-7607c0e9.vercel.app/api/v1/rates \
  -H "Authorization: Bearer $SHIPYESTERDAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address_from": { "name": "Dock Four Goods", "street1": "2121 Alton Pkwy",
                      "city": "Irvine", "state": "CA", "zip": "92614", "country": "US" },
    "address_to":   { "name": "Maya Ortega", "street1": "48 Livingston St",
                      "city": "Brooklyn", "state": "NY", "zip": "11201", "country": "US" },
    "parcel": { "length": 12, "width": 9, "height": 4, "weight": 32, "mass_unit": "oz" },
    "address_return": { "name": "Returns Dept", "company": "Juniper Candle Co", "street1": "965 Mission St",
                        "city": "San Francisco", "state": "CA", "zip": "94103", "country": "US" },
    "reference": "order-4480",
    "options": { "signature": "STANDARD", "label_format": "PDF_4x6" }
  }'
201 Created
{
  "shipment_id": "2b0f...",
  "mode": "test",
  "simulated": false,
  "quotes": [
    { "id": "7c1e9d2a-...", "carrier": "USPS", "service": "Ground Advantage",
      "estimated_days": 4, "carrier_cost_cents": 942, "markup_cents": 82,
      "price_cents": 1024, "expires_at": "2026-11-05T18:15:00Z" }
  ],
  "messages": []
}

Buy a label

POST/api/v1/labels

Buys the quote and charges the quote's price_cents to your wallet. The Idempotency-Key header is required: retrying with the same key returns the first label (replayed: true) and never charges twice. Responses: 201 bought, 202 still waiting on the carrier (money held, check back with GET), 422 the carrier refused (money returned).

curl https://shipyesterday-side-projects-7607c0e9.vercel.app/api/v1/labels \
  -H "Authorization: Bearer $SHIPYESTERDAY_KEY" \
  -H "Idempotency-Key: order-4480" \
  -H "Content-Type: application/json" \
  -d '{ "quote_id": "7c1e9d2a-..." }'
201 Created
{
  "label": {
    "id": "f3a4...", "status": "purchased", "mode": "test",
    "carrier": "USPS", "service": "Ground Advantage",
    "tracking_number": "9400111899223456781234",
    "reference": "order-4480",
    "label_url": "https://...",
    "amount": { "postage_cents": 942, "fee_cents": 82, "total_cents": 1024, "currency": "USD" },
    "created_at": "2026-11-05T18:01:12Z"
  },
  "replayed": false
}

Get a label

GET/api/v1/labels/{id}

Returns one label. Use it to poll a label that answered 202.

200 OK
{ "label": { "id": "f3a4...", "status": "purchased", ... } }

Download the label file

GET/api/v1/labels/{id}/file

Streams the PDF or ZPL from our domain. This is the address in every label's label_url; send your API key with it.

200 OK
Content-Type: application/pdf
Content-Disposition: inline; filename="label-9400111899223456781234.pdf"

Track a label

GET/api/v1/labels/{id}/tracking

The carrier's current status and scan history, newest first. Test labels have no scans.

200 OK
{ "tracking": { "status": "TRANSIT", "eta": "2026-11-08T00:00:00Z", "tracking_number": "9400...",
  "events": [ { "status": "TRANSIT", "details": "Arrived at USPS facility", "at": "...", "location": "Jersey City, NJ" } ] } }

List labels

GET/api/v1/labels?limit=25&before=<created_at>

Newest first, up to 100 per page. Pass the last created_at as before for the next page.

200 OK
{ "data": [ { "id": "...", "status": "purchased", ... } ], "has_more": true }

Void a label

POST/api/v1/labels/{id}/refund

Asks the carrier to void an unused label. The wallet is credited when the carrier confirms: 200 if it already did, 202 while it is pending.

202 Accepted
{ "label": { "id": "f3a4...", "status": "refund_pending", ... } }

Wallet balance

GET/api/v1/wallet

Available balance and anything held for a withdrawal, in cents.

200 OK
{ "mode": "test", "currency": "USD", "available_cents": 48976, "pending_withdrawal_cents": 0 }

Wallet history

GET/api/v1/wallet/transactions?limit=25

Ledger lines for your wallet. Positive amounts add money, negative amounts spend it. Lines never change after they are written.

200 OK
{ "data": [ { "id": "...", "kind": "label_purchase", "amount_cents": -1024,
             "memo": "USPS Ground Advantage", "reference": { "type": "label", "id": "f3a4..." },
             "created_at": "..." } ] }

Check an address

POST/api/v1/addresses/validate

Checks whether an address is deliverable. Same address shape as in rates.

200 OK
{ "valid": true, "messages": [], "simulated": false }

What prints on the label

Checked on USPS Ground Advantage test labels (Nov 5, 2026). Only USPS labels can be bought today.

  • Without address_return, the sender block prints address_from: name, company, street, city line.
  • With address_return, the sender block prints the return address instead, and address_from does not appear on the label. address_from is still sent with the shipment data and the carrier can receive it.
  • The return address must be a real US address that can receive undeliverable parcels; USPS requires a legible domestic return address.
  • reference is sent as reference_1; its first 30 characters print bottom right.
  • Phone and email are not printed. The label carries the postage company's name in the postage mark and its logo in the footer. USPS allows this mark and it can't be removed.

Errors

Errors look like { "error": { "code": "...", "message": "..." } }.

HTTPCodeMeaning
401unauthorizedMissing, unknown or revoked key
402insufficient_fundsWallet balance is below the quote total
403mode_mismatchA test key against live, or the reverse
404quote_not_found / label_not_foundNot on this account
409idempotency_key_reusedSame key, different quote
409quote_already_purchasedQuote bought under another key
410quote_expiredQuotes last 15 minutes; ask for rates again
422invalid_requestValidation failed; see issues[]
422carrier_rejectedThe carrier refused; nothing was charged
503carrier_unavailableThe carrier network did not answer; safe to retry

Label statuses

purchased
Bought. Print it from label_url.
needs_review
We did not hear back from the carrier in time. Money is held, never doubled; it settles to purchased or failed.
failed
The carrier refused. The full total is back in your wallet.
refund_pending
Void requested; waiting for the carrier.
refunded
Voided. The full total is back in your wallet.