Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}

- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: ${{ steps.get_version.outputs.VERSION }}
path: dist
Expand Down
341 changes: 225 additions & 116 deletions README.md

Large diffs are not rendered by default.

415 changes: 206 additions & 209 deletions poetry.lock

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
[tool.poetry]
name = "aftership-tracking-sdk"
version = "7.1.0"
version = "8.0.0"
description = "The official AfterShip Tracking Python API library"
authors = ["AfterShip <support@aftership.com>"]
license = "MIT"
readme = "README.md"
keywords = ["aftership", "tracking", "track", "fedex", "ups", "usps", "dhl", "shipping", "fulfillment", "couriers", "carriers", "logistics"]
keywords = ["aftership", "tracking", "dhl"]

classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Topic :: Software Development",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Topic :: Software Development",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]

packages = [
{ include = "tracking" },
{ include = "*.md" },
{ include = "LICENSE" },
{ include = "tracking" },
{ include = "*.md" },
{ include = "LICENSE" },
]

[tool.poetry.dependencies]
python = "^3.8"
pycryptodome = ">= 3.9.0"
pydantic = ">=2"
httpx = ">=0.27.0"
retrying = "^1.3.4"
typing_extensions = ">=4.7.1"
urllib3 = "^2.2.2"
socksio = "^1.0.0"
httpx = "^0.28.1"

[tool.poetry.group.dev.dependencies]
ruff = "^0.4.8"
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__ = "7.1.0"
__version__ = "8.0.0"
4 changes: 2 additions & 2 deletions tracking/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
# Do not edit the class manually.

__all__ = [
"CourierApi",
"CourierConnectionApi",
"EstimatedDeliveryDateApi",
"TrackingApi",
"CourierApi",
]

from .courier import CourierApi
from .courier_connection import CourierConnectionApi
from .estimated_delivery_date import EstimatedDeliveryDateApi
from .tracking import TrackingApi
from .courier import CourierApi
53 changes: 27 additions & 26 deletions tracking/api/courier.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@


from tracking.models import (
GetCouriersResponse,
DetectCourierRequest,
DetectCourierResponse,
GetCouriersResponse,
)
from tracking.request import ApiClient, validate_params

Expand All @@ -19,51 +19,52 @@ class CourierApi(ApiClient):
"""CourierApi api implements"""

@validate_params
def detect_courier(
self, detect_courier_request: Union[DetectCourierRequest, dict], **kwargs
) -> DetectCourierResponse:
def get_couriers(self, **kwargs) -> GetCouriersResponse:
"""
Return a list of matched couriers based on tracking number format and or a list of couriers.
:param detect_courier_request:
Return a list of couriers.
:param kwargs:
request options:
**headers** (dict): support custom headers.
**verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
verify the identity of requested hosts. Either `True` (default CA bundle),
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
query params:
**active**: bool. get user activated couriers
**slug**: str. Unique courier code Use comma for multiple values. (Example: dhl,ups,usps)
"""
url = "/tracking/2025-07/couriers/detect"
url = "/tracking/2025-07/couriers"

body = detect_courier_request
if not isinstance(body, dict):
body = detect_courier_request.model_dump(exclude_none=True)
body = json.dumps(body)
params_keys = {
"active",
"slug",
}
params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}

result = self._request("POST", url=url, body=body, **kwargs)
return DetectCourierResponse().from_dict(result)
result = self._request("GET", url=url, params=params, **kwargs)
return GetCouriersResponse.model_validate(result)

@validate_params
def get_couriers(self, **kwargs) -> GetCouriersResponse:
def detect_courier(
self, detect_courier_request: Union[DetectCourierRequest, dict], **kwargs
) -> DetectCourierResponse:
"""
Return a list of couriers.
Return a list of matched couriers based on tracking number format and or a list of couriers.
:param detect_courier_request:
:param kwargs:
request options:
**headers** (dict): support custom headers.
**verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
verify the identity of requested hosts. Either `True` (default CA bundle),
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
query params:
**active**: bool. get user activated couriers
**slug**: str. Unique courier code Use comma for multiple values. (Example: dhl,ups,usps)
"""
url = "/tracking/2025-07/couriers"
params_keys = {
"active",
"slug",
}
params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
url = "/tracking/2025-07/couriers/detect"

result = self._request("GET", url=url, params=params, **kwargs)
return GetCouriersResponse().from_dict(result)
body = detect_courier_request
if not isinstance(body, dict):
body = detect_courier_request.model_dump(exclude_none=True, mode="json")
body = json.dumps(body)

result = self._request("POST", url=url, body=body, **kwargs)
return DetectCourierResponse.model_validate(result)
104 changes: 50 additions & 54 deletions tracking/api/courier_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,20 @@
from pydantic import Field

from tracking.models import (
DeleteCourierConnectionsByIdResponse,
GetCourierConnectionsResponse,
GetCourierConnectionsByIdResponse,
PostCourierConnectionsRequest,
PostCourierConnectionsResponse,
GetCourierConnectionsByIdResponse,
PutCourierConnectionsByIdRequest,
PutCourierConnectionsByIdResponse,
DeleteCourierConnectionsByIdResponse,
)
from tracking.request import ApiClient, validate_params


class CourierConnectionApi(ApiClient):
"""CourierConnectionApi api implements"""

@validate_params
def delete_courier_connections_by_id(
self, courier_connection_id: Annotated[str, Field(min_length=1)], **kwargs
) -> DeleteCourierConnectionsByIdResponse:
"""
Delete a courier connection.
:param courier_connection_id: str.
:param kwargs:
request options:
**headers** (dict): support custom headers.
**verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
verify the identity of requested hosts. Either `True` (default CA bundle),
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2025-07/courier-connections/{courier_connection_id}"

result = self._request("DELETE", url=url, **kwargs)
return DeleteCourierConnectionsByIdResponse().from_dict(result)

@validate_params
def get_courier_connections(self, **kwargs) -> GetCourierConnectionsResponse:
"""
Expand All @@ -60,6 +40,7 @@ def get_courier_connections(self, **kwargs) -> GetCourierConnectionsResponse:
**limit**: str. Number of courier connections each page contain. (Default: 100, Max: 200)
"""
url = "/tracking/2025-07/courier-connections"

params_keys = {
"courier_slug",
"cursor",
Expand All @@ -68,20 +49,15 @@ def get_courier_connections(self, **kwargs) -> GetCourierConnectionsResponse:
params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}

result = self._request("GET", url=url, params=params, **kwargs)
return GetCourierConnectionsResponse().from_dict(
{
"pagination": result.get("pagination"),
"courier_connections": result.get("courier_connections"),
}
)
return GetCourierConnectionsResponse.model_validate(result)

@validate_params
def get_courier_connections_by_id(
self, courier_connection_id: Annotated[str, Field(min_length=1)], **kwargs
) -> GetCourierConnectionsByIdResponse:
def post_courier_connections(
self, post_courier_connections_request: Union[PostCourierConnectionsRequest, dict], **kwargs
) -> PostCourierConnectionsResponse:
"""
Get courier connection results of a single courier connection.
:param courier_connection_id: str.

:param post_courier_connections_request:
:param kwargs:
request options:
**headers** (dict): support custom headers.
Expand All @@ -90,18 +66,23 @@ 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/2025-07/courier-connections/{courier_connection_id}"
url = "/tracking/2025-07/courier-connections"

result = self._request("GET", url=url, **kwargs)
return GetCourierConnectionsByIdResponse().from_dict(result)
body = post_courier_connections_request
if not isinstance(body, dict):
body = post_courier_connections_request.model_dump(exclude_none=True, mode="json")
body = json.dumps(body)

result = self._request("POST", url=url, body=body, **kwargs)
return PostCourierConnectionsResponse.model_validate(result)

@validate_params
def post_courier_connections(
self, post_courier_connections_request: Union[PostCourierConnectionsRequest, dict], **kwargs
) -> PostCourierConnectionsResponse:
def get_courier_connections_by_id(
self, id: Annotated[str, Field(min_length=1)], **kwargs
) -> GetCourierConnectionsByIdResponse:
"""

:param post_courier_connections_request:
Get courier connection results of a single courier connection.
:param id: str.
:param kwargs:
request options:
**headers** (dict): support custom headers.
Expand All @@ -110,26 +91,21 @@ def post_courier_connections(
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = "/tracking/2025-07/courier-connections"

body = post_courier_connections_request
if not isinstance(body, dict):
body = post_courier_connections_request.model_dump(exclude_none=True)
body = json.dumps(body)
url = f"/tracking/2025-07/courier-connections/{id}"

result = self._request("POST", url=url, body=body, **kwargs)
return PostCourierConnectionsResponse().from_dict(result)
result = self._request("GET", url=url, **kwargs)
return GetCourierConnectionsByIdResponse.model_validate(result)

@validate_params
def put_courier_connections_by_id(
self,
courier_connection_id: Annotated[str, Field(min_length=1)],
id: Annotated[str, Field(min_length=1)],
put_courier_connections_by_id_request: Union[PutCourierConnectionsByIdRequest, dict],
**kwargs,
) -> PutCourierConnectionsByIdResponse:
"""
Update a courier connection.
:param courier_connection_id: str.
:param id: str.
:param put_courier_connections_by_id_request:
:param kwargs:
request options:
Expand All @@ -139,12 +115,32 @@ 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/2025-07/courier-connections/{courier_connection_id}"
url = f"/tracking/2025-07/courier-connections/{id}"

body = put_courier_connections_by_id_request
if not isinstance(body, dict):
body = put_courier_connections_by_id_request.model_dump(exclude_none=True)
body = put_courier_connections_by_id_request.model_dump(exclude_none=True, mode="json")
body = json.dumps(body)

result = self._request("PATCH", url=url, body=body, **kwargs)
return PutCourierConnectionsByIdResponse().from_dict(result)
return PutCourierConnectionsByIdResponse.model_validate(result)

@validate_params
def delete_courier_connections_by_id(
self, id: Annotated[str, Field(min_length=1)], **kwargs
) -> DeleteCourierConnectionsByIdResponse:
"""
Delete a courier connection.
:param id: str.
:param kwargs:
request options:
**headers** (dict): support custom headers.
**verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
verify the identity of requested hosts. Either `True` (default CA bundle),
a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
(which will disable verification).
"""
url = f"/tracking/2025-07/courier-connections/{id}"

result = self._request("DELETE", url=url, **kwargs)
return DeleteCourierConnectionsByIdResponse.model_validate(result)
14 changes: 8 additions & 6 deletions tracking/api/estimated_delivery_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


from tracking.models import (
PredictRequest,
EstimatedDeliveryDateRequest,
PredictResponse,
PredictBatchRequest,
PredictBatchResponse,
Expand All @@ -20,7 +20,9 @@ class EstimatedDeliveryDateApi(ApiClient):
"""EstimatedDeliveryDateApi api implements"""

@validate_params
def predict(self, predict_request: Union[PredictRequest, dict], **kwargs) -> PredictResponse:
def predict(
self, predict_request: Union[EstimatedDeliveryDateRequest, dict], **kwargs
) -> PredictResponse:
"""
> The estimated delivery date is provided by AfterShip, based on its AI-predictive model. You can display the EDD on the product page, cart, and order checkout page. It indicates when a customer will receive the order.You can use to activate this feature.
:param predict_request:
Expand All @@ -36,11 +38,11 @@ def predict(self, predict_request: Union[PredictRequest, dict], **kwargs) -> Pre

body = predict_request
if not isinstance(body, dict):
body = predict_request.model_dump(exclude_none=True)
body = predict_request.model_dump(exclude_none=True, mode="json")
body = json.dumps(body)

result = self._request("POST", url=url, body=body, **kwargs)
return PredictResponse().from_dict(result)
return PredictResponse.model_validate(result)

@validate_params
def predict_batch(
Expand All @@ -61,8 +63,8 @@ def predict_batch(

body = predict_batch_request
if not isinstance(body, dict):
body = predict_batch_request.model_dump(exclude_none=True)
body = predict_batch_request.model_dump(exclude_none=True, mode="json")
body = json.dumps(body)

result = self._request("POST", url=url, body=body, **kwargs)
return PredictBatchResponse().from_dict(result)
return PredictBatchResponse.model_validate(result)
Loading