A Python client for the Zid e-commerce platform API.
pip install zid-clientRequires Python 3.10+.
fromzidimportZidClient# Initialize the client with your OAuth tokensclient=ZidClient(
authorization="your-partner-token",
store_token="store-access-token",
)
# List ordersfororderinclient.orders.list():
print(order.id, order.order_status.code)
# Get a specific customercustomer=client.customers.get(12345)
print(customer.name, customer.email)The SDK uses OAuth 2.0 tokens obtained through Zid's OAuth flow:
authorization: Your partner/app token (theAuthorizationvalue from OAuth callback)store_token: Store-level access token (theaccess_tokenfrom OAuth callback)
client=ZidClient(
authorization="abc123...",
store_token="xyz789...",
)Configure the client to automatically refresh expired tokens:
defsave_tokens(auth):
"""Persist new tokens to your database."""db.update_tokens(
store_token=auth.store_token,
refresh_token=auth.refresh_token,
authorization=auth.authorization,
)
client=ZidClient(
authorization="abc123...",
store_token="xyz789...",
refresh_token="refresh-token",
client_id="48",
client_secret="your-secret",
redirect_uri="https://yourapp.com/callback",
on_tokens_refreshed=save_tokens,
)When a request fails with 401, the SDK will automatically refresh tokens and retry.
- 13 resources, 50+ typed Pydantic models with full IDE autocomplete
- Composable sub-resources (e.g.
client.products.images,client.products.variants) - Automatic pagination across cursor-based and offset/limit APIs
- Auto token refresh with a callback to persist new credentials
- Retry with exponential backoff, jitter, and rate limit handling
- Dict-style access on models (
order["id"]works alongsideorder.id) .rawproperty on every model to access undocumented API fields- Automatic camelCase ↔ snake_case field mapping
- Lazy resource initialization — resources are created on first access
# List productsforproductinclient.products.list():
print(product.id, product.name)
# CRUDproduct=client.products.get("product-uuid")
product=client.products.create(name="Wireless Headphones", price=257, sku="WH-1000XM5")
product=client.products.update("product-uuid", price=79.99)
client.products.delete("product-uuid")
# Sub-resourcesimages=client.products.images.list("product-uuid")
client.products.images.upload("product-uuid", image=("photo.jpg", b"...", "image/jpeg"), alt_text="Front view")
client.products.variants.create("product-uuid", variants=[{"sku": "WH-BLK", "price": 257, "attributes": [...]}])
stocks=client.products.stocks.list("product-uuid")
categories=client.products.categories.list()
notifications=client.products.notifications.list(product_id="product-uuid")# List orders (paginated, filterable)fororderinclient.orders.list():
print(order.id, order.order_status.code)
fororderinclient.orders.list(order_status="new", payload_type="default"):
print(order.id, order.products)
# Get, update status, credit notesorder=client.orders.get(12345)
client.orders.update_status(12345, order_status="preparing")
credit_notes=client.orders.list_credit_notes(12345)
# Create a draft order (requires customer, consignee, products, shipping, payment)order=client.orders.create(
currency_code="SAR",
customer={"full_name": "John Doe", "mobile_country_code": "966", "mobile_number": "500000000"},
consignee={
"contact": {"full_name": "John Doe", "mobile_country_code": "966", "mobile_number": "500000000"},
"address": {"line_1": "King Fahd Road", "city_name": "Riyadh", "country_code": "SA"},
},
products=[{"sku": "PROD-SKU-123", "quantity": 1}],
shipping_method={"type": "delivery", "id": 432480},
payment_method={"id": 555224},
)# List customersforcustomerinclient.customers.list():
print(customer.name, customer.email)
# Get a specific customercustomer=client.customers.get(12345)| Resource | Accessor | Key Methods |
|---|---|---|
| Coupons | client.coupons | list(), get(id), create(...), delete(id) |
| Locations | client.locations | list(), get(id), create(...), update(...), update_stock(id, items) |
| Abandoned Carts | client.abandoned_carts | list(), get(cart_uuid) |
| Reverse Orders | client.reverse_orders | create(...), list_reasons(), refund(...), create_waybill(...) |
| Delivery Options | client.delivery_options | list() |
| Payment Methods | client.payment_methods | list() |
| Store Profile | client.stores | get_profile(), get_vat_settings() |
| Geography | client.geography | list_operating_countries(), list_all_countries(), list_cities(country_id) |
| Webhooks | client.webhooks | list(), create(event, target_url, original_id), delete(original_id) |
| Bundle Offers | client.bundle_offers | list() |
| Loyalty | client.loyalty | get_status(), get_program(), get_customer_summary(customer_id), adjust_customer_points(...) |
All list methods return a PaginatedIterator. All resources have full docstrings — use your IDE's autocomplete or help() for parameter details.
List methods return a PaginatedIterator that handles pagination automatically:
# Iterate through all pages transparentlyfororderinclient.orders.list():
print(order.id)
# Get total count without iteratingorders=client.orders.list()
print(f"Total orders: {len(orders)}")
# Control page sizefororderinclient.orders.list(per_page=100):
print(order.id)
# Convenience methodslatest=client.orders.list(order_status="new").first()
top_5=client.products.list().take(5)
all_customers=client.customers.list().to_list()The SDK raises specific exceptions for different error types:
fromzidimport (
ZidError,
ZidAPIError,
ZidAuthenticationError,
ZidAuthorizationError,
ZidNotFoundError,
ZidValidationError,
ZidRateLimitError,
ZidServerError,
ZidConnectionError,
)
try:
customer=client.customers.get(99999)
exceptZidNotFoundError:
print("Customer not found")
exceptZidAuthenticationErrorase:
print(f"Auth failed: {e.error_code}")
exceptZidRateLimitErrorase:
print(f"Rate limited. Retry after {e.retry_after}s")
exceptZidValidationErrorase:
print(f"Validation errors: {e.errors}")
exceptZidAPIErrorase:
print(f"API error {e.status_code}: {e.message}")
exceptZidConnectionError:
print("Network error")ZidError— Base exceptionZidAPIError— API returned an error responseZidAuthenticationError— 401 UnauthorizedZidAuthorizationError— 403 ForbiddenZidNotFoundError— 404 Not FoundZidValidationError— 400/422 Validation errorsZidRateLimitError— 429 Too Many RequestsZidServerError— 5xx Server errors
ZidConnectionError— Network failuresAuthError— Invalid auth configurationTokenRefreshError— Token refresh failed
Use the client as a context manager to ensure proper cleanup:
withZidClient(authorization="...", store_token="...") asclient:
orders=list(client.orders.list())
# Connection is automatically closedclient=ZidClient(
authorization="...",
store_token="...",
base_url="https://api.zid.sa", # Defaulttimeout=30.0, # Request timeout in secondslanguage="en", # Accept-Language header (en/ar)auto_refresh=True, # Auto-refresh tokens on 401
)The SDK automatically retries failed requests with exponential backoff:
fromzidimportZidClient, RetryConfig# Default behavior: 3 retries with exponential backoffclient=ZidClient(authorization="...", store_token="...")
# Custom retry configurationclient=ZidClient(
authorization="...",
store_token="...",
retry=RetryConfig(
max_retries=5, # Number of retry attemptsbase_delay=0.5, # Initial delay (seconds)max_delay=30.0, # Maximum delay between retriesretry_on_rate_limit=True, # Auto-wait on 429 responsesmax_rate_limit_wait=120.0, # Max seconds to wait for rate limit
),
)
# Disable retries entirelyclient=ZidClient(
authorization="...",
store_token="...",
retry=RetryConfig(max_retries=0),
)The retry logic handles:
- Server errors (500, 502, 503, 504) with exponential backoff + jitter
- Rate limits (429) by waiting for the
Retry-Afterduration - Connection errors and timeouts
MIT