Modern Python SDK for Zendesk API
Project description
Python Zendesk SDK
Modern Python SDK for Zendesk API with async support, full type safety, and comprehensive error handling.
Features
- Async HTTP Client: Built on httpx with retry logic, rate limiting, and exponential backoff
- Type Safety: Full Pydantic v2 models for Users, Organizations, Tickets, Comments, and Help Center
- Namespace Pattern: Clean API organization (
client.users,client.tickets,client.help_center) - Help Center: Full CRUD for Categories, Sections, and Articles
- Pagination: Both offset-based and cursor-based pagination support
- Search: Zendesk search API support
- Configuration: Flexible configuration with environment variable support
Installation
pip install python-zendesk-sdk
Quick Start
import asyncio
from zendesk_sdk import ZendeskClient, ZendeskConfig
async def main():
config = ZendeskConfig(
subdomain="your-subdomain",
email="your-email@example.com",
token="your-api-token",
)
async with ZendeskClient(config) as client:
# Get users with pagination
paginator = await client.users.list(per_page=10)
users = await paginator.get_page()
for user in users:
print(f"User: {user['name']} ({user['email']})")
# Get specific ticket
ticket = await client.tickets.get(12345)
print(f"Ticket: {ticket.subject}")
# Search tickets
results = await client.search.tickets("status:open priority:high")
for ticket in results:
print(f"High priority: {ticket.subject}")
asyncio.run(main())
Configuration
Direct instantiation
config = ZendeskConfig(
subdomain="mycompany",
email="user@example.com",
token="api_token_here"
)
Environment variables
export ZENDESK_SUBDOMAIN=mycompany
export ZENDESK_EMAIL=user@example.com
export ZENDESK_TOKEN=api_token_here
config = ZendeskConfig() # Will load from environment
API Methods
Users
user = await client.users.get(user_id) # Get user by ID
paginator = await client.users.list() # List users with pagination
user = await client.users.by_email(email) # Get user by email
users = await client.users.search(query) # Search users
users = await client.users.get_many([id1, id2]) # Get multiple users
Organizations
org = await client.organizations.get(org_id) # Get organization by ID
paginator = await client.organizations.list() # List organizations
orgs = await client.organizations.search(query) # Search organizations
Tickets
ticket = await client.tickets.get(ticket_id) # Get ticket by ID
paginator = await client.tickets.list() # List tickets
tickets = await client.tickets.for_user(user_id) # Get user's tickets
tickets = await client.tickets.for_organization(org_id) # Get org's tickets
tickets = await client.tickets.search(query) # Search tickets
Comments (nested under tickets)
comments = await client.tickets.comments.list(ticket_id)
ticket = await client.tickets.comments.add(ticket_id, body, public=False)
await client.tickets.comments.make_private(ticket_id, comment_id)
comment = await client.tickets.comments.redact(ticket_id, comment_id, text)
Tags (nested under tickets)
tags = await client.tickets.tags.get(ticket_id) # Get tags
tags = await client.tickets.tags.add(ticket_id, ["vip"]) # Add tags
tags = await client.tickets.tags.set(ticket_id, ["new"]) # Replace all tags
tags = await client.tickets.tags.remove(ticket_id, ["old"]) # Remove tags
Enriched Tickets
Load tickets with all related data (comments + users) in minimum API requests:
# Get ticket with all related data
enriched = await client.tickets.get_enriched(12345)
print(f"Ticket: {enriched.ticket.subject}")
print(f"Requester: {enriched.requester.name}")
print(f"Assignee: {enriched.assignee.name if enriched.assignee else 'Unassigned'}")
for comment in enriched.comments:
author = enriched.get_comment_author(comment)
print(f"Comment by {author.name}: {comment.body[:50]}...")
# Search with all data loaded
results = await client.tickets.search_enriched("status:open")
for item in results:
print(f"{item.ticket.subject} - {len(item.comments)} comments")
Attachments
content = await client.attachments.download(content_url) # Download file
token = await client.attachments.upload(data, filename, content_type) # Upload file
# Attach to comment
await client.tickets.comments.add(ticket_id, "See attached", uploads=[token])
Search
paginator = await client.search.all(query) # General search
tickets = await client.search.tickets(query) # Search tickets
users = await client.search.users(query) # Search users
orgs = await client.search.organizations(query) # Search organizations
Help Center
Access Help Center (Guide) via client.help_center namespace:
Categories
cat = await client.help_center.categories.get(category_id)
paginator = await client.help_center.categories.list()
cat = await client.help_center.categories.create(name, description)
cat = await client.help_center.categories.update(category_id, name=new_name)
await client.help_center.categories.delete(category_id, force=True)
Sections
sec = await client.help_center.sections.get(section_id)
paginator = await client.help_center.sections.list()
paginator = await client.help_center.sections.for_category(category_id)
sec = await client.help_center.sections.create(category_id, name, description)
sec = await client.help_center.sections.update(section_id, name=new_name)
await client.help_center.sections.delete(section_id, force=True)
Articles
art = await client.help_center.articles.get(article_id)
paginator = await client.help_center.articles.list()
paginator = await client.help_center.articles.for_section(section_id)
paginator = await client.help_center.articles.for_category(category_id)
results = await client.help_center.articles.search(query)
art = await client.help_center.articles.create(section_id, title, body=html)
art = await client.help_center.articles.update(article_id, title=new_title)
await client.help_center.articles.delete(article_id)
Example
async with ZendeskClient(config) as client:
hc = client.help_center
# Get permission_group_id from existing article (required for article creation)
existing = await (await hc.articles.list(per_page=1)).get_page()
article_details = await hc.articles.get(existing[0]["id"])
permission_group_id = article_details.permission_group_id
# Create category -> section -> article hierarchy
category = await hc.categories.create(
name="Product Documentation",
description="Help articles for our product"
)
section = await hc.sections.create(
category.id,
"Getting Started"
)
article = await hc.articles.create(
section.id,
title="Installation Guide",
body="<h1>Installation</h1><p>Follow these steps...</p>",
permission_group_id=permission_group_id,
draft=True,
label_names=["installation", "guide"],
)
# Search articles (useful for AI assistants)
results = await hc.articles.search("password reset")
for article in results:
print(f"{article.title}")
print(f"Snippet: {article.snippet}") # Matching text with <em> tags
# Cascade delete (removes category + all sections + all articles)
await hc.categories.delete(category.id, force=True)
Note:
delete()for categories and sections requiresforce=Trueas a safety measure since they cascade delete all child content.
Error Handling
The SDK provides specific exception classes for different error types:
from zendesk_sdk.exceptions import (
ZendeskAuthException,
ZendeskHTTPException,
ZendeskRateLimitException,
ZendeskTimeoutException,
ZendeskValidationException,
)
async with ZendeskClient(config) as client:
try:
user = await client.users.get(12345)
except ZendeskAuthException as e:
# 401/403 - Authentication failed
print(f"Auth error: {e.message}")
except ZendeskRateLimitException as e:
# 429 - Rate limit exceeded
print(f"Rate limited, retry after: {e.retry_after}s")
except ZendeskHTTPException as e:
# Other HTTP errors (404, 500, etc.)
print(f"HTTP {e.status_code}: {e.message}")
except ZendeskTimeoutException as e:
# Request timeout
print(f"Timeout: {e.message}")
Automatic Retry
The SDK automatically retries on:
- Rate limiting (429) - with respect to
Retry-Afterheader - Server errors (5xx) - with exponential backoff
- Network errors and timeouts
Configure retry behavior:
config = ZendeskConfig(
subdomain="mycompany",
email="user@example.com",
token="api_token",
timeout=30.0, # Request timeout in seconds
max_retries=3, # Number of retry attempts
)
Examples
See the examples/ directory for complete usage examples:
basic_usage.py- Basic configuration and API operationspagination_example.py- Working with paginated resultserror_handling.py- Error handling patternsenriched_tickets.py- Loading tickets with related datahelp_center.py- Help Center categories, sections, and articles
Requirements
- Python 3.8+
- httpx
- pydantic >=2.0
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file python_zendesk_sdk-0.2.0.tar.gz.
File metadata
- Download URL: python_zendesk_sdk-0.2.0.tar.gz
- Upload date:
- Size: 48.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7d6577beca14a1c8ae885a2e916cb585b4f9fa274b41a5818d926b8d2c751d8
|
|
| MD5 |
fe9bf03829f1de9c5295a1f995d33056
|
|
| BLAKE2b-256 |
ddb9b9e1354fda979529980459da61f80819a0235e961e7eb92ff4a7d3caabef
|
Provenance
The following attestation bundles were made for python_zendesk_sdk-0.2.0.tar.gz:
Publisher:
publish.yml on bormog/python-zendesk-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_zendesk_sdk-0.2.0.tar.gz -
Subject digest:
f7d6577beca14a1c8ae885a2e916cb585b4f9fa274b41a5818d926b8d2c751d8 - Sigstore transparency entry: 789704942
- Sigstore integration time:
-
Permalink:
bormog/python-zendesk-sdk@2edadaa0cad56b5ab602d9e2034679b778009d29 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/bormog
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2edadaa0cad56b5ab602d9e2034679b778009d29 -
Trigger Event:
release
-
Statement type:
File details
Details for the file python_zendesk_sdk-0.2.0-py3-none-any.whl.
File metadata
- Download URL: python_zendesk_sdk-0.2.0-py3-none-any.whl
- Upload date:
- Size: 42.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
299714eeb0c8f3538e4742e50d39dddeb7e7dd9ef9a02044ed7ad2f822d6081c
|
|
| MD5 |
29107fcf9a3a8ea681a0fd153565ff1c
|
|
| BLAKE2b-256 |
bd5694bdba1b1714027a3d4ebb5a12ca509739f57066abe05fb56e5542a24025
|
Provenance
The following attestation bundles were made for python_zendesk_sdk-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on bormog/python-zendesk-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_zendesk_sdk-0.2.0-py3-none-any.whl -
Subject digest:
299714eeb0c8f3538e4742e50d39dddeb7e7dd9ef9a02044ed7ad2f822d6081c - Sigstore transparency entry: 789704944
- Sigstore integration time:
-
Permalink:
bormog/python-zendesk-sdk@2edadaa0cad56b5ab602d9e2034679b778009d29 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/bormog
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2edadaa0cad56b5ab602d9e2034679b778009d29 -
Trigger Event:
release
-
Statement type: