REST API reference

Pirate Ship API

14 endpoints for Pirate Ship. Each one is a POST with a JSON body, called with your API key.

Base URL
https://cotlet.dev/api/v1/pirate-ship
Connects with
Your own Pirate Ship account, with a payment method or Account Balance for postage
Integration page
Pirate Ship
Pirate Ship Endpoints
Endpoint Access Description
get_rates read USPS and UPS services with price for one package, cheapest first.
get_batch_rates read Rates for every shipment in a batch, like Get Rates with all rows ticked.
list_import_orders read Unshipped orders from your connected store integrations, as in the Import grid.
list_shipments read Your label purchases from the Ship page grid: recipient, carrier, service, cost, tracking number and status.
get_shipment read One shipment with its service, cost, insurance, tracking number and status.
get_account_balance read Account Balance, Pending Refunds and Pending Returns from the Reports page.
list_saved_packages read Your Saved Packages from Settings: size, weight and the extra services stored with each.
export_shipments read Your shipments grid for the current filter as a spreadsheet download link.
create_batch write A batch from spreadsheet-style rows or import orders. Nothing is bought yet.
buy_label write Buy one label from a rate, paid from your Account Balance or payment method.
buy_batch write Buy labels for every shipment in a batch with one service, like Buy Labels.
refund_label write Request a refund for an unused label, like Refund Label.
create_scan_form write USPS End of Day SCAN Form for the labels of one Ship Date, as a PDF link.
schedule_pickup write Ask USPS or UPS to collect your packages, like Schedule a Pickup.

Get rates

read

POST https://cotlet.dev/api/v1/pirate-ship/get_rates

Returns each service Pirate Ship offers for this package with its price. Rates change with weight, size, ZIP and packaging, so call it again if any of them changes. Nothing is bought.

Parameters

Parameters: get_rates
NameTypeDescription
to_address required object name, street1, street2, city, state, zip, country.
from_address_id string Ship From Address ID. Default: your default Ship From Address.
package object weight_oz, and length, width, height in inches. Use this or saved_package_id.
saved_package_id string ID of one of your Saved Packages from list_saved_packages. Its size, weight and extra services such as insurance apply.
packaging string Type of Packaging as the app lists it, e.g. a box or a poly mailer. Priority Mail packaging works only with Priority Mail.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/get_rates \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to_address":"to address"}'
Python
result = client.call("pirate-ship", "get_rates", {"to_address":"to address"})
Node.js
const result = await cotlet.call('pirate-ship', 'get_rates', {"to_address":"to address"});
CLI
cotlet call pirate-ship get_rates --data '{"to_address":"to address"}'

Example response

{
  "rates": [
    {
      "rate_id": "r_01UG",
      "carrier": "USPS",
      "service": "Ground Advantage",
      "price_usd": 7.12
    },
    {
      "rate_id": "r_02PM",
      "carrier": "USPS",
      "service": "Priority Mail",
      "price_usd": 9.86
    },
    {
      "rate_id": "r_03UP",
      "carrier": "UPS",
      "service": "Ground",
      "price_usd": 11.4
    }
  ]
}

POST https://cotlet.dev/api/v1/pirate-ship/get_batch_rates

Returns each service with its total for the batch and the price per shipment, so the agent can pick one service for all labels. Shipments with an address problem are listed separately and are skipped until fixed. Nothing is bought.

Parameters

Parameters: get_batch_rates
NameTypeDescription
batch_id required string Batch ID from create_batch.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/get_batch_rates \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"batch_id":"batch_8c41d2"}'
Python
result = client.call("pirate-ship", "get_batch_rates", {"batch_id":"batch_8c41d2"})
Node.js
const result = await cotlet.call('pirate-ship', 'get_batch_rates', {"batch_id":"batch_8c41d2"});
CLI
cotlet call pirate-ship get_batch_rates --data '{"batch_id":"batch_8c41d2"}'

Example response

{
  "batch_id": "b_7Q4K2M",
  "shipments": 4,
  "services": [
    {
      "carrier": "USPS",
      "service": "USPS Ground Advantage",
      "total_usd": 32
    },
    {
      "carrier": "UPS",
      "service": "UPS Ground",
      "total_usd": 41.6
    }
  ],
  "address_problems": []
}

POST https://cotlet.dev/api/v1/pirate-ship/list_import_orders

Returns orders the Import grid shows, which by default are the unfulfilled or unshipped ones. Orders sync on their own; refresh asks for new ones right away. Each order has the ID that create_batch accepts.

Parameters

Parameters: list_import_orders
NameTypeDescription
source string Only orders from this connected store, like the Source filter. Default: all.
refresh boolean Press Refresh Data first. Default: false.
limit integer Maximum orders to return.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/list_import_orders \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("pirate-ship", "list_import_orders")
Node.js
const result = await cotlet.call('pirate-ship', 'list_import_orders');
CLI
cotlet call pirate-ship list_import_orders

Example response

{
  "orders": [
    {
      "order_id": "o_4412",
      "source": "Shopify",
      "order_number": "#1042",
      "ship_to": "J. Rivera, Austin, TX 78701"
    },
    {
      "order_id": "o_4413",
      "source": "Shopify",
      "order_number": "#1043",
      "ship_to": "M. Chen, Portland, OR 97205"
    },
    {
      "order_id": "o_4415",
      "source": "eBay",
      "order_number": "22-11044-31",
      "ship_to": "S. Okafor, Atlanta, GA 30303"
    }
  ]
}

POST https://cotlet.dev/api/v1/pirate-ship/list_shipments

Returns the grid rows that match, newest first. Batches appear as their labels. Use the shipment_id with get_shipment or refund_label.

Parameters

Parameters: list_shipments
NameTypeDescription
search string Recipient, Order ID or tracking number, as in the grid search.
status string Tracking status as the grid filter names it.
service string Shipping service as the grid filter names it.
since date Only labels with a Ship Date on or after this date.
cursor string Next page cursor from the previous call.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/list_shipments \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("pirate-ship", "list_shipments")
Node.js
const result = await cotlet.call('pirate-ship', 'list_shipments');
CLI
cotlet call pirate-ship list_shipments

Example response

{
  "shipments": [
    {
      "shipment_id": "s_83F2A1",
      "ship_date": "2026-10-08",
      "recipient": "J. Rivera",
      "carrier": "USPS",
      "service": "Ground Advantage",
      "cost_usd": 7.12,
      "tracking": "9400 1112 0000 0000 0001 23",
      "status": "In Transit"
    },
    {
      "shipment_id": "s_83F2A2",
      "ship_date": "2026-10-08",
      "recipient": "M. Chen",
      "carrier": "USPS",
      "service": "Ground Advantage",
      "cost_usd": 8.45,
      "tracking": "9400 1112 0000 0000 0001 30",
      "status": "Delivered"
    }
  ],
  "next_cursor": null
}

POST https://cotlet.dev/api/v1/pirate-ship/get_shipment

Returns what the label page shows, including the Email Recipients line that says whether a tracking email was sent. It sends nothing to the customer.

Parameters

Parameters: get_shipment
NameTypeDescription
shipment_id required string Shipment ID from list_shipments, buy_label or buy_batch.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/get_shipment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"shipment_id":"shipment_8c41d2"}'
Python
result = client.call("pirate-ship", "get_shipment", {"shipment_id":"shipment_8c41d2"})
Node.js
const result = await cotlet.call('pirate-ship', 'get_shipment', {"shipment_id":"shipment_8c41d2"});
CLI
cotlet call pirate-ship get_shipment --data '{"shipment_id":"shipment_8c41d2"}'

Example response

{
  "shipment_id": "s_83F2A1",
  "ship_date": "2026-10-08",
  "recipient": "J. Rivera",
  "carrier": "USPS",
  "service": "Ground Advantage",
  "cost_usd": 7.12,
  "insurance": "none",
  "tracking": "9400 1112 0000 0000 0001 23",
  "status": "In Transit",
  "tracking_email_sent": false
}

POST https://cotlet.dev/api/v1/pirate-ship/get_account_balance

Returns the funds available for labels and the refunds the carrier has not approved yet. Funds come from added credit, approved refunds and adjustments. Call it before buy_batch to see that the balance covers the batch.

Parameters

No parameters.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/get_account_balance \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("pirate-ship", "get_account_balance")
Node.js
const result = await cotlet.call('pirate-ship', 'get_account_balance');
CLI
cotlet call pirate-ship get_account_balance

Example response

{
  "account_balance_usd": 84.5,
  "pending_refunds_usd": 7.12,
  "pending_returns_usd": 0
}

POST https://cotlet.dev/api/v1/pirate-ship/list_saved_packages

Returns the presets you can pass to get_rates, create_batch and buy_label. A Saved Package holds size and weight, and can also hold extras such as Signature Confirmation or insurance.

Parameters

No parameters.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/list_saved_packages \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("pirate-ship", "list_saved_packages")
Node.js
const result = await cotlet.call('pirate-ship', 'list_saved_packages');
CLI
cotlet call pirate-ship list_saved_packages

Example response

{
  "saved_packages": [
    {
      "saved_package_id": "pkg_small_box",
      "name": "Small box",
      "weight_oz": 12,
      "length_in": 9,
      "width_in": 6,
      "height_in": 3,
      "extras": []
    },
    {
      "saved_package_id": "pkg_insured_mailer",
      "name": "Insured mailer",
      "weight_oz": 6,
      "length_in": 12,
      "width_in": 9,
      "height_in": 1,
      "extras": [
        "insurance"
      ]
    }
  ]
}

POST https://cotlet.dev/api/v1/pirate-ship/export_shipments

Does what the Export button does: all rows of the current filter or search, with the columns you set in the grid. A blank search exports every label purchase to date.

Parameters

Parameters: export_shipments
NameTypeDescription
search string Same search as list_shipments.
status string Tracking status as the grid filter names it.
service string Shipping service as the grid filter names it.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/export_shipments \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("pirate-ship", "export_shipments")
Node.js
const result = await cotlet.call('pirate-ship', 'export_shipments');
CLI
cotlet call pirate-ship export_shipments

Example response

{
  "rows": 2,
  "download_url": "https://cotlet.dev/api/v1/files/exports/pirate-ship-shipments-2026-10-11.xlsx"
}

Create batch

write

POST https://cotlet.dev/api/v1/pirate-ship/create_batch

Does the work of a spreadsheet upload or an import from a store. Rows without their own weight and size use the package or saved package you pass. Insurance and Extra Services come only from a saved package, and one batch holds one package type. The call reaches no one and spends no money, so it does not ask for your OK.

Parameters

Parameters: create_batch
NameTypeDescription
rows object[] Recipients: name, address, address2, city, state, zip, country, company, email, order_id, weight, length, width, height. Name, address, city, state and zip are required.
order_ids string[] Order IDs from list_import_orders, instead of rows.
package object weight_oz, and length, width, height in inches. Use this or saved_package_id.
saved_package_id string ID of one of your Saved Packages from list_saved_packages. Its size, weight and extra services such as insurance apply.
rubber_stamp string[] Up to 3 row fields to print in the corner of each label, e.g. order_id.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/create_batch \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"order_ids":["o_4412","o_4413","o_4415"],"saved_package_id":"pkg_small_box","rubber_stamp":["order_id"]}'
Python
result = client.call("pirate-ship", "create_batch", {"order_ids":["o_4412","o_4413","o_4415"],"saved_package_id":"pkg_small_box","rubber_stamp":["order_id"]})
Node.js
const result = await cotlet.call('pirate-ship', 'create_batch', {"order_ids":["o_4412","o_4413","o_4415"],"saved_package_id":"pkg_small_box","rubber_stamp":["order_id"]});
CLI
cotlet call pirate-ship create_batch --data '{"order_ids":["o_4412","o_4413","o_4415"],"saved_package_id":"pkg_small_box","rubber_stamp":["order_id"]}'

Example response

{
  "batch_id": "b_9X2LQ1",
  "shipments": 3,
  "rows_with_address_errors": 0,
  "bought": false
}

Buy label

write Confirm

POST https://cotlet.dev/api/v1/pirate-ship/buy_label

Spends money: the balance is used first and a payment method must be on file. Returns the shipment ID, the tracking number and the label to print. A bought label can be refunded with refund_label within 28 days. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: buy_label
NameTypeDescription
rate_id required string Rate ID from get_rates.
ship_date date Default: today.
extras string[] Extra Services such as Signature Confirmation, as the app names them.
insured_value_usd number Declared value for added insurance.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/buy_label \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rate_id":"rate_8c41d2"}'
Python
result = client.call("pirate-ship", "buy_label", {"rate_id":"rate_8c41d2"})
Node.js
const result = await cotlet.call('pirate-ship', 'buy_label', {"rate_id":"rate_8c41d2"});
CLI
cotlet call pirate-ship buy_label --data '{"rate_id":"rate_8c41d2"}'

Example response

{
  "shipment_id": "s_91AB02",
  "carrier": "USPS",
  "service": "Ground Advantage",
  "cost_usd": 7.12,
  "tracking": "9400 1112 0000 0000 0002 09",
  "labels_url": "https://cotlet.dev/api/v1/files/labels/s_91AB02.pdf"
}

Buy batch

write Confirm

POST https://cotlet.dev/api/v1/pirate-ship/buy_batch

Spends money for every label in the batch at once. Returns each shipment with its cost and tracking number, the total, and one link to print all labels. For store orders, Pirate Ship then sends the tracking numbers back to the store. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: buy_batch
NameTypeDescription
batch_id required string Batch ID from create_batch.
service required string Service from get_batch_rates, e.g. USPS Ground Advantage.
ship_date date Default: today.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/buy_batch \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"batch_id":"b_7Q4K2M","service":"USPS Ground Advantage","ship_date":"2026-10-12"}'
Python
result = client.call("pirate-ship", "buy_batch", {"batch_id":"b_7Q4K2M","service":"USPS Ground Advantage","ship_date":"2026-10-12"})
Node.js
const result = await cotlet.call('pirate-ship', 'buy_batch', {"batch_id":"b_7Q4K2M","service":"USPS Ground Advantage","ship_date":"2026-10-12"});
CLI
cotlet call pirate-ship buy_batch --data '{"batch_id":"b_7Q4K2M","service":"USPS Ground Advantage","ship_date":"2026-10-12"}'

Example response

{
  "batch_id": "b_7Q4K2M",
  "status": "purchased",
  "service": "USPS Ground Advantage",
  "ship_date": "2026-10-12",
  "labels": [
    {
      "shipment_id": "s_91B001",
      "order_id": "#1042",
      "cost_usd": 7.12,
      "tracking": "9400 1112 0000 0000 0003 11"
    },
    {
      "shipment_id": "s_91B002",
      "order_id": "#1043",
      "cost_usd": 7.12,
      "tracking": "9400 1112 0000 0000 0003 28"
    },
    {
      "shipment_id": "s_91B003",
      "order_id": "#1044",
      "cost_usd": 8.45,
      "tracking": "9400 1112 0000 0000 0003 35"
    },
    {
      "shipment_id": "s_91B004",
      "order_id": "#1045",
      "cost_usd": 9.31,
      "tracking": "9400 1112 0000 0000 0003 42"
    }
  ],
  "total_usd": 32,
  "labels_url": "https://cotlet.dev/api/v1/files/labels/b_7Q4K2M.pdf"
}

Refund label

write Confirm

POST https://cotlet.dev/api/v1/pirate-ship/refund_label

Works within 28 days of buying the label. USPS labels you have not printed refund instantly, printed ones wait for USPS to confirm they were never scanned, and UPS refunds are usually approved within about an hour. Approved money goes to your Account Balance. A refunded label can no longer be used. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: refund_label
NameTypeDescription
shipment_id required string Shipment ID from list_shipments, buy_label or buy_batch.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/refund_label \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"shipment_id":"s_84C001"}'
Python
result = client.call("pirate-ship", "refund_label", {"shipment_id":"s_84C001"})
Node.js
const result = await cotlet.call('pirate-ship', 'refund_label', {"shipment_id":"s_84C001"});
CLI
cotlet call pirate-ship refund_label --data '{"shipment_id":"s_84C001"}'

Example response

{
  "shipment_id": "s_83F2A1",
  "carrier": "USPS",
  "status": "refunded",
  "amount_usd": 7.12,
  "credited_to": "Account Balance"
}

POST https://cotlet.dev/api/v1/pirate-ship/create_scan_form

Covers USPS labels only. Labels bought after 8:00 PM local time belong to the next day. A created form cannot be edited, so a later label needs a second form. It reaches no one and costs nothing, so it does not ask for your OK.

Parameters

Parameters: create_scan_form
NameTypeDescription
ship_date date Ship Date group on the End of Day page. Default: today.
batch_id string Only the labels of this batch.
from_address_id string Ship From Address, when you ship from more than one.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/create_scan_form \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("pirate-ship", "create_scan_form")
Node.js
const result = await cotlet.call('pirate-ship', 'create_scan_form');
CLI
cotlet call pirate-ship create_scan_form

Example response

{
  "ship_date": "2026-10-12",
  "usps_labels": 24,
  "scan_form_url": "https://cotlet.dev/api/v1/files/scan-forms/2026-10-12.pdf"
}

Schedule pickup

write Confirm

POST https://cotlet.dev/api/v1/pirate-ship/schedule_pickup

USPS pickups are free and run every day except Sunday. UPS pickups are always requested by hand, and the help center lists a fee for them, so check get_account_balance first. The call books a driver visit at your address. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: schedule_pickup
NameTypeDescription
carrier required enum usps or ups.
date required date Pickup date.
package_count required integer Number of packages.
total_weight_oz number Total weight of the packages, required for USPS.
location string Where the carrier finds the packages, e.g. front door.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/pirate-ship/schedule_pickup \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"carrier":"usps","date":"2026-10-13","package_count":24,"total_weight_oz":496,"location":"front porch"}'
Python
result = client.call("pirate-ship", "schedule_pickup", {"carrier":"usps","date":"2026-10-13","package_count":24,"total_weight_oz":496,"location":"front porch"})
Node.js
const result = await cotlet.call('pirate-ship', 'schedule_pickup', {"carrier":"usps","date":"2026-10-13","package_count":24,"total_weight_oz":496,"location":"front porch"});
CLI
cotlet call pirate-ship schedule_pickup --data '{"carrier":"usps","date":"2026-10-13","package_count":24,"total_weight_oz":496,"location":"front porch"}'

Example response

{
  "carrier": "USPS",
  "date": "2026-10-13",
  "package_count": 12,
  "total_weight_oz": 264,
  "fee_usd": 0,
  "status": "scheduled"
}