---
title: Purchase a shipping label
description: >-
  Use the GraphQL Admin API to purchase a Shopify Shipping label for a
  fulfillment order.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps/purchase-shipping-labels
  md: >-
    https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps/purchase-shipping-labels.md
api_name: admin
---

# 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`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/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`](https://shopify.dev/docs/api/admin-graphql/latest/objects/ShippingLabelPurchaseResult) to track the purchase and retrieve the label.

***

## Requirements

* Your app uses Admin API version `2026-07` or higher.

* Your app can make [authenticated requests](https://shopify.dev/docs/api/admin-graphql#authentication) to the GraphQL Admin API.

* Your app has the `write_orders` [access scope](https://shopify.dev/docs/api/usage/access-scopes), along with a fulfillment order write scope that matches where the fulfillment order is assigned:

  * `write_merchant_managed_fulfillment_orders` for fulfillment orders assigned to merchant-managed locations.
  * `write_assigned_fulfillment_orders` for fulfillment orders assigned to a fulfillment service that your app manages.
  * `write_third_party_fulfillment_orders` for fulfillment orders assigned to another fulfillment service.

* If a user is authenticated with the request, then the user has the `buy_shipping_labels` permission (a Shopify admin staff permission, separate from the access scopes above).

* The store has accepted the [Shopify Shipping terms of service](https://help.shopify.com/manual/fulfillment/shopify-shipping).

* You've met Shopify's [protected customer data requirements](https://shopify.dev/docs/apps/launch/protected-customer-data).

**Note:**

The `shippingLabelPurchase` mutation doesn't support FedEx label purchases.

***

## Step 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`](https://shopify.dev/docs/api/admin-graphql/latest/objects/FulfillmentOrder) 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

```graphql
query {
  order(id: "gid://shopify/Order/1234") {
    fulfillmentOrders(first: 1) {
      nodes {
        id
        status
        requestStatus
        assignedLocation {
          name
        }
        destination {
          city
          countryCode
        }
      }
    }
  }
}
```

## JSON response

```json
{
  "data": {
    "order": {
      "fulfillmentOrders": {
        "nodes": [
          {
            "id": "gid://shopify/FulfillmentOrder/5678",
            "status": "OPEN",
            "requestStatus": "UNSUBMITTED",
            "assignedLocation": {
              "name": "Main warehouse"
            },
            "destination": {
              "city": "Ottawa",
              "countryCode": "CA"
            }
          }
        ]
      }
    }
  }
}
```

***

## Step 2: Purchase a shipping label

Pass the shipment details to the [`shippingLabelPurchase`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/shippingLabelPurchase) mutation in a [`ShippingLabelPurchaseInput`](https://shopify.dev/docs/api/admin-graphql/latest/input-objects/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 `userErrors` field. Some errors include a `code` that identifies the failure, such as `FULFILLMENT_ORDER_INVALID` or `RATES_NOT_FOUND`, but `code` can be `null`. Use the `message` and `field` to handle errors that don't return a `code`.
* **Asynchronous purchase-processing errors** appear in the [`ShippingLabelPurchaseResult`](https://shopify.dev/docs/api/admin-graphql/latest/objects/ShippingLabelPurchaseResult) `errors` field. You retrieve these when you [poll the purchase result](#step-3-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

```graphql
mutation {
  shippingLabelPurchase(shippingLabelPurchase: {
    fulfillmentOrderId: "gid://shopify/FulfillmentOrder/5678",
    shippingDatetime: "2026-09-20T09:00:00Z",
    totalWeight: {
      value: 2.5,
      unit: POUNDS
    },
    packageInfo: {
      customPackage: {
        weight: {
          value: 3.0,
          unit: OUNCES
        },
        dimensions: {
          length: 12.0,
          width: 9.0,
          height: 3.0,
          unit: INCHES
        },
        type: BOX
      }
    },
    notifyCustomer: true
  }) {
    shippingLabelPurchaseResult {
      id
      status
    }
    userErrors {
      field
      code
      message
    }
  }
}
```

## JSON response

```json
{
  "data": {
    "shippingLabelPurchase": {
      "shippingLabelPurchaseResult": {
        "id": "gid://shopify/ShippingLabelPurchaseResult/9012",
        "status": "PENDING_PURCHASE"
      },
      "userErrors": []
    }
  }
}
```

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.

**Note:**

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.

### Select 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

```graphql
mutation {
  shippingLabelPurchase(shippingLabelPurchase: {
    fulfillmentOrderId: "gid://shopify/FulfillmentOrder/5678",
    shippingDatetime: "2026-09-20T09:00:00Z",
    preferredRateSelection: {
      carrierCode: "ups_shipping",
      serviceCode: "03"
    }
  }) {
    shippingLabelPurchaseResult {
      id
      status
    }
    userErrors {
      field
      code
      message
    }
  }
}
```

## JSON response

```json
{
  "data": {
    "shippingLabelPurchase": {
      "shippingLabelPurchaseResult": {
        "id": "gid://shopify/ShippingLabelPurchaseResult/9012",
        "status": "PENDING_PURCHASE"
      },
      "userErrors": []
    }
  }
}
```

***

## Step 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](#step-2-purchase-a-shipping-label) to poll the result with the [`node`](https://shopify.dev/docs/api/admin-graphql/latest/queries/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

```graphql
query {
  node(id: "gid://shopify/ShippingLabelPurchaseResult/9012") {
    ... on ShippingLabelPurchaseResult {
      status
      errors {
        code
        message
      }
    }
  }
}
```

## JSON response

```json
{
  "data": {
    "node": {
      "status": "PURCHASE_FAILED",
      "errors": [
        {
          "code": "CARRIER_NOT_AVAILABLE",
          "message": "The carrier is not available for this label. Please try another carrier."
        }
      ]
    }
  }
}
```

***

## Step 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

```graphql
query {
  node(id: "gid://shopify/ShippingLabelPurchaseResult/9012") {
    ... on ShippingLabelPurchaseResult {
      status
      shippingLabels {
        id
        cancellable
        printed
        trackingInfo {
          number
          company
          url
        }
        shippingDocuments {
          documentType
          format
          url
        }
      }
    }
  }
}
```

## JSON response

```json
{
  "data": {
    "node": {
      "status": "PURCHASED",
      "shippingLabels": [
        {
          "id": "gid://shopify/ShippingLabel/3456",
          "cancellable": true,
          "printed": false,
          "trackingInfo": {
            "number": "1Z999AA10123456784",
            "company": "UPS",
            "url": "https://www.ups.com/track?tracknum=1Z999AA10123456784"
          },
          "shippingDocuments": [
            {
              "documentType": "LABEL",
              "format": "PDF",
              "url": "https://cdn.shopify.com/shipping-labels/3456.pdf"
            }
          ]
        }
      ]
    }
  }
}
```

Use the `url` on each `ShippingDocument` to print the label and any required customs forms.

***

## Next steps

* Learn how to [manage fulfillments as an order management app](https://shopify.dev/docs/apps/build/orders-fulfillment/order-management-apps/build-fulfillment-solutions).
* Consult the [`shippingLabelPurchase`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/shippingLabelPurchase) mutation and the [`ShippingLabelPurchaseResult`](https://shopify.dev/docs/api/admin-graphql/latest/objects/ShippingLabelPurchaseResult) object in the GraphQL Admin API reference.

***
