Skip to content

ZipTax/ziptax-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ziptax Python SDK

Official Python SDK for the Ziptax API - Get accurate sales and use tax rates for any US or Canadian address, with optional TaxCloud order management support.

Python Version License

Features

Core Features (ZipTax API)

  • 🚀 Simple and intuitive API
  • 🔄 Automatic retry logic with exponential backoff
  • ✅ Input validation
  • 🔍 Type hints for better IDE support
  • 📦 Pydantic models for response validation
  • 🔒 Comprehensive error handling
  • ⚡ Support for concurrent operations
  • 🧪 Well-tested with high code coverage

TaxCloud Integration (Optional)

  • 📋 Order Management: Create, retrieve, and update orders
  • 💰 Refund Processing: Full and partial refund support
  • 🔗 Dual API Support: Seamlessly integrate both ZipTax and TaxCloud
  • 🔐 Optional Configuration: TaxCloud features only enabled when credentials provided

Installation

pip install ziptax-sdk

Quick Start

from ziptax import ZipTaxClient

# Initialize the client with your API key
client = ZipTaxClient.api_key("your-api-key-here")

# Get sales tax by address
response = client.request.GetSalesTaxByAddress(
    "200 Spectrum Center Drive, Irvine, CA 92618"
)

print(f"Address: {response.address_detail.normalized_address}")
if response.tax_summaries:
    for summary in response.tax_summaries:
        print(f"{summary.summary_name}: {summary.rate * 100:.2f}%")

# Always close the client when done
client.close()

Usage

Initialize the Client

from ziptax import ZipTaxClient

# Basic initialization
client = ZipTaxClient.api_key("your-api-key-here")

# With custom configuration
client = ZipTaxClient.api_key(
    "your-api-key-here",
    timeout=60,           # Request timeout in seconds
    max_retries=5,        # Maximum retry attempts
    retry_delay=2.0,      # Base delay between retries
)

# Using as a context manager (recommended)
with ZipTaxClient.api_key("your-api-key-here") as client:
    response = client.request.GetSalesTaxByAddress("123 Main St")

Get Sales Tax by Address

response = client.request.GetSalesTaxByAddress(
    address="200 Spectrum Center Drive, Irvine, CA 92618",
    country_code="USA",      # Optional: "USA" or "CAN" (default: "USA")
    historical="202401",     # Optional: Historical date (YYYYMM format)
    format="json",           # Optional: Response format (default: "json")
)

# Access response data
print(response.address_detail.normalized_address)
print(response.address_detail.geo_lat)
print(response.address_detail.geo_lng)

# Response code
print(f"Response: {response.metadata.response.code} - {response.metadata.response.message}")

# Tax summaries with display rates
if response.tax_summaries:
    for summary in response.tax_summaries:
        print(f"{summary.summary_name}: {summary.rate}")
        for display_rate in summary.display_rates:
            print(f"  {display_rate.name}: {display_rate.rate}")

# Base rates by jurisdiction
if response.base_rates:
    for rate in response.base_rates:
        print(f"{rate.jur_name} ({rate.jur_type}): {rate.rate}")

# Sourcing rules
if response.sourcing_rules:
    print(f"Sourcing: {response.sourcing_rules.value}")

Get Sales Tax by Geolocation

response = client.request.GetSalesTaxByGeoLocation(
    lat="33.6489",
    lng="-117.8386",
    country_code="USA",
    format="json",
)

print(response.address_detail.normalized_address)

Get Rates by Postal Code

response = client.request.GetRatesByPostalCode(
    postal_code="92694",
    format="json",
)

# Response includes all tax jurisdictions for the postal code
for result in response.results:
    print(f"{result.geo_city}, {result.geo_state}")
    print(f"Sales Tax: {result.tax_sales * 100:.2f}%")
    print(f"Use Tax: {result.tax_use * 100:.2f}%")

Get Account Metrics

metrics = client.request.GetAccountMetrics()

print(f"Requests: {metrics.request_count:,} / {metrics.request_limit:,}")
print(f"Usage: {metrics.usage_percent:.2f}%")
print(f"Account Active: {metrics.is_active}")
print(f"Message: {metrics.message}")

TaxCloud Order Management

The SDK includes optional support for TaxCloud order management features. To use these features, you need both a ZipTax API key and TaxCloud credentials (Connection ID and API Key).

Initialize Client with TaxCloud Support

from ziptax import ZipTaxClient

# Initialize with TaxCloud credentials
client = ZipTaxClient.api_key(
    api_key="your-ziptax-api-key",
    taxcloud_connection_id="25eb9b97-5acb-492d-b720-c03e79cf715a",
    taxcloud_api_key="your-taxcloud-api-key",
)

# TaxCloud features are now available via client.request

Create an Order

from ziptax.models import (
    CreateOrderRequest,
    TaxCloudAddress,
    CartItemWithTax,
    Tax,
    Currency,
)

# Prepare order request
order_request = CreateOrderRequest(
    order_id="my-order-1",
    customer_id="customer-453",
    transaction_date="2024-01-15T09:30:00Z",
    completed_date="2024-01-15T09:30:00Z",
    origin=TaxCloudAddress(
        line1="323 Washington Ave N",
        city="Minneapolis",
        state="MN",
        zip="55401-2427",
    ),
    destination=TaxCloudAddress(
        line1="323 Washington Ave N",
        city="Minneapolis",
        state="MN",
        zip="55401-2427",
    ),
    line_items=[
        CartItemWithTax(
            index=0,
            item_id="item-1",
            price=10.8,
            quantity=1.5,
            tax=Tax(amount=1.31, rate=0.0813),
        )
    ],
    currency=Currency(currency_code="USD"),
)

# Create the order
order = client.request.CreateOrder(order_request)
print(f"Created order: {order.order_id}")
print(f"Tax amount: ${order.line_items[0].tax.amount}")

Retrieve an Order

# Get an existing order by ID
order = client.request.GetOrder("my-order-1")

print(f"Order ID: {order.order_id}")
print(f"Customer ID: {order.customer_id}")
print(f"Completed Date: {order.completed_date}")
print(f"Total Tax: ${sum(item.tax.amount for item in order.line_items)}")

Update an Order

from ziptax.models import UpdateOrderRequest

# Update the order's completed date
update_request = UpdateOrderRequest(
    completed_date="2024-01-16T10:00:00Z"
)

updated_order = client.request.UpdateOrder("my-order-1", update_request)
print(f"Updated completed date: {updated_order.completed_date}")

Create a Refund

from ziptax.models import (
    RefundTransactionRequest,
    CartItemRefundWithTaxRequest,
)

# Partial refund - specify items and quantities
refund_request = RefundTransactionRequest(
    items=[
        CartItemRefundWithTaxRequest(
            item_id="item-1",
            quantity=1.0,
        )
    ]
)
refunds = client.request.RefundOrder("my-order-1", refund_request)
print(f"Refunded tax: ${refunds[0].items[0].tax.amount}")

# Full refund - omit items parameter
full_refunds = client.request.RefundOrder("my-order-2")
print("Full refund created")

TaxCloud Error Handling

from ziptax import ZipTaxCloudConfigError

try:
    # Attempt to use TaxCloud feature without credentials
    order = client.request.GetOrder("my-order-1")

except ZipTaxCloudConfigError as e:
    # TaxCloud credentials not configured
    print(f"TaxCloud error: {e.message}")
    print("Please provide taxcloud_connection_id and taxcloud_api_key")

except ZipTaxNotFoundError as e:
    # Order not found
    print(f"Order not found: {e.message}")

Configuration

You can configure the client using dict-style access:

client = ZipTaxClient.api_key("your-api-key-here")

# Set configuration options
client.config["format"] = "json"
client.config["timeout"] = 60

# Get configuration options
timeout = client.config["timeout"]

Error Handling

The SDK provides comprehensive error handling with specific exception types:

from ziptax import (
    ZipTaxClient,
    ZipTaxValidationError,
    ZipTaxAuthenticationError,
    ZipTaxRateLimitError,
    ZipTaxServerError,
    ZipTaxError,
)

client = ZipTaxClient.api_key("your-api-key-here")

try:
    response = client.request.GetSalesTaxByAddress("123 Main St")

except ZipTaxValidationError as e:
    # Input validation errors
    print(f"Validation error: {e.message}")

except ZipTaxAuthenticationError as e:
    # Authentication failures (401)
    print(f"Authentication error: {e.message}")

except ZipTaxRateLimitError as e:
    # Rate limit exceeded (429)
    print(f"Rate limit error: {e.message}")
    if e.retry_after:
        print(f"Retry after {e.retry_after} seconds")

except ZipTaxServerError as e:
    # Server errors (5xx)
    print(f"Server error: {e.message}")

except ZipTaxError as e:
    # General Ziptax errors
    print(f"Ziptax error: {e.message}")

Exception Hierarchy

ZipTaxError
├── ZipTaxAPIError
│   ├── ZipTaxAuthenticationError (401)
│   ├── ZipTaxAuthorizationError (403)
│   ├── ZipTaxNotFoundError (404)
│   ├── ZipTaxRateLimitError (429)
│   └── ZipTaxServerError (5xx)
├── ZipTaxValidationError
├── ZipTaxConnectionError
├── ZipTaxTimeoutError
├── ZipTaxRetryError
└── ZipTaxCloudConfigError (TaxCloud credentials not configured)

Async Operations

For concurrent operations, you can use asyncio with the SDK:

import asyncio
from concurrent.futures import ThreadPoolExecutor
from ziptax import ZipTaxClient

async def get_tax_rates_async(client, addresses):
    loop = asyncio.get_event_loop()
    with ThreadPoolExecutor() as executor:
        tasks = [
            loop.run_in_executor(
                executor,
                client.request.GetSalesTaxByAddress,
                address
            )
            for address in addresses
        ]
        return await asyncio.gather(*tasks)

# Usage
client = ZipTaxClient.api_key("your-api-key-here")
addresses = ["123 Main St, CA", "456 Oak Ave, NY"]
responses = asyncio.run(get_tax_rates_async(client, addresses))

See examples/async_usage.py for more examples.

Response Models

All API responses are validated using Pydantic models:

V60Response

class V60Response:
    metadata: V60Metadata                           # Response metadata with code/message
    base_rates: Optional[List[V60BaseRate]]        # Tax rates by jurisdiction
    service: Optional[V60Service]                   # Service taxability (None for some regions)
    shipping: Optional[V60Shipping]                 # Shipping taxability (None for some regions)
    sourcing_rules: Optional[V60SourcingRules]     # Origin/Destination rules
    tax_summaries: Optional[List[V60TaxSummary]]   # Tax summaries with display rates
    address_detail: V60AddressDetail                # Address details

V60Metadata

class V60Metadata:
    version: str                    # API version (e.g., "v60")
    response: V60ResponseInfo       # Response info object

class V60ResponseInfo:
    code: int                       # Response code (100 = success)
    name: str                       # Response code name
    message: str                    # Response message
    definition: str                 # Schema definition URL

V60TaxSummary

class V60TaxSummary:
    rate: float                                    # Summary tax rate
    tax_type: str                                  # Tax type (e.g., "SALES_TAX")
    summary_name: str                              # Summary description
    display_rates: List[V60DisplayRate]           # Display rates breakdown

class V60DisplayRate:
    name: str                                      # Display rate name
    rate: float                                    # Display rate value

V60AccountMetrics

class V60AccountMetrics:
    request_count: int       # Number of API requests made
    request_limit: int       # Maximum allowed API requests
    usage_percent: float     # Percentage of request limit used
    is_active: bool          # Whether the account is currently active
    message: str             # Account status or informational message

Note: Uses extra="allow" to accept any additional fields the API may return.

See the models documentation for complete model definitions.

Development

Setup

# Clone the repository
git clone https://github.com/ziptax/ziptax-python.git
cd ziptax-python

# Install dependencies
pip install -e ".[dev]"

Running Tests

# Run all tests
pytest

# Run with coverage
pytest --cov=src/ziptax --cov-report=html

# Run specific test file
pytest tests/test_client.py

Code Quality

# Format code
black src/ tests/

# Lint code
ruff src/ tests/

# Type checking
mypy src/

Examples

See the examples/ directory for complete examples:

API Reference

ZipTaxClient

Main client for interacting with the Ziptax API.

Methods

  • api_key(api_key, **kwargs) - Create a client instance with an API key
  • close() - Close the HTTP client session

Properties

  • config - Configuration object (dict-like access)
  • request - Functions object for making API requests

Functions

API endpoint functions accessible via client.request.

ZipTax API Methods

  • GetSalesTaxByAddress(address, **kwargs) - Get tax rates by address
  • GetSalesTaxByGeoLocation(lat, lng, **kwargs) - Get tax rates by coordinates
  • GetRatesByPostalCode(postal_code, **kwargs) - Get tax rates by US postal code
  • GetAccountMetrics(**kwargs) - Get account usage metrics

TaxCloud API Methods (Optional)

Requires taxcloud_connection_id and taxcloud_api_key in client initialization.

  • CreateOrder(request, **kwargs) - Create an order in TaxCloud
  • GetOrder(order_id) - Retrieve an order by ID
  • UpdateOrder(order_id, request) - Update an order's completed date
  • RefundOrder(order_id, request) - Create a full or partial refund

Requirements

  • Python 3.8+
  • requests >= 2.28.0
  • pydantic >= 2.0.0

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes and write tests
  4. Bump the version using python scripts/bump_version.py patch (or minor/major)
  5. Update CHANGELOG.md with your changes
  6. Commit your changes (git commit -m 'Add some amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Note: All PRs require a version bump. See docs/VERSIONING.md for details on our versioning strategy.

Changelog

See CHANGELOG.md for version history and changes.


Made with ❤️ by the Ziptax Team

About

Ziptax Python SDK

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages