Python SDK

The official Python SDK for Unified Commerce Platform. Built with modern Python features, featuring full async support, comprehensive type hints, and Pydantic models for data validation.

Version: 1.8.3 | License: MIT | Python: 3.8+ | Size: ~180KB

Installation

Install the SDK using pip:

# Using pip
pip install unified-commerce

# Using poetry
poetry add unified-commerce

# Using pipenv
pipenv install unified-commerce

# With async extras
pip install unified-commerce[async]

Requirements

  • Python 3.8 or higher
  • pip 21.0 or higher

Optional Dependencies

# Async support (httpx, aiohttp)
pip install unified-commerce[async]

# Django integration
pip install unified-commerce[django]

# FastAPI integration
pip install unified-commerce[fastapi]

# All extras
pip install unified-commerce[all]

Quick Start

Basic Setup

import os
from unified_commerce import UnifiedCommerce

# Initialize the client
client = UnifiedCommerce(
    api_key=os.environ['UNIFIED_COMMERCE_API_KEY'],
    environment='production'  # or 'sandbox' for testing
)

# Query products
products = client.products.list(
    category='electronics',
    limit=10
)

for product in products:
    print(f"{product.name}: ${product.price}")

Async Setup

import asyncio
import os
from unified_commerce import AsyncUnifiedCommerce

async def main():
    # Initialize async client
    client = AsyncUnifiedCommerce(
        api_key=os.environ['UNIFIED_COMMERCE_API_KEY'],
        environment='production'
    )

    # Query products asynchronously
    products = await client.products.list(
        category='electronics',
        limit=10
    )

    for product in products:
        print(f"{product.name}: ${product.price}")

    # Close the client
    await client.close()

if __name__ == '__main__':
    asyncio.run(main())

Context Manager

from unified_commerce import UnifiedCommerce

# Automatically handles client lifecycle
with UnifiedCommerce(api_key=os.environ['UNIFIED_COMMERCE_API_KEY']) as client:
    products = client.products.list(limit=10)
    print(products)

# Async context manager
async with AsyncUnifiedCommerce(api_key=os.environ['UNIFIED_COMMERCE_API_KEY']) as client:
    products = await client.products.list(limit=10)
    print(products)

Configuration

Client Options

from unified_commerce import UnifiedCommerce

client = UnifiedCommerce(
    # Required: Your API key
    api_key='uc_live_xxxxxxxxxxxxx',

    # Environment: 'production' or 'sandbox'
    environment='production',

    # Custom API endpoint
    api_url='https://api.custom-domain.com',

    # Request timeout in seconds (default: 30)
    timeout=60,

    # Maximum retry attempts (default: 3)
    max_retries=5,

    # Retry backoff factor (default: 2)
    retry_backoff=2,

    # Enable debug logging (default: False)
    debug=True,

    # Custom headers for all requests
    headers={
        'X-Custom-Header': 'value'
    },

    # HTTP session (for connection pooling)
    session=custom_session,

    # Proxy configuration
    proxies={
        'http': 'http://proxy.example.com:8080',
        'https': 'https://proxy.example.com:8080'
    }
)

Environment Variables

Create a .env file in your project:

# .env
UNIFIED_COMMERCE_API_KEY=uc_live_xxxxxxxxxxxxx
UNIFIED_COMMERCE_ENVIRONMENT=production
UNIFIED_COMMERCE_TIMEOUT=60

Load environment variables:

from dotenv import load_dotenv
from unified_commerce import UnifiedCommerce

load_dotenv()

client = UnifiedCommerce.from_env()  # Loads from environment

Authentication

API Key Authentication

from unified_commerce import UnifiedCommerce

# Method 1: Direct initialization
client = UnifiedCommerce(api_key='uc_live_xxxxxxxxxxxxx')

# Method 2: From environment variable
client = UnifiedCommerce(api_key=os.environ['UNIFIED_COMMERCE_API_KEY'])

# Method 3: Using configuration object
from unified_commerce import Config

config = Config(
    api_key='uc_live_xxxxxxxxxxxxx',
    environment='production'
)
client = UnifiedCommerce(config=config)

Per-Request Authentication

# Override API key for specific requests
products = client.products.list(
    limit=10,
    api_key='different_api_key'
)

Service Account (Coming Soon)

from unified_commerce import UnifiedCommerce

client = UnifiedCommerce(
    service_account={
        'type': 'service_account',
        'project_id': 'your-project-id',
        'private_key': 'your-private-key',
        'client_email': 'service@project.iam.gserviceaccount.com'
    }
)

Core Concepts

Products

from unified_commerce import UnifiedCommerce
from unified_commerce.models import Product, ProductCreate, ProductUpdate

client = UnifiedCommerce(api_key=os.environ['UNIFIED_COMMERCE_API_KEY'])

# List products with filters
products = client.products.list(
    category='electronics',
    min_price=100,
    max_price=1000,
    in_stock=True,
    limit=20,
    offset=0,
    sort_by='price',
    sort_order='asc'
)

# Get a single product
product = client.products.get('product-123')

# Create a product
new_product = client.products.create(
    ProductCreate(
        name='Wireless Headphones',
        description='Premium noise-cancelling headphones',
        price=299.99,
        category='electronics',
        sku='WH-1000XM5',
        inventory={
            'quantity': 100,
            'track_inventory': True
        }
    )
)

# Update a product
updated = client.products.update(
    'product-123',
    ProductUpdate(
        price=279.99,
        inventory={'quantity': 150}
    )
)

# Delete a product
client.products.delete('product-123')

# Search products
results = client.products.search(
    query='wireless headphones',
    filters={'category': 'electronics'}
)

Orders

from unified_commerce.models import OrderCreate, OrderItem, Customer, Address

# Create an order
order = client.orders.create(
    OrderCreate(
        items=[
            OrderItem(
                product_id='product-123',
                quantity=2,
                price=299.99
            ),
            OrderItem(
                product_id='product-456',
                quantity=1,
                price=149.99
            )
        ],
        customer=Customer(
            id='customer-789',
            email='customer@example.com',
            name='John Doe'
        ),
        shipping_address=Address(
            street='123 Main St',
            city='San Francisco',
            state='CA',
            postal_code='94105',
            country='US'
        ),
        payment_method={
            'type': 'card',
            'token': 'pm_xxxxxxxxxxxxx'
        }
    )
)

# List orders
orders = client.orders.list(
    customer_id='customer-789',
    status='pending',
    limit=20
)

# Get order details
order_details = client.orders.get('order-123')

# Update order status
client.orders.update(
    'order-123',
    status='shipped',
    tracking={
        'carrier': 'UPS',
        'tracking_number': '1Z999AA10123456784'
    }
)

# Cancel an order
client.orders.cancel(
    'order-123',
    reason='Customer requested cancellation'
)

Customers

from unified_commerce.models import CustomerCreate, CustomerUpdate

# Create a customer
customer = client.customers.create(
    CustomerCreate(
        email='customer@example.com',
        name='John Doe',
        phone='+1234567890',
        addresses=[{
            'type': 'shipping',
            'street': '123 Main St',
            'city': 'San Francisco',
            'state': 'CA',
            'postal_code': '94105',
            'country': 'US'
        }]
    )
)

# List customers
customers = client.customers.list(
    search='john',
    limit=20
)

# Update customer
client.customers.update(
    'customer-123',
    CustomerUpdate(phone='+9876543210')
)

# Get customer with orders
customer_with_orders = client.customers.get(
    'customer-123',
    include=['orders', 'addresses']
)

Inventory

from unified_commerce.models import InventoryUpdate

# Get inventory for a product
inventory = client.inventory.get('product-123')

# Update inventory
client.inventory.update(
    'product-123',
    InventoryUpdate(
        quantity=100,
        location='warehouse-1',
        reserved_quantity=5
    )
)

# Bulk update
client.inventory.bulk_update([
    {'product_id': 'product-123', 'quantity': 100},
    {'product_id': 'product-456', 'quantity': 50}
])

# Track inventory movements
movements = client.inventory.movements(
    'product-123',
    start_date='2024-01-01',
    end_date='2024-01-31'
)

# Reserve inventory
reservation = client.inventory.reserve(
    'product-123',
    quantity=5,
    expires_at='2024-12-31T23:59:59Z'
)

Async Support

Async Client

import asyncio
from unified_commerce import AsyncUnifiedCommerce

async def fetch_products():
    async with AsyncUnifiedCommerce(
        api_key=os.environ['UNIFIED_COMMERCE_API_KEY']
    ) as client:
        # Concurrent requests
        products, orders, customers = await asyncio.gather(
            client.products.list(limit=10),
            client.orders.list(limit=10),
            client.customers.list(limit=10)
        )

        return products, orders, customers

# Run async function
products, orders, customers = asyncio.run(fetch_products())

Async Iteration

async def process_all_products():
    async with AsyncUnifiedCommerce(
        api_key=os.environ['UNIFIED_COMMERCE_API_KEY']
    ) as client:
        # Async iterator for pagination
        async for product in client.products.iterate(category='electronics'):
            print(f"Processing {product.name}")
            await process_product(product)

Async Context Managers

from unified_commerce import AsyncUnifiedCommerce

class ProductProcessor:
    def __init__(self, api_key: str):
        self.client = AsyncUnifiedCommerce(api_key=api_key)

    async def __aenter__(self):
        await self.client.__aenter__()
        return self

    async def __aexit__(self, *args):
        await self.client.__aexit__(*args)

    async def process(self):
        products = await self.client.products.list(limit=100)
        # Process products
        return products

# Use it
async with ProductProcessor(api_key=os.environ['UNIFIED_COMMERCE_API_KEY']) as processor:
    results = await processor.process()

Django Integration

Settings Configuration

# settings.py
UNIFIED_COMMERCE = {
    'API_KEY': os.environ['UNIFIED_COMMERCE_API_KEY'],
    'ENVIRONMENT': 'production',
    'TIMEOUT': 30,
    'MAX_RETRIES': 3,
    'DEBUG': DEBUG
}

Model Integration

# models.py
from django.db import models
from unified_commerce.django import UnifiedCommerceField

class Product(models.Model):
    name = models.CharField(max_length=255)
    uc_product = UnifiedCommerceField()  # Syncs with Unified Commerce

    def save(self, *args, **kwargs):
        # Auto-sync to Unified Commerce
        if not self.uc_product:
            from unified_commerce.django import get_client
            client = get_client()
            uc_product = client.products.create({
                'name': self.name,
                # ... other fields
            })
            self.uc_product = uc_product.id
        super().save(*args, **kwargs)

Views Integration

# views.py
from django.http import JsonResponse
from unified_commerce.django import get_client

def product_list(request):
    client = get_client()
    products = client.products.list(
        category=request.GET.get('category'),
        limit=20
    )

    return JsonResponse({
        'products': [p.dict() for p in products]
    })

FastAPI Integration

Setup

# main.py
from fastapi import FastAPI, Depends
from unified_commerce.fastapi import UnifiedCommerceClient, get_client

app = FastAPI()

# Initialize at startup
@app.on_event("startup")
async def startup():
    await UnifiedCommerceClient.initialize(
        api_key=os.environ['UNIFIED_COMMERCE_API_KEY']
    )

@app.on_event("shutdown")
async def shutdown():
    await UnifiedCommerceClient.close()

Dependency Injection

from fastapi import APIRouter, Depends
from unified_commerce.fastapi import get_client
from unified_commerce import AsyncUnifiedCommerce

router = APIRouter()

@router.get("/products")
async def list_products(
    client: AsyncUnifiedCommerce = Depends(get_client)
):
    products = await client.products.list(limit=20)
    return {"products": products}

@router.post("/orders")
async def create_order(
    order_data: OrderCreate,
    client: AsyncUnifiedCommerce = Depends(get_client)
):
    order = await client.orders.create(order_data)
    return {"order": order}

Background Tasks

from fastapi import BackgroundTasks
from unified_commerce.fastapi import get_client

async def sync_inventory(product_id: str):
    client = await get_client()
    inventory = await client.inventory.get(product_id)
    # Update local database
    await update_local_inventory(inventory)

@router.post("/products/{product_id}/sync")
async def sync_product_inventory(
    product_id: str,
    background_tasks: BackgroundTasks
):
    background_tasks.add_task(sync_inventory, product_id)
    return {"status": "syncing"}

Error Handling

Exception Types

from unified_commerce.exceptions import (
    UnifiedCommerceError,
    AuthenticationError,
    ValidationError,
    NotFoundError,
    RateLimitError,
    NetworkError,
    ServerError
)

try:
    product = client.products.get('invalid-id')
except NotFoundError as e:
    print(f"Product not found: {e.message}")
except AuthenticationError as e:
    print(f"Invalid API key: {e.message}")
except RateLimitError as e:
    print(f"Rate limit exceeded. Retry after: {e.retry_after} seconds")
except ValidationError as e:
    print(f"Validation errors: {e.errors}")
    for field, errors in e.errors.items():
        print(f"  {field}: {', '.join(errors)}")
except UnifiedCommerceError as e:
    print(f"API error: {e.message}")
except Exception as e:
    print(f"Unexpected error: {e}")

Retry Decorator

from unified_commerce.decorators import retry

@retry(max_attempts=5, backoff=2)
def fetch_products():
    return client.products.list(limit=100)

# With custom retry logic
@retry(
    max_attempts=3,
    exceptions=(NetworkError, ServerError),
    on_retry=lambda e, attempt: print(f"Retry {attempt}: {e}")
)
async def fetch_orders():
    return await async_client.orders.list(limit=100)

Webhooks

Webhook Verification

from unified_commerce.webhooks import verify_webhook
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/unified-commerce', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Unified-Commerce-Signature')
    payload = request.get_data()

    try:
        event = verify_webhook(
            payload,
            signature,
            os.environ['WEBHOOK_SECRET']
        )

        # Handle the verified event
        if event.type == 'order.created':
            handle_order_created(event.data)
        elif event.type == 'order.updated':
            handle_order_updated(event.data)
        elif event.type == 'inventory.low':
            handle_low_inventory(event.data)

        return jsonify({'received': True})

    except Exception as e:
        return jsonify({'error': str(e)}), 400

def handle_order_created(order):
    print(f"New order created: {order.id}")
    # Process order

Webhook Event Handlers

from unified_commerce.webhooks import WebhookHandler

handler = WebhookHandler(secret=os.environ['WEBHOOK_SECRET'])

@handler.on('order.created')
def handle_order_created(event):
    order = event.data
    print(f"Order created: {order.id}")
    send_confirmation_email(order)

@handler.on('payment.failed')
def handle_payment_failed(event):
    payment = event.data
    print(f"Payment failed: {payment.id}")
    notify_customer(payment)

@handler.on('inventory.low')
async def handle_low_inventory(event):
    inventory = event.data
    print(f"Low inventory alert: {inventory.product_id}")
    await reorder_stock(inventory)

# Process webhook
@app.route('/webhooks/unified-commerce', methods=['POST'])
def webhook():
    try:
        handler.handle(
            request.get_data(),
            request.headers.get('X-Unified-Commerce-Signature')
        )
        return jsonify({'received': True})
    except Exception as e:
        return jsonify({'error': str(e)}), 400

Advanced Features

GraphQL Queries

from unified_commerce.graphql import gql

# Custom GraphQL query
CUSTOM_QUERY = gql("""
    query GetProductsWithReviews($category: String!) {
        products(category: $category) {
            id
            name
            price
            reviews {
                id
                rating
                comment
                author {
                    name
                }
            }
        }
    }
""")

result = client.query(
    CUSTOM_QUERY,
    variables={'category': 'electronics'}
)

products = result['products']

Batch Operations

# Batch create products
products = client.products.batch_create([
    {'name': 'Product 1', 'price': 99.99},
    {'name': 'Product 2', 'price': 149.99},
    {'name': 'Product 3', 'price': 199.99}
])

# Batch update
client.products.batch_update([
    {'id': 'product-1', 'price': 89.99},
    {'id': 'product-2', 'price': 139.99}
])

# Batch delete
client.products.batch_delete(['product-1', 'product-2', 'product-3'])

Pagination

# Offset-based pagination
def fetch_all_products():
    all_products = []
    offset = 0
    limit = 100

    while True:
        products = client.products.list(
            limit=limit,
            offset=offset
        )

        all_products.extend(products.data)

        if len(products.data) < limit:
            break

        offset += limit

    return all_products

# Cursor-based pagination
def fetch_all_products_cursor():
    all_products = []
    cursor = None

    while True:
        response = client.products.list(
            limit=100,
            cursor=cursor
        )

        all_products.extend(response.data)

        if not response.next_cursor:
            break

        cursor = response.next_cursor

    return all_products

# Using iterator
for product in client.products.iterate(category='electronics'):
    print(product.name)

File Uploads

# Upload product image
with open('product-image.jpg', 'rb') as f:
    image = client.products.upload_image(
        'product-123',
        file=f,
        alt='Product image',
        position=0
    )

# Multiple images
images = [
    ('image1.jpg', 'Front view'),
    ('image2.jpg', 'Side view'),
    ('image3.jpg', 'Back view')
]

for filename, alt in images:
    with open(filename, 'rb') as f:
        client.products.upload_image(
            'product-123',
            file=f,
            alt=alt
        )

# Async upload
async with aiofiles.open('product-image.jpg', 'rb') as f:
    content = await f.read()
    image = await async_client.products.upload_image(
        'product-123',
        file=content,
        alt='Product image'
    )

Type Hints & Validation

Pydantic Models

from unified_commerce.models import Product, Order, Customer
from pydantic import BaseModel, validator

class CustomProduct(Product):
    @validator('price')
    def price_must_be_positive(cls, v):
        if v <= 0:
            raise ValueError('Price must be positive')
        return v

# Use custom model
product_data = {
    'name': 'Test Product',
    'price': -10  # Will raise ValidationError
}

try:
    product = CustomProduct(**product_data)
except ValueError as e:
    print(f"Validation error: {e}")

Type Checking

from typing import List, Optional
from unified_commerce import UnifiedCommerce
from unified_commerce.models import Product, Order

def process_products(client: UnifiedCommerce) -> List[Product]:
    products: List[Product] = client.products.list(limit=10)
    return products

def get_customer_orders(
    client: UnifiedCommerce,
    customer_id: str
) -> Optional[List[Order]]:
    try:
        orders: List[Order] = client.orders.list(customer_id=customer_id)
        return orders
    except NotFoundError:
        return None

Testing

Mock Client

from unittest.mock import Mock, patch
from unified_commerce import UnifiedCommerce
from unified_commerce.models import Product

def test_fetch_products():
    # Create mock client
    mock_client = Mock(spec=UnifiedCommerce)
    mock_client.products.list.return_value = [
        Product(id='1', name='Test Product', price=99.99)
    ]

    # Use in test
    products = mock_client.products.list(limit=10)
    assert len(products) == 1
    assert products[0].name == 'Test Product'

# Using patch
@patch('unified_commerce.UnifiedCommerce')
def test_with_patch(mock_commerce):
    mock_commerce.return_value.products.list.return_value = [
        Product(id='1', name='Test Product', price=99.99)
    ]

    client = UnifiedCommerce(api_key='test')
    products = client.products.list()

    assert len(products) == 1

Test Utilities

from unified_commerce.testing import (
    mock_product,
    mock_order,
    mock_customer,
    MockUnifiedCommerce
)

# Create mock objects
test_product = mock_product(
    name='Test Product',
    price=99.99
)

test_order = mock_order(
    items=[{'product': test_product, 'quantity': 2}]
)

# Use mock client
mock_client = MockUnifiedCommerce(api_key='test')
mock_client.products.add_mock_response('list', [test_product])

products = mock_client.products.list()
assert products == [test_product]

Logging

Enable Debug Logging

import logging
from unified_commerce import UnifiedCommerce

# Configure logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Enable SDK debug mode
client = UnifiedCommerce(
    api_key=os.environ['UNIFIED_COMMERCE_API_KEY'],
    debug=True
)

# Or set logging level for SDK
logging.getLogger('unified_commerce').setLevel(logging.DEBUG)

Custom Logger

from unified_commerce import UnifiedCommerce
import logging

# Create custom logger
logger = logging.getLogger('my_app.commerce')

client = UnifiedCommerce(
    api_key=os.environ['UNIFIED_COMMERCE_API_KEY'],
    logger=logger
)

Changelog

Version 1.8.3 (Latest)

  • Added Python 3.12 support
  • Improved async performance with httpx 0.25
  • New Django 5.0 integration helpers
  • Fixed memory leak in long-running async operations
  • Added Pydantic v2 support

Version 1.8.0

  • Breaking: Dropped Python 3.7 support
  • New async client with httpx backend
  • Added FastAPI integration utilities
  • Improved type hints with generics
  • Added webhook verification helpers

Version 1.7.0

  • Added batch operations support
  • New GraphQL query builder
  • Improved error messages
  • Added file upload support
  • Performance optimizations

View full changelog →

Resources

Support

Need help? We're here for you:

Was this page helpful?