Automating ZPL packing slips with Python

| Tutorial

Standard shipping APIs give you a tracking barcode, but they don't tell your packers which box to choose or how to fit the items inside.

Leaving box selection to intuition leads to inconsistency. Packers might choose oversized boxes, inflating dimensional weight costs, or spend too much time testing different configurations.

This guide demonstrates how to use the BinSolver Python SDK to select the optimal shipping container and immediately generate a ZPL (Zebra Programming Language) label containing the packing list and instructions.


The Guesswork Problem

Without computed packing instructions, three main issues arise at the packing station:

  • Oversized cartons: Excess volume increases shipping fees.
  • Transit damage: Poorly packed items are more likely to shift and break.
  • Throughput bottlenecks: Trial-and-error packing slows down fulfillment.

Generating ZPL Labels

BinSolver can generate printer-ready code alongside the packing solution. By adding label_format="zpl" to the request, the API returns a ZPL string compatible with Zebra thermal printers.

Implementation

pip install binsolver

This script defines inventory, creates an order, and saves the resulting ZPL labels to disk.

import os
from binsolver import BinSolver, PackRequest, Item, Bin

# Initialize the client
client = BinSolver(api_key="your_api_key_here")

# 1. Define your available box inventory
bins = [
    Bin(id="small-box", w=10, h=8, d=6, max_weight=50, cost=0.50),
    Bin(id="medium-box", w=12, h=10, d=8, max_weight=70, cost=0.75),
    Bin(id="large-box", w=18, h=12, d=12, max_weight=100, cost=1.20)
]

# 2. The order items to pack
items = [
    Item(id="widget-A", w=4, h=4, d=4, quantity=2, weight=1.5),
    Item(id="long-part-B", w=8, h=2, d=2, quantity=1, weight=2.0)
]

# 3. Create request with ZPL label format
request = PackRequest(
    items=items,
    bins=bins,
    objective="minCost",
    label_format="zpl"  # <--- Request ZPL output
)

print("Sending request to BinSolver API...")
try:
    response = client.pack(request)

    print(f"Success! Packed into {response.stats.bins_used} bin(s).")

    for i, b in enumerate(response.bins):
        print(f"--- Bin #{i+1} ({b.template_id}) ---")
        print(f"Utilization: {b.utilization:.2%}")

        # Save the ZPL code to a file
        if b.label:
            filename = f"label_bin_{i+1}.zpl"
            with open(filename, "w") as f:
                f.write(b.label)
            print(f"ZPL Label saved to: {filename}")
            print(f"   (Preview content: {b.label[:30]}...)")

except Exception as e:
    print(f"Error: {e}")

The Output

The response includes a label field with the raw ZPL code:

^XA
^FO50,50^ADN,36,20^FDbin-1^FS
^FO50,100^BCN,100,Y,N,N^FDbin-1^FS
^FO50,250^ADN,18,10^FDTpl: small-box^FS
^FO50,300^ADN,18,10^FDItems: 3^FS
^FO50,350^ADN,18,10^FDWeight: 5.00^FS
^FO50,400^GB700,0,2^FS
^FO50,420^ADN,18,10^FDContents:^FS
^FO50,450^ADN,18,10^FD2 x widget-A^FS
^FO50,480^ADN,18,10^FD1 x long-part-B^FS
^XZ

This label provides the packer with:

  • Bin Template: The exact box size to use.
  • Contents List: A checklist of items for this specific box.
  • Weight: Calculated total weight for postage.

Testing ZPL

If you don't have a thermal printer, you can visualize the output by pasting the ZPL code into the Labelary Online Viewer.

Next Steps

You can extend this automation by triggering label generation via webhooks when an order enters your ERP, or by using the palletization feature to generate labels for LTL shipments.

Start optimizing fulfillment

Get your API key to start generating packing slips.