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
| 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
readPOST 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
| Name | Type | Description |
|---|---|---|
| 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 -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"}' result = client.call("pirate-ship", "get_rates", {"to_address":"to address"}) const result = await cotlet.call('pirate-ship', 'get_rates', {"to_address":"to address"}); 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
}
]
} Get batch rates
readPOST 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
| Name | Type | Description |
|---|---|---|
| batch_id required | string | Batch ID from create_batch. |
Request
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"}' result = client.call("pirate-ship", "get_batch_rates", {"batch_id":"batch_8c41d2"}) const result = await cotlet.call('pirate-ship', 'get_batch_rates', {"batch_id":"batch_8c41d2"}); 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": []
} List import orders
readPOST 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
| Name | Type | Description |
|---|---|---|
| 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 -X POST https://cotlet.dev/api/v1/pirate-ship/list_import_orders \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("pirate-ship", "list_import_orders") const result = await cotlet.call('pirate-ship', 'list_import_orders'); 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"
}
]
} List shipments
readPOST 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
| Name | Type | Description |
|---|---|---|
| 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 -X POST https://cotlet.dev/api/v1/pirate-ship/list_shipments \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("pirate-ship", "list_shipments") const result = await cotlet.call('pirate-ship', 'list_shipments'); 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
} Get shipment
readPOST 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
| Name | Type | Description |
|---|---|---|
| shipment_id required | string | Shipment ID from list_shipments, buy_label or buy_batch. |
Request
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"}' result = client.call("pirate-ship", "get_shipment", {"shipment_id":"shipment_8c41d2"}) const result = await cotlet.call('pirate-ship', 'get_shipment', {"shipment_id":"shipment_8c41d2"}); 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
} Get account balance
readPOST 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 -X POST https://cotlet.dev/api/v1/pirate-ship/get_account_balance \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("pirate-ship", "get_account_balance") const result = await cotlet.call('pirate-ship', 'get_account_balance'); cotlet call pirate-ship get_account_balance Example response
{
"account_balance_usd": 84.5,
"pending_refunds_usd": 7.12,
"pending_returns_usd": 0
} List saved packages
readPOST 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 -X POST https://cotlet.dev/api/v1/pirate-ship/list_saved_packages \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("pirate-ship", "list_saved_packages") const result = await cotlet.call('pirate-ship', 'list_saved_packages'); 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"
]
}
]
} Export shipments
readPOST 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
| Name | Type | Description |
|---|---|---|
| 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 -X POST https://cotlet.dev/api/v1/pirate-ship/export_shipments \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("pirate-ship", "export_shipments") const result = await cotlet.call('pirate-ship', 'export_shipments'); 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
writePOST 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
| Name | Type | Description |
|---|---|---|
| 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 -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"]}' 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"]}) 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"]}); 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
} 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
| Name | Type | Description |
|---|---|---|
| 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 -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"}' result = client.call("pirate-ship", "buy_label", {"rate_id":"rate_8c41d2"}) const result = await cotlet.call('pirate-ship', 'buy_label', {"rate_id":"rate_8c41d2"}); 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"
} 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
| Name | Type | Description |
|---|---|---|
| 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 -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"}' result = client.call("pirate-ship", "buy_batch", {"batch_id":"b_7Q4K2M","service":"USPS Ground Advantage","ship_date":"2026-10-12"}) const result = await cotlet.call('pirate-ship', 'buy_batch', {"batch_id":"b_7Q4K2M","service":"USPS Ground Advantage","ship_date":"2026-10-12"}); 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"
} 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
| Name | Type | Description |
|---|---|---|
| shipment_id required | string | Shipment ID from list_shipments, buy_label or buy_batch. |
Request
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"}' result = client.call("pirate-ship", "refund_label", {"shipment_id":"s_84C001"}) const result = await cotlet.call('pirate-ship', 'refund_label', {"shipment_id":"s_84C001"}); 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"
} Create SCAN Form
writePOST 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
| Name | Type | Description |
|---|---|---|
| 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 -X POST https://cotlet.dev/api/v1/pirate-ship/create_scan_form \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("pirate-ship", "create_scan_form") const result = await cotlet.call('pirate-ship', 'create_scan_form'); 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"
} 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
| Name | Type | Description |
|---|---|---|
| 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 -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"}' result = client.call("pirate-ship", "schedule_pickup", {"carrier":"usps","date":"2026-10-13","package_count":24,"total_weight_oz":496,"location":"front porch"}) 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"}); 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"
}