Purchase a shipping label
Apps can purchase Shopify Shipping labels for fulfillment orders on behalf of merchants. For example, an order management app might automate label purchases as soon as an order is ready to ship.
This guide shows how to use the GraphQL Admin API to purchase a shipping label for a fulfillment order, poll the asynchronous result, and retrieve the purchased label.
The shippingLabelPurchase mutation buys one shipping label for one fulfillment order. The mutation validates the fulfillment order, shipment, package, and available rates, and then starts an asynchronous purchase. You use the returned ShippingLabelPurchaseResult to track the purchase and retrieve the label.
Anchor to RequirementsRequirements
- Your app uses Admin API version
2026-07or higher. - Your app can make authenticated requests to the GraphQL Admin API.
- Your app has the
write_ordersaccess scope, along with a fulfillment order write scope that matches where the fulfillment order is assigned:write_merchant_managed_fulfillment_ordersfor fulfillment orders assigned to merchant-managed locations.write_assigned_fulfillment_ordersfor fulfillment orders assigned to a fulfillment service that your app manages.write_third_party_fulfillment_ordersfor fulfillment orders assigned to another fulfillment service.
- If a user is authenticated with the request, then the user has the
buy_shipping_labelspermission (a Shopify admin staff permission, separate from the access scopes above). - The store has accepted the Shopify Shipping terms of service.
- You've met Shopify's protected customer data requirements.
The shippingLabelPurchase mutation doesn't support FedEx label purchases.
The shippingLabelPurchase mutation doesn't support FedEx label purchases.
Anchor to Step 1: Retrieve an eligible fulfillment orderStep 1: Retrieve an eligible fulfillment order
You can purchase a shipping label only for a fulfillment order that's fulfillable, requires shipping, has a shipping destination, and has an assigned location.
Query the order's fulfillmentOrders to find an eligible fulfillment order and its ID. The following example requests the fulfillment order's status, assigned location, and destination.
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
GraphQL query
JSON response
Anchor to Step 2: Purchase a shipping labelStep 2: Purchase a shipping label
Pass the shipment details to the shippingLabelPurchase mutation in a ShippingLabelPurchaseInput. Only fulfillmentOrderId and shippingDatetime are required. You can optionally provide package details, total weight, and a preferred rate.
The mutation returns errors in two places:
- Synchronous validation errors appear in the mutation's
userErrorsfield. Some errors include acodethat identifies the failure, such asFULFILLMENT_ORDER_INVALIDorRATES_NOT_FOUND, butcodecan benull. Use themessageandfieldto handle errors that don't return acode. - Asynchronous purchase-processing errors appear in the
ShippingLabelPurchaseResulterrorsfield. You retrieve these when you poll the purchase result.
If userErrors is empty, then the purchase has started. The result begins with a status of PENDING_PURCHASE.
The following example purchases a label using a custom package. Because preferredRateSelection is omitted, the mutation uses Shopify Shipping's default rate selection.
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
GraphQL mutation
JSON response
If you provide packageInfo, then include exactly one of customPackage or carrierPackage. If you omit packageInfo, then Shopify uses the package assigned to the shipment. If you omit totalWeight, then Shopify calculates the total weight from the package and the fulfillable line items.
Set shippingDatetime to a date and time in the future. The mutation returns a SHIPPING_DATE_IN_THE_PAST error for a past value.
For international shipments, the order must already include the required customs information, such as an HS code and a country of origin for each item. The mutation uses the customs information from the order, so you don't provide it in the input.
For international shipments, the order must already include the required customs information, such as an HS code and a country of origin for each item. The mutation uses the customs information from the order, so you don't provide it in the input.
Anchor to Select a preferred rateSelect a preferred rate
To purchase a label with a specific carrier and service, include preferredRateSelection with a carrierCode and serviceCode. The serviceCode must exactly match a service code returned for the shipment's rates. If the selection doesn't match an available rate, then the mutation returns a RATES_NOT_FOUND error. The following values are examples.
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
GraphQL mutation
JSON response
Anchor to Step 3: Poll the purchase resultStep 3: Poll the purchase result
Shopify purchases the label asynchronously, so the result doesn't contain the label right away. Use the ShippingLabelPurchaseResult ID from Step 2 to poll the result with the node query until the status field is PURCHASED or PURCHASE_FAILED.
Most purchases finish within a few seconds. Carrier processing and retries can occasionally take longer, so set an overall timeout, such as 60 seconds, instead of polling indefinitely.
If the purchase fails, then the errors field describes what went wrong.
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
GraphQL query
JSON response
Anchor to Step 4: Retrieve the purchased labelStep 4: Retrieve the purchased label
When the status is PURCHASED, the shippingLabels field contains the purchased label. Query it to retrieve the label's tracking information and printable documents.
POST https://{shop}.myshopify.com/admin/api/{api_version}/graphql.json
GraphQL query
JSON response
Use the url on each ShippingDocument to print the label and any required customs forms.
Anchor to Next stepsNext steps
- Learn how to manage fulfillments as an order management app.
- Consult the
shippingLabelPurchasemutation and theShippingLabelPurchaseResultobject in the GraphQL Admin API reference.