Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ Before you begin to integrate:

### API and SDK Version

- SDK Version: 9.0.0
- API Version: 2026-01
- SDK Version: 10.0.0
- API Version: 2026-07

## Quick Start

Expand Down Expand Up @@ -98,6 +98,27 @@ except exceptions.RateLimitExceedError:

See the [Rate Limit](https://www.aftership.com/docs/tracking/quickstart/rate-limit) to understand the AfterShip rate limit policy.

The API returns its current rate limit status in the headers of every response, and the SDK exposes these headers on both successful responses and rate-limited errors, so you can monitor your consumption proactively instead of waiting for `429` errors.

| Header | Description |
| ----------------------- | ---------------------------------------------------------- |
| `X-RateLimit-Limit` | The rate limit ceiling for the current endpoint per second |
| `X-RateLimit-Remaining` | The number of requests left for the 1-second window |
| `X-RateLimit-Reset` | The Unix timestamp when the rate limit will be reset |

Every successful response exposes a `response_header` dict alongside `data` (header names are lower-cased, each value is a list of strings). Taking the Quick Start example above:

```python
remaining = int(result.response_header["x-ratelimit-remaining"][0])
reset_at = int(result.response_header["x-ratelimit-reset"][0])

if remaining <= 1:
# Throttle or defer lower-priority requests until reset_at
pass
```

When the rate limit is exceeded, the request fails with a `429` error that carries the same headers — see [Error Handling](#error-handling).

## Error Handling

The SDK will return an error object when there is any error during the request, with the following specification:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "aftership-tracking-sdk"
version = "9.0.0"
version = "10.0.0"
description = "The official AfterShip Tracking Python API library"
authors = ["AfterShip <support@aftership.com>"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion tracking/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
from . import exceptions
from .models import *

__version__ = "9.0.0"
__version__ = "10.0.0"
4 changes: 2 additions & 2 deletions tracking/api/courier.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def get_couriers(self, **kwargs) -> GetCouriersResponse:
**active**: bool. get user activated couriers
**slug**: str. Unique courier code Use comma for multiple values. (Example: dhl,ups,usps)
"""
url = "/tracking/2026-01/couriers"
url = "/tracking/2026-07/couriers"

params_keys = {
"active",
Expand All @@ -59,7 +59,7 @@ def detect_courier(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = "/tracking/2026-01/couriers/detect"
url = "/tracking/2026-07/couriers/detect"

body = detect_courier_request
if not isinstance(body, dict):
Expand Down
10 changes: 5 additions & 5 deletions tracking/api/courier_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def get_courier_connections(self, **kwargs) -> GetCourierConnectionsResponse:
**cursor**: str. A string representing the cursor value for the current page of results.
**limit**: str. Number of courier connections each page contain. (Default: 100, Max: 200)
"""
url = "/tracking/2026-01/courier-connections"
url = "/tracking/2026-07/courier-connections"

params_keys = {
"courier_slug",
Expand All @@ -66,7 +66,7 @@ def post_courier_connections(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = "/tracking/2026-01/courier-connections"
url = "/tracking/2026-07/courier-connections"

body = post_courier_connections_request
if not isinstance(body, dict):
Expand All @@ -91,7 +91,7 @@ def get_courier_connections_by_id(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2026-01/courier-connections/{id}"
url = f"/tracking/2026-07/courier-connections/{id}"

result = self._request("GET", url=url, **kwargs)
return GetCourierConnectionsByIdResponse.model_validate(result)
Expand All @@ -115,7 +115,7 @@ def put_courier_connections_by_id(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2026-01/courier-connections/{id}"
url = f"/tracking/2026-07/courier-connections/{id}"

body = put_courier_connections_by_id_request
if not isinstance(body, dict):
Expand All @@ -140,7 +140,7 @@ def delete_courier_connections_by_id(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2026-01/courier-connections/{id}"
url = f"/tracking/2026-07/courier-connections/{id}"

result = self._request("DELETE", url=url, **kwargs)
return DeleteCourierConnectionsByIdResponse.model_validate(result)
4 changes: 2 additions & 2 deletions tracking/api/estimated_delivery_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def predict(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = "/tracking/2026-01/estimated-delivery-date/predict"
url = "/tracking/2026-07/estimated-delivery-date/predict"

body = predict_request
if not isinstance(body, dict):
Expand All @@ -59,7 +59,7 @@ def predict_batch(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = "/tracking/2026-01/estimated-delivery-date/predict-batch"
url = "/tracking/2026-07/estimated-delivery-date/predict-batch"

body = predict_batch_request
if not isinstance(body, dict):
Expand Down
14 changes: 7 additions & 7 deletions tracking/api/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def get_trackings(self, **kwargs) -> GetTrackingsResponse:
**shipment_tags**: str. Tags you added to your shipments to help categorize and filter them easily. Use a comma to separate multiple values (Example: a,b)
**order_id**: str. A globally-unique identifier for the order. Use comma for multiple values.(Example: 6845a095a27a4caeb27487806f058add,4845a095a27a4caeb27487806f058abc)
"""
url = "/tracking/2026-01/trackings"
url = "/tracking/2026-07/trackings"

params_keys = {
"cursor",
Expand Down Expand Up @@ -99,7 +99,7 @@ def create_tracking(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = "/tracking/2026-01/trackings"
url = "/tracking/2026-07/trackings"

body = create_tracking_request
if not isinstance(body, dict):
Expand Down Expand Up @@ -127,7 +127,7 @@ def get_tracking_by_id(
**fields**: str. List of fields to include in the response. Use comma for multiple values. Fields to include: `destination_postal_code`, `tracking_ship_date`, `tracking_account_number`, `tracking_key`, `origin_country_region`, `destination_country_region`, `destination_state`, `title`, `order_id`, `tag`, `checkpoints`
**lang**: str. Translate checkpoint messages from the carrier’s provided language to the target language. Supported target languages include:&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- English (en)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- French (fr)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- French Canadian (fr-CA)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Arabic (ar)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Bulgarian (bg)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Catalan (ca)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Croatian (hr)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Czech (cs)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Danish (da)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Dutch (nl)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Estonian (et)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Filipino (tl)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Finnish (fi)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- German (de)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Greek (el)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Hebrew (he)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Hindi (hi)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Hungarian (hu)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Indonesian (id)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Italian (it)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Japanese (ja)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Korean (ko)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Latvian (lv)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Lithuanian (lt)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Malay (ms)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Polish (pl)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Portuguese (pt)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Romanian (ro)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Russian (ru)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Serbian (sr)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Slovak (sk)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Slovenian (sl)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Spanish (es)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Swedish (sv)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Thai (th)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Turkish (tr)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Ukrainian (uk)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Vietnamese (vi)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Simplified Chinese (zh-Hans)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Traditional Chinese (zh-Hant)&lt;/br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Norwegian (nb)&lt;/br&gt;
"""
url = f"/tracking/2026-01/trackings/{id}"
url = f"/tracking/2026-07/trackings/{id}"

params_keys = {
"fields",
Expand Down Expand Up @@ -157,7 +157,7 @@ def update_tracking_by_id(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2026-01/trackings/{id}"
url = f"/tracking/2026-07/trackings/{id}"

body = update_tracking_by_id_request
if not isinstance(body, dict):
Expand All @@ -182,7 +182,7 @@ def delete_tracking_by_id(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2026-01/trackings/{id}"
url = f"/tracking/2026-07/trackings/{id}"

result = self._request("DELETE", url=url, **kwargs)
return DeleteTrackingByIdResponse.model_validate(result)
Expand All @@ -202,7 +202,7 @@ def retrack_tracking_by_id(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2026-01/trackings/{id}/retrack"
url = f"/tracking/2026-07/trackings/{id}/retrack"

result = self._request("POST", url=url, **kwargs)
return RetrackTrackingByIdResponse.model_validate(result)
Expand All @@ -226,7 +226,7 @@ def mark_tracking_completed_by_id(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2026-01/trackings/{id}/mark-as-completed"
url = f"/tracking/2026-07/trackings/{id}/mark-as-completed"

body = mark_tracking_completed_by_id_request
if not isinstance(body, dict):
Expand Down
24 changes: 24 additions & 0 deletions tracking/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"Meta",
"TrackingCourierEstimatedDeliveryDate",
"TrackingShipmentWeight",
"TrackingShipmentDimensions",
"TrackingOrderPromisedDeliveryDate",
"TrackingAftershipEstimatedDeliveryDate",
"TrackingCustomEstimatedDeliveryDateType",
Expand All @@ -27,6 +28,14 @@
"TrackingLastMileSource",
"TrackingLastMile",
"TrackingCustomers",
"TrackingProofOfDelivery",
"TrackingMultiPieceInfoType",
"TrackingMultiPieceInfoPiecesType",
"TrackingMultiPieceInfoPieces",
"TrackingMultiPieceInfo",
"TrackingShipmentDirection",
"TrackingReturnShipment",
"TrackingForwardShipment",
"Tracking",
"Tag",
"CheckpointCoordinate",
Expand Down Expand Up @@ -64,12 +73,14 @@
"CreateTrackingRequestDeliveryType",
"CreateTrackingRequestLastMile",
"CreateTrackingRequestCustomers",
"CreateTrackingRequestShipmentDirection",
"CreateTrackingRequest",
"CreateTrackingResponse",
"GetTrackingByIdResponse",
"UpdateTrackingByIdRequestOrderPromisedDeliveryDate",
"UpdateTrackingByIdRequestDeliveryType",
"UpdateTrackingByIdRequestCustomers",
"UpdateTrackingByIdRequestShipmentDirection",
"UpdateTrackingByIdRequest",
"UpdateTrackingByIdResponse",
"DeleteTrackingByIdResponse",
Expand Down Expand Up @@ -106,6 +117,7 @@
from .meta import Meta
from .tracking_courier_estimated_delivery_date import TrackingCourierEstimatedDeliveryDate
from .tracking_shipment_weight import TrackingShipmentWeight
from .tracking_shipment_dimensions import TrackingShipmentDimensions
from .tracking_order_promised_delivery_date import TrackingOrderPromisedDeliveryDate
from .tracking_aftership_estimated_delivery_date import TrackingAftershipEstimatedDeliveryDate
from .tracking_custom_estimated_delivery_date_type import TrackingCustomEstimatedDeliveryDateType
Expand All @@ -122,6 +134,14 @@
from .tracking_last_mile_source import TrackingLastMileSource
from .tracking_last_mile import TrackingLastMile
from .tracking_customers import TrackingCustomers
from .tracking_proof_of_delivery import TrackingProofOfDelivery
from .tracking_multi_piece_info_type import TrackingMultiPieceInfoType
from .tracking_multi_piece_info_pieces_type import TrackingMultiPieceInfoPiecesType
from .tracking_multi_piece_info_pieces import TrackingMultiPieceInfoPieces
from .tracking_multi_piece_info import TrackingMultiPieceInfo
from .tracking_shipment_direction import TrackingShipmentDirection
from .tracking_return_shipment import TrackingReturnShipment
from .tracking_forward_shipment import TrackingForwardShipment
from .tracking import Tracking
from .tag import Tag
from .checkpoint_coordinate import CheckpointCoordinate
Expand Down Expand Up @@ -183,6 +203,7 @@
from .create_tracking_request_delivery_type import CreateTrackingRequestDeliveryType
from .create_tracking_request_last_mile import CreateTrackingRequestLastMile
from .create_tracking_request_customers import CreateTrackingRequestCustomers
from .create_tracking_request_shipment_direction import CreateTrackingRequestShipmentDirection
from .create_tracking_request import CreateTrackingRequest
from .create_tracking_response import CreateTrackingResponse
from .get_tracking_by_id_response import GetTrackingByIdResponse
Expand All @@ -191,6 +212,9 @@
)
from .update_tracking_by_id_request_delivery_type import UpdateTrackingByIdRequestDeliveryType
from .update_tracking_by_id_request_customers import UpdateTrackingByIdRequestCustomers
from .update_tracking_by_id_request_shipment_direction import (
UpdateTrackingByIdRequestShipmentDirection,
)
from .update_tracking_by_id_request import UpdateTrackingByIdRequest
from .update_tracking_by_id_response import UpdateTrackingByIdResponse
from .delete_tracking_by_id_response import DeleteTrackingByIdResponse
Expand Down
1 change: 1 addition & 0 deletions tracking/models/checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class Checkpoint(BaseModel):
raw_tag: Optional[str] = None
events: Optional[List[CheckpointEvents]] = None
source: Optional[CheckpointSource] = None
hash: Optional[str] = None

def to_str(self, **kwargs) -> str:
return pprint.pformat(self.model_dump(**kwargs))
Expand Down
4 changes: 4 additions & 0 deletions tracking/models/create_tracking_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
from tracking.models.create_tracking_request_delivery_type import CreateTrackingRequestDeliveryType
from tracking.models.create_tracking_request_last_mile import CreateTrackingRequestLastMile
from tracking.models.create_tracking_request_customers import CreateTrackingRequestCustomers
from tracking.models.create_tracking_request_shipment_direction import (
CreateTrackingRequestShipmentDirection,
)


class CreateTrackingRequest(BaseModel):
Expand Down Expand Up @@ -59,6 +62,7 @@ class CreateTrackingRequest(BaseModel):
shipping_method: Optional[str] = None
last_mile: Optional[CreateTrackingRequestLastMile] = None
customers: Optional[List[CreateTrackingRequestCustomers]] = None
shipment_direction: Optional[CreateTrackingRequestShipmentDirection] = None

def to_str(self, **kwargs) -> str:
return pprint.pformat(self.model_dump(**kwargs))
Expand Down
1 change: 1 addition & 0 deletions tracking/models/create_tracking_request_customers.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class CreateTrackingRequestCustomers(BaseModel):
phone_number: Optional[str] = None
email: Optional[str] = None
language: Optional[str] = None
id: Optional[str] = None

def to_str(self, **kwargs) -> str:
return pprint.pformat(self.model_dump(**kwargs))
Expand Down
18 changes: 18 additions & 0 deletions tracking/models/create_tracking_request_shipment_direction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# coding: utf-8
#
# This code was auto generated by AfterShip SDK Generator.
# Do not edit the class manually.

from enum import Enum, unique


@unique
class CreateTrackingRequestShipmentDirection(Enum):
"""
Indicates the business direction of the shipment in the e-commerce fulfillment lifecycle.Possible values:- `forward`: A forward (outbound-to-customer) shipment created for order fulfillment.- `return`: A return (customer-to-merchant) shipment created for after-sales return or exchange.When provided, this field gives AfterShip additional context about the shipment's intent, enabling more accurate status identification.

allowed enum values
"""

FORWARD = "forward"
RETURN = "return"
2 changes: 1 addition & 1 deletion tracking/models/create_tracking_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class CreateTrackingResponse(BaseModel):
"""
Object describes the tracking information.&lt;div style=&#34;display:none; height: 0&#34;&gt;&lt;/div&gt;
Object describes the tracking information.&lt;div style=&#34;visibility:hidden; height: 0&#34;&gt;&lt;/div&gt;
"""

response_header: Dict[str, List[str]] = {}
Expand Down
2 changes: 1 addition & 1 deletion tracking/models/delete_tracking_by_id_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class DeleteTrackingByIdResponse(BaseModel):
"""
Object describes the tracking information.&lt;div style=&#34;display:none; height: 0&#34;&gt;&lt;/div&gt;
Object describes the tracking information.&lt;div style=&#34;visibility:hidden; height: 0&#34;&gt;&lt;/div&gt;
"""

response_header: Dict[str, List[str]] = {}
Expand Down
2 changes: 1 addition & 1 deletion tracking/models/get_tracking_by_id_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class GetTrackingByIdResponse(BaseModel):
"""
Object describes the tracking information.&lt;div style=&#34;display:none; height: 0&#34;&gt;&lt;/div&gt;
Object describes the tracking information.&lt;div style=&#34;visibility:hidden; height: 0&#34;&gt;&lt;/div&gt;
"""

response_header: Dict[str, List[str]] = {}
Expand Down
2 changes: 1 addition & 1 deletion tracking/models/mark_tracking_completed_by_id_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class MarkTrackingCompletedByIdResponse(BaseModel):
"""
Object describes the tracking information.&lt;div style=&#34;display:none; height: 0&#34;&gt;&lt;/div&gt;
Object describes the tracking information.&lt;div style=&#34;visibility:hidden; height: 0&#34;&gt;&lt;/div&gt;
"""

response_header: Dict[str, List[str]] = {}
Expand Down
2 changes: 1 addition & 1 deletion tracking/models/retrack_tracking_by_id_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class RetrackTrackingByIdResponse(BaseModel):
"""
Object describes the tracking information.&lt;div style=&#34;display:none; height: 0&#34;&gt;&lt;/div&gt;
Object describes the tracking information.&lt;div style=&#34;visibility:hidden; height: 0&#34;&gt;&lt;/div&gt;
"""

response_header: Dict[str, List[str]] = {}
Expand Down
Loading