A modern, comprehensive, and fully typed Python library for the Freshdesk API (v2).
- Fully Typed: Built entirely on Pydantic v2 models, providing complete IDE autocomplete, type hints, and payload validation.
- Sync & Async Support: Offers both synchronous (
Freshdesk) and asynchronous (AsyncFreshdesk) clients usinghttpx. - Complete API Coverage: Comprehensive mapping of all Freshdesk API resources (Tickets, Contacts, Companies, Agents, Groups, Settings, Automations, etc.).
- Proactive Rate Limiting: Client-side sliding-window limiter prevents exceeding Freshdesk's per-minute quota. Configurable per plan with self-correction from server headers.
- Structured Logging: Built on Python's
loggingmodule with verbosity levels. One-liner stdout setup or custom callback handler for any destination. - Robust Error Handling: Custom exceptions map directly to specific API failures (Auth, Rate Limits, Validation errors).
Requires Python 3.9+.
pip install .
# With dev/test dependencies
pip install ".[dev]"from freshdesk import Freshdesk
client = Freshdesk(domain="yourcompany", api_key="your-api-key", plan="pro")
tickets = client.tickets.list(per_page=10)
for ticket in tickets:
print(f"Ticket #{ticket.id}: {ticket.subject}")
client.close()import asyncio
from freshdesk import AsyncFreshdesk
async def main():
async with AsyncFreshdesk(domain="yourcompany", api_key="your-api-key", plan="pro") as client:
contacts = await client.contacts.list(per_page=5)
for contact in contacts:
print(f"Contact: {contact.name}")
asyncio.run(main())from freshdesk import Freshdesk
with Freshdesk(domain="yourcompany", api_key="key", plan="pro") as client:
me = client.agents.me()
print(f"Logged in as agent #{me.id}")The library enforces Freshdesk's API rate limits proactively — it tracks calls in a sliding 60-second window and throttles before exceeding the quota, so you never accidentally burn your limit.
from freshdesk import Freshdesk, FreshdeskPlan
# Plan presets (sets calls/min automatically)
client = Freshdesk(domain="x", api_key="k") # Growth (200/min, default)
client = Freshdesk(domain="x", api_key="k", plan="pro") # Pro (400/min)
client = Freshdesk(domain="x", api_key="k", plan=FreshdeskPlan.ENTERPRISE) # Enterprise (700/min)
# Custom limit (e.g. purchased additional quota)
client = Freshdesk(domain="x", api_key="k", calls_per_minute=1000)
# Disable rate limiting entirely
client = Freshdesk(domain="x", api_key="k", calls_per_minute=0)| Plan | Rate Limit |
|---|---|
| Trial | 50/min |
| Growth | 200/min |
| Pro | 400/min |
| Enterprise | 700/min |
The limiter also reads X-RateLimit-Remaining from every response to self-correct when other clients or apps share the same account quota.
The library logs under the "freshdesk" logger using Python's standard logging module. Silent by default.
from freshdesk import enable_logging
enable_logging("debug") # or "info", "warning", "error"from freshdesk import Freshdesk, CallbackHandler
def my_sink(record):
send_to_my_system(level=record.levelname, msg=record.getMessage())
client = Freshdesk(
domain="x", api_key="k",
log_handler=CallbackHandler(my_sink),
)import logging
logging.getLogger("freshdesk").setLevel(logging.DEBUG)
logging.getLogger("freshdesk").addHandler(my_handler)| Level | What gets logged |
|---|---|
| DEBUG | Every request/response with method, path, status code, latency (ms). Rate limiter window usage. |
| INFO | Client initialization (domain, plan, rate limit). Rate limiter throttling with wait duration. |
| WARNING | 429 rate limit hits with retry details. Server-side quota corrections. |
| ERROR | Auth failures (401/403). API errors with status and payload. Retries exhausted. |
All resources follow the client.<resource>.<action>() pattern:
tickets— CRUD, filter, restore, forwardcontacts— CRUD, filter, search, merge, make_agent, send_invitecompanies— CRUD, filter, searchagents— CRUD, me, searchgroups— CRUDconversations— reply, note, update, delete, reply_to_forwardroles— view, listskills— CRUDtime_entries— CRUD, toggle_timercanned_responses— CRUD for responses and folderscustom_objects— schemas, records CRUD, filterdiscussions— categories, forums, topics, comments (full CRUD)solutions— categories, folders, articles (full CRUD), searchsurveys— list surveys, satisfaction ratingsautomations— scenario automations, SLA policies, automation rulesfields— ticket, contact, company fieldsforms_messages— ticket forms, outbound messagesadmin— account, business hours, email configs/mailboxes, productsminor_resources— jobs, helpdesk settings, collaboration threads/messagesmissing_resources— agent availability, admin groups, omnichannel, FSM
from freshdesk.exceptions import FreshdeskAPIError, FreshdeskRateLimitError, FreshdeskAuthError
try:
client.tickets.view(999999)
except FreshdeskAuthError:
print("Invalid credentials or access denied")
except FreshdeskRateLimitError as e:
print(f"Rate limited. Retry in {e.retry_after}s")
except FreshdeskAPIError as e:
print(f"API error {e.status_code}: {e.error_payload}")Tests live in tests/ and use pytest. Unit tests (mocked) run without credentials. Integration tests require a real Freshdesk instance.
# Run all tests
pytest
# Run only unit tests (no credentials needed)
pytest tests/test_client.py tests/test_exceptions.py tests/test_models.py tests/test_rate_limiter.py tests/test_logging.pySet credentials via environment variables:
export FRESHDESK_DOMAIN=your-subdomain
export FRESHDESK_API_KEY=your-api-keyOr create tests/test_config.json (gitignored):
{
"domain": "your-subdomain",
"api_key": "your-api-key",
"plan": "pro"
}See tests/test_config.example.json for reference.