Batch Guide

This guide will teach you how to use Batches to create and purchase shipping labels. Batches make it simpler to create and purchase many shipping labels in just a few API calls. Even better, we group all the labels in a batch into a single file that you can download.

In this example, we will be shipping four T-Shirts to four very lucky EasyPost fans. Batches can support up to 10,000 shipments per batch.

Before You Start

  1. Sign up for an EasyPost account or log in to an existing account
  2. Set up your webhook URLs for Test Mode and Production Mode.
  3. Grab one of our official client libraries.

If you haven't run through our Getting Started Guide, definitely do that before moving on to this one.

Step 1: Creating a Batch of Shipments

A Batch is a collection of Shipments that you purchase and generate labels for together. When creating a Batch, you can either:

  • Create and purchase the Shipment objects ahead of time and pass Shipment IDs.
  • Pass us the information needed to create Address, Parcel, and Shipment objects as well as tell us which Carrier and Service you want to use for that Shipment.

When you create a Batch, the Shipments within the Batch are created asynchronously. When you first POST to create the Batch, we will send a webhook event to your app that the Batch object is being created. ("status":"creating"). Due to the webhook update being asynchronous, you should carefully consider the implications of using Batches instead of multiple Shipment requests.

Once all shipments are created, we will send another webhook to your application telling you it is complete ("status":"created"). If there are any errors for the Batch object, we will tell you about them in the webhook ("state":"creation_failed"). Step 2 of this tutorial will show you how to fix any errors that arise. There is more information on webhooks later in the tutorial.

Creating a Batch
const EasyPostClient = require('@easypost/api');

const client = new EasyPostClient('EASYPOST_API_KEY');

(async () => {
  const batch = await client.Batch.create({
    shipments: [{ id: 'shp_...' }, { id: 'shp_...' }],
  });

  console.log(batch);
})();
Batch JSON Response
{
  "id": "batch_94b0f73e5d3344158b588f1cac2d083c",
  "object": "Batch",
  "mode": "test",
  "state": "creating",
  "num_shipments": 1,
  "reference": null,
  "created_at": "2024-01-24T00:03:49Z",
  "updated_at": "2024-01-24T00:03:49Z",
  "scan_form": null,
  "shipments": [],
  "status": {
    "created": 0,
    "queued_for_purchase": 0,
    "creation_failed": 0,
    "postage_purchased": 0,
    "postage_purchase_failed": 0
  },
  "pickup": null,
  "label_url": null
}

Step 2: Adding and Removing Shipments from a Batch

After you create a Batch, you can still add Shipments to it. To add Shipments to a Batch, you need to create and purchase the Shipment object ahead of time and then add it to your already existing Batch.

Here is an example:

Adding Shipments
const EasyPostClient = require('@easypost/api');

const client = new EasyPostClient('EASYPOST_API_KEY');

(async () => {
  const batch = await client.Batch.retrieve('batch_...');

  const batchWithShipments = await client.Batch.addShipments(batch.id, ['shp_...', 'shp_...']);

  console.log(batchWithShipments);
})();
Parcel Response
{
  "id": "batch_848345b6ef6c4297a489fd34af7e1514",
  "object": "Batch",
  "mode": "test",
  "state": "created",
  "num_shipments": 1,
  "reference": null,
  "created_at": "2024-01-24T00:04:53Z",
  "updated_at": "2024-01-24T00:04:53Z",
  "scan_form": null,
  "shipments": [
    {
      "batch_status": "postage_purchased",
      "batch_message": null,
      "reference": null,
      "tracking_code": "9405500207552011812467",
      "id": "shp_406a7ca4c34c47739093acf06ffbc4b6"
    }
  ],
  "status": {
    "created": 0,
    "queued_for_purchase": 0,
    "creation_failed": 0,
    "postage_purchased": 1,
    "postage_purchase_failed": 0
  },
  "pickup": null,
  "label_url": null
}

There may be times when you need to remove a Shipment from a Batch. You can do that too. For example, a particular Shipment may have an invalid address but you may still want to continue on with the rest of the Shipments.

You can easily remove a Shipment from a Batch with the following code:

Removing Shipments
const EasyPostClient = require('@easypost/api');

const client = new EasyPostClient('EASYPOST_API_KEY');

(async () => {
  const batch = await client.Batch.retrieve('batch_...');

  const batchWithoutShipments = await client.Batch.removeShipments(batch.id, ['shp_...']);

  console.log(batchWithoutShipments);
})();
Removing Shipment JSON Response
{
  "id": "batch_f25fcb6de1254de1b3e0a3f770333292",
  "object": "Batch",
  "mode": "test",
  "state": "purchased",
  "num_shipments": 0,
  "reference": null,
  "created_at": "2024-01-24T00:04:55Z",
  "updated_at": "2024-01-24T00:04:55Z",
  "scan_form": null,
  "shipments": [],
  "status": {
    "created": 0,
    "queued_for_purchase": 0,
    "creation_failed": 0,
    "postage_purchased": 0,
    "postage_purchase_failed": 0
  },
  "pickup": null,
  "label_url": null
}

Step 3: Creating and Purchasing Shipping labels for a Batch

The next step is to purchase and create all labels for the shipments in your batch. All you need to do is issue a buy on the particular Batch object you're ready to buy. When you buy the batch, we kick off an asynchronous process to create all the labels you need.

The initial response from buying a Batch will not have the URL of a label. Because we support up to 10,000 shipments in a Batch, it takes time to create all the labels.

Once we've purchased and created all the labels for a batch, we'll send a webhook to your application letting you know that it has completed. When completed, the "state" of the Batch object will be "purchased". If there are any errors, the state of the Batch object will be "purchase_failed". You will need to fix or remove any of the shipments that failed before proceeding to the next step of creating the Batch Label.

Here's a code example of buying a Batch:

Buying a Batch
const EasyPostClient = require('@easypost/api');

const client = new EasyPostClient('EASYPOST_API_KEY');

(async () => {
  const batch = await client.Batch.retrieve('batch_...');

  const boughtBatch = await client.Batch.buy(batch.id);

  console.log(boughtBatch);
})();
Printing Results
{
  "id": "batch_26bd321f6ba947be94a9928f7167a968",
  "object": "Batch",
  "mode": "test",
  "state": "created",
  "num_shipments": 1,
  "reference": null,
  "created_at": "2024-01-24T00:03:50Z",
  "updated_at": "2024-01-24T00:03:50Z",
  "scan_form": null,
  "shipments": [
    {
      "batch_status": "queued_for_purchase",
      "batch_message": null,
      "reference": null,
      "tracking_code": null,
      "id": "shp_52b29348cf76420c8b353ab3a022a4ba"
    }
  ],
  "status": {
    "created": 1,
    "queued_for_purchase": 0,
    "creation_failed": 0,
    "postage_purchased": 0,
    "postage_purchase_failed": 0
  },
  "pickup": null,
  "label_url": null
}

Step 4: Creating a Batch Label

Once you have received the webhook that your Batch has been purchased, the final step is to create and retrieve all the shipping labels in a single Batch Label. All the labels for the Batch will be in a single file that you download. A Batch Label can be retrieved in either PDF, EPL2, or ZPL format.

View an example of retrieving your labels in a single PDF file.

Creating Batch Label
const EasyPostClient = require('@easypost/api');

const client = new EasyPostClient('EASYPOST_API_KEY');

(async () => {
  const batch = await client.Batch.retrieve('batch_...');

  const batchWithLabel = await client.Batch.generateLabel(batch.id, 'PDF');

  console.log(batchWithLabel);
})();
Creating Batch Label Response
{
  "id": "batch_5e5323be0f8444e4b9e100a76afbc921",
  "object": "Batch",
  "mode": "test",
  "state": "label_generating",
  "num_shipments": 1,
  "reference": null,
  "created_at": "2024-01-24T00:04:56Z",
  "updated_at": "2024-01-24T00:05:36Z",
  "scan_form": null,
  "shipments": [
    {
      "batch_status": "postage_purchased",
      "batch_message": null,
      "reference": null,
      "tracking_code": "9405500207552011812511",
      "id": "shp_b9419ff95a034151826c7cad52556628"
    }
  ],
  "status": {
    "created": 0,
    "queued_for_purchase": 0,
    "creation_failed": 0,
    "postage_purchased": 1,
    "postage_purchase_failed": 0
  },
  "pickup": null,
  "label_url": null
}

You can only get shipping labels for a Batch if all Shipments in the 'postage_purchased' status. If any of your purchases are failed, you should just remove that Shipment from the Batch during the previous step.

If the Batch Label fails to create, the Batch object will return back to "purchased" state.

Step 5: Using Webhooks for a Batch

When using Batches, webhooks are useful at three points:

  • Creating a Batch (Step 1 in the tutorial)
  • Purchasing and creating labels for a Batch (Step 3 in the tutorial)
  • Creating and retrieving a Batch label (Step 4 in the tutorial)

Webhooks are required because these processes are completed asynchronously. You will receive a webhook both on the initial POST to EasyPost and once the given action has reached a final state. If there are any errors, we will pass them back in the associated webhook.

When evaluating Events that hit your webhook URL, you'll know it is a Batch event because the object "type" is "Event" and the "description" is "batch.created" or "batch.updated". As part of this Event, we will also pass you back the Batch object. The value of the Event's "result" attribute will contain the Batch object. Check our Webhooks Guide for additional information on using webhooks.

We recommend you evaluate the "state" of the Batch object to check if your Batch was successfully processed. If there is an error (eg "creation failed" or "purchase_failed"), you can then inspect the individual Shipment objects we return to see which one caused the issue. Each Shipment will have a "batch_status" that will let you know which Shipment needs to be fixed. We also provide a summary of successes and failure so you know how many Shipments need attention.

Here's an example of webhook for a Batch:

{
  "completed_urls": [],
  "created_at": "2014-07-22T07:46:44Z",
  "description": "batch.updated",
  "id": "evt_...",
  "mode": "test",
  "object": "Event",
  "pending_urls": ["https://webhooks.example.com"],
  "previous_attributes": { "state": "label_generating" },
  "result": {
    "created_at": "2014-07-22T07:46:44Z",
    "id": "batch_...",
    "label_url": "https://amazonaws.com/.../a1b2c3.pdf",
    "mode": "test",
    "num_shipments": 1,
    "object": "Batch",
    "reference": null,
    "scan_form": null,
    "shipments": [
      { "batch_message": null, "batch_status": "created", "id": "shp_..." },
      { "batch_message": null, "batch_status": "created", "id": "shp_..." },
      { "batch_message": null, "batch_status": "created", "id": "shp_..." },
      { "batch_message": null, "batch_status": "created", "id": "shp_..." }
    ],
    "state": "label_generated",
    "status": {
      "created": 0,
      "creation_failed": 0,
      "postage_purchase_failed": 0,
      "postage_purchased": 1,
      "queued_for_purchase": 0
    },
    "updated_at": "2014-08-04T22:37:52Z"
  },
  "updated_at": "2014-08-04T22:37:51Z"
}

Congratulations! You've just created and purchased your first batch with EasyPost! Check out our Full Reference API Documentation.