Add strategies to check orders before they are created: create_order_strategy of ClickAndDrop and MockClickAndDrop, and create_orders(orders, strategy=...).
NullStrategy uploads the orders as given. This is the default.
RejectDuplicateReferences does not upload orders whose reference already exists.
ReplaceDuplicateReferences deletes all existing orders with the same reference before the upload, including duplicates.
It does not delete manifested orders, nor orders with a printed label unless delete_printed=True.
Orders rejected by a strategy are in failed_orders of the result with a negative error code of StrategyErrorCode.
The other orders are uploaded.
Add replace_orders(): upload the orders in as few requests as possible
and delete older orders with the same references afterwards.
Add delete_orders_with_references() to delete all orders of references, including duplicates.
Add ClickAndDropError as the base class of InvalidWeight and InvalidDimensions.
Add lookup_references() to look up references without listing the orders.
It returns a ReferenceLookup with the sets absent, unique, ambiguous and the property existent.
Add find_orders_by_reference() to find the orders of references, including duplicates.
It searches the newest orders first and stops when two orders of each duplicated reference are found.
Add iter_orders() and iter_recent_orders() to iterate over all orders page by page, from the newest to the oldest.
get_order() and get_order_details() return None for a missing reference, as documented.
Before, Click & Drop's error was raised.
Add now() for the current time of the API.
MockClickAndDrop:
Behaves like Click & Drop, tested against the API:
get_orders raises BadRequestException with code "5" for references with several orders,
and with code "3" if no reference exists. Each error is followed by an error with code "1" for the reference in quotes.
Missing references next to existing ones are left out.
delete_orders with a reference that has several orders deletes nothing and raises BadRequestException
with code "22", and code "23" for each copy that could be deleted.
Orders are listed from the newest to the oldest. Times are cut off to whole seconds.
end must be after start.
Manifested orders cannot be deleted.
create_orders rejects orders with the same reference in one request (error 76)
and service codes the account cannot use (error 31).
get_label raises ForbiddenException if the account is not OBA.
New parameter channel_shipper: if false, get_orders_details raises ForbiddenException.
Missing orders: code "2" for identifiers, "3" for references, and one code "1" with all identifiers of the request.
delete_orders and get_orders answer with 400 if nothing is found or deleted.
delete_orders() returns the errors of orders that do not exist instead of raising,
so that the result does not depend on how the orders are split into requests.
lookup_references() finds duplicated and missing references next to existing ones:
Click & Drop leaves them out without an error.
Add integration tests in test/integration that run against the mock and,
with CI=true and API_KEY set, against the real API.
3.3.0
Update Click & Drop API v1. (2026-10-29)
ProductItem has non_standardised_manufacturer_id, standardised_manufacturer_id and pga_reference_number.
Royal Mail requires a manufacturer ID for EU shipments of goods (error 119).
ProductItem.customs_description allows up to 255 characters.
ShipmentPackageRequest has commercial_invoice, commercial_invoice_number and supporting_documents.
New ServicesApi and OlpServicesApi list the services available for a destination.
3.2.0
Update Click & Drop API v1. (2026-08-25)
3.1.1
Set default carrier_name to "Royal Mail OLP" or "Royal Mail OBA" in manifest_orders. This is the default or OBA and non-OBA accounts.
Check in the settings of your Royal Mail Account if this applies.
3.1.0
MockClickAndDrop.create_orders raises BadRequestException when more than one order in the batch requests a label (include_label_in_response=True), matching the real API behaviour.
v3.0.8
Fix PackageShippingOption.ships_to.
v3.0.7
OrderInfo.tracking_link returns the Royal Mail tracking URL for the order, or None.
v3.0.6
MockClickAndDrop.get_label returns label with CN if include_cn=True.
MockClickAndDrop.create_orders now includes a tracking code in case the service includes tracking.
v3.0.5
ClickAndDrop.get_order and ClickAndDrop.get_orders now return OrderInfo objects that have a status_history property
The mock API supports setting the status for orders correctly.
v3.0.4
Add manifest_orders(carrier_name=None) to AbstractClickAndDrop — manifests all eligible orders (status Label Generated or Despatched) and applies postage
Add ManifestedOrders type with a .pdf property that decodes the base64 manifest PDF to bytearray, returning None when no PDF is included
Add update_orders(order_identifiers, *, status, tracking_number, despatch_date, shipping_carrier, shipping_service) to update one or more order fields in one call
MockClickAndDrop accepts is_oba: bool = True constructor argument; is_oba() returns it directly without making API calls
MockClickAndDrop.get_label() now returns the bundled mock-label.pdf from click_and_drop_api.examples
MockClickAndDrop.manifest_orders() now returns the bundled mock-manifest.pdf as a base64-encoded response, with an incrementing manifest number
Add oba_full_workflow.py example — end-to-end OBA flow: check account type, pick cheapest domestic service, create order with label, save label, manifest, set despatched
Add ClickAndDrop.from_env as a shortcut to load the API from environment variables.
v3.0.3
Remove limit of 100 for ClickAndDrop.create_orders, ClickAndDrop.get_orders, ClickAndDrop.delete_orders
Add country_codes property to PackageShippingOption
v3.0.2
Better formatted error message for ClickAndDrop.test_shipping
Add any_or_none property to ShippingDB
v3.0.1
Add view_order example: print order details by order number or reference (python -m click_and_drop_api.examples.view_order <id>)
python -m click_and_drop_api.examples now lists all examples dynamically from the directory
v3.0.0
cache is_oba() by api key
Add PackageShippingOption.max_weight_kg property (converts max_weight_g to kg)
Breaking changes
remove Python 3.9 support
v2.0.0
Breaking changes
PackageSize and ShippingOption have been removed and merged into the single
class PackageShippingOption. The free-standing helper functions
(get_shipping_option, get_shipping_options_for, list_service_codes,
list_package_size_codes, choose_package_size_by_weight, choose_by_weight)
have also been removed. Use the db object (a ShippingDB instance) instead.
New features
PackageShippingOption — flat dataclass merging all package format fields
(package_size_code, package_name, package_max_weight_g, depth_mm,
width_mm, height_mm, max_sum_mm) with all service fields. The effective
max_weight_g is min(package_max_weight_g, service_max_weight_g).
Add ServiceConstraint dataclass and SERVICE_CONSTRAINTS dict to parcel module — per-service-code physical limits (weight + dimensions) for all OBA parcel service codes
Add SERVICE_MAX_WEIGHT_G to parcel module — per-service-code weight limits verified against the live API via scripts/test_parcel_orders.py
Add scripts/test_parcel_orders.py — creates test orders for each parcel type and reports 3 kg capability per service code
v1.2.1
fix: include the shipping option files.
v1.2.0 (yanked)
Remove add_shipping_option and shipping_options from the API
record international sending
parse shipping options from files with content copied from the website where you apply postage
Add ShippingOption.ships_to to check if the shipping option can ship to a country
v1.1.1
Document how to create an OBA
Add label generation example
Make order creation easier and add label request example
add ClickAndDrop.get_label()
v1.1.0
Add better typing for PackageSize.
Add methods to check for weight and size in PackageSize
Add conversion methods to make creating a API request easier.
Use dependency groups
Add more information to the example order creation