ShipStation Interaction / Automation
Async Python client for ShipStation v1 and v2 APIs with an emphasis on typing.
Install
pip
pip install AsyncShipStation
Manual
git clone git@github.com:sudoDeVinci/AsyncShipStation.git
cd AsyncShipStation
pip install -r requirements.txt
Quick Start
Create a connection, then pass it to the portal you want to use.
import asyncio
from AsyncShipStation import ShipStationClient, ShipmentPortal
async def main() -> None:
connection = await ShipStationClient.connect(
v2_key="your_v2_api_key",
v1_key="your_v1_api_key",
v1_secret="your_v1_secret",
)
async with ShipStationClient.scoped_client(connection=connection, version="v2"):
status, shipments = await ShipmentPortal.list(
connection,
page_size=10,
page=1,
)
print(status, shipments)
if __name__ == "__main__":
asyncio.run(main())
The library comes pre-configured with 'sensible' default values for connections, but you can configure these parameters by passing a ConnectionConfig object with the parameters you would like changed.
config = ConnectionConfig(
version="v2",
timeout=30,
max_connections=20,
max_keepalive_connections=10,
http2=False,
retries=4,
user_agent="your_custom_UA_string",
)
connection = await ShipStationClient.connect(
v2_key="your_v2_api_key",
v1_key="your_v1_api_key",
v1_secret="your_v1_secret",
config=config,
)
NOTE: A connection's config is tied to its identity. Two connections to the ssame credentials, if using different configs, will be treated as separate connections.
Client Lifecycle
Use the async context manager w/ existing connection object
Use scoped_client() when you want the connection lifecycle handled for you.
import asyncio
import os
from dotenv import load_dotenv
from AsyncShipStation import ShipStationClient, ShipmentPortal
load_dotenv()
V2_API_KEY: str | None = os.getenv("SHIP_STATION_V2")
V1_API_KEY: str | None = os.getenv("SHIP_STATION_V1")
V1_SECRET: str | None = os.getenv("SHIP_STATION_SECRET")
async def main() -> None:
connection = await ShipStationClient.connect(
v2_key=V2_API_KEY or "",
v1_key=V1_API_KEY,
v1_secret=V1_SECRET,
)
async with ShipStationClient.scoped_client(connection=connection, version="v2"):
status, shipments = await ShipmentPortal.list(connection, page_size=10, page=1)
print(status, shipments)
if __name__ == "__main__":
asyncio.run(main())
Use the async context manager w/o existing connection object
If you don't need to reuse the connection object, you can skip the explicit configuration step and pass the credentials directly to scoped_client().
import asyncio
import os
from dotenv import load_dotenv
from AsyncShipStation import ShipStationClient, ShipmentPortal
load_dotenv()
V2_API_KEY: str | None = os.getenv("SHIP_STATION_V2")
V1_API_KEY: str | None = os.getenv("SHIP_STATION_V1")
V1_SECRET: str | None = os.getenv("SHIP_STATION_SECRET")
async def main() -> None:
connection = await ShipStationClient.connect(
)
async with ShipStationClient.scoped_client(
v2_key=V2_API_KEY or "",
v1_key=V1_API_KEY,
v1_secret=V1_SECRET, version="v2"
):
status, shipments = await ShipmentPortal.list(connection, page_size=10, page=1)
print(status, shipments)
if __name__ == "__main__":
asyncio.run(main())
Start and close explicitly
Use start() and close() if you want to manage the lifecycle yourself.
import asyncio
import os
from dotenv import load_dotenv
from AsyncShipStation import ShipStationClient, ShipmentPortal
load_dotenv()
V2_API_KEY: str | None = os.getenv("SHIP_STATION_V2")
V1_API_KEY: str | None = os.getenv("SHIP_STATION_V1")
V1_SECRET: str | None = os.getenv("SHIP_STATION_SECRET")
async def main() -> None:
connection = await ShipStationClient.connect(
v2_key=V2_API_KEY or "",
v1_key=V1_API_KEY,
v1_secret=V1_SECRET,
)
await ShipStationClient.start(connection=connection, version="v2")
try:
status, shipments = await ShipmentPortal.list(connection, page_size=10, page=1)
print(status, shipments)
finally:
await ShipStationClient.close(connection=connection, version="v2")
if __name__ == "__main__":
asyncio.run(main())
Concurrent Requests
A single connection is meant to be shared across concurrent requests.
import asyncio
from AsyncShipStation import (
BatchPortal,
LabelPortal,
ShipmentPortal,
ShipStationClient,
)
async def main() -> None:
connection = await ShipStationClient.connect(
v2_key="your_v2_api_key",
v1_key="your_v1_api_key",
v1_secret="your_v1_secret",
)
async with ShipStationClient.scoped_client(connection=connection, version="v2"):
results = await asyncio.gather(
ShipmentPortal.list(connection, page_size=10, page=1),
BatchPortal.list(connection, page_size=10, page=1),
LabelPortal.list(connection, page_size=10, page=1),
)
for status, data in results:
if status in (200, 201, 207):
print(f"Success :: {data}")
else:
print(f"Error :: {data}")
if __name__ == "__main__":
asyncio.run(main())
Connection Lookup
If you need to retrieve a connection from the pool later, use connection.uid.
import asyncio
from AsyncShipStation import ShipStationClient, ShipmentPortal
async def main() -> None:
connection = await ShipStationClient.connect(
v2_key="your_v2_api_key",
v1_key="your_v1_api_key",
v1_secret="your_v1_secret",
)
async with ShipStationClient.scoped_client(
connection_hash=connection.pool_key,
version="v2",
) as scoped_connection:
status, shipments = await ShipmentPortal.list(
scoped_connection,
page_size=10,
page=1,
)
print(status, shipments)
if __name__ == "__main__":
asyncio.run(main())
Rate Limiting
Accounts that send too many requests in quick succession will receive a 429 Too Many Requests response with a Retry-After header that tells you how long to wait.
ShipStation bulk operation endpoints count as a single request.
TODO
Metadata
Release files for AsyncShipStation 0.2.1.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| asyncshipstation-0.2.1.8.tar.gz | 51.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| asyncshipstation-0.2.1.8-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 158.9 kB
Release files / asyncshipstation-0.2.1.8.tar.gz
| Download URL | asyncshipstation-0.2.1.8.tar.gz |
|---|---|
| Size | 51.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f8d15901b47bc5865e6aaec4107a9451c4f13ecd3e8a62553cf415f32f9403f8
|
|
BLAKE2b-256 checksum How to use checksums |
2899e8f5db1fb51a41133287a0fc67bd3b58d769644ff0aa08a8c9a198af41d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / asyncshipstation-0.2.1.8-py3-none-any.whl
| Download URL | asyncshipstation-0.2.1.8-py3-none-any.whl |
|---|---|
| Size | 107.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2c8241fb081dc65421029ed63507c1ceab4a63dcb70b068fd7d5d8883173b057
|
|
BLAKE2b-256 checksum How to use checksums |
2f03900d904c5993d8a6b2ad95f60e2d8c296c1ae780f2878d7a808d838758d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Security for AsyncShipStation 0.2.1.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.