Python client for the Kajabi API
Project description
kajabi-py
Python client for the Kajabi API. Provides both sync and async interfaces with full type hints, Pydantic models, and automatic OAuth2 token management.
Installation
pip install kajabi-py
Requires Python 3.11+.
Quick start
from kajabi import Client
client = Client(client_id="your_id", client_secret="your_secret")
# List resources — returns a lazy collection, no request until accessed
contacts = client.contacts.list()
print(contacts.count) # total across all pages (triggers first page fetch)
print(contacts[0].name) # indexed access (fetches page if not cached)
for contact in contacts: # iterates all pages lazily
print(contact.name, contact.email)
# Get a single resource
post = client.blog_posts.get("42")
# Create, update, delete
contact = client.contacts.create(
name="Jane Doe",
email="jane@example.com",
site_id="your_site_id",
)
contact = contact.update(name="Jane Smith")
contact.delete()
Async
from kajabi import AsyncClient
async with AsyncClient(client_id="your_id", client_secret="your_secret") as client:
contacts = client.contacts.list() # no await — returns collection immediately
count = await contacts.acount() # total across all pages
first = await contacts.aget(0) # indexed access
async for contact in contacts: # iterates all pages lazily
print(contact.name)
contact = await client.contacts.get("42")
contact = await contact.aupdate(name="Updated Name")
await contact.adelete()
Authentication
Pass client_id/client_secret or username/password. Authentication is lazy -- the first API call triggers the token exchange. Tokens are refreshed automatically.
# OAuth2 client credentials
client = Client(client_id="...", client_secret="...")
# Resource owner password
client = Client(username="user@example.com", password="...")
Resources
All resources are Pydantic models returned from manager methods on the client. Read-only resources support get() and list(). Writable resources also support create(), update()/aupdate(), and delete()/adelete().
| Manager | Model | Writable |
|---|---|---|
blog_posts |
BlogPost |
No |
contacts |
Contact |
Yes |
contact_notes |
ContactNote |
Yes |
contact_tags |
ContactTag |
No |
courses |
Course |
No |
custom_fields |
CustomField |
No |
customers |
Customer |
No |
forms |
Form |
No |
form_submissions |
FormSubmission |
No |
landing_pages |
LandingPage |
No |
offers |
Offer |
No |
orders |
Order |
No |
order_items |
OrderItem |
No |
payouts |
Payout |
No |
podcasts |
Podcast |
No |
products |
Product |
No |
purchases |
Purchase |
No |
sites |
Site |
No |
site_pages |
SitePage |
No |
transactions |
Transaction |
No |
webhooks |
Webhook |
Yes (create/delete) |
Filtering, sorting, and sparse fields
contacts = client.contacts.list(
filters={"site_id": "123"},
sort="-created_at",
fields=["name", "email"],
)
The default page size is 20. Pass page_size when constructing the client to change it globally:
client = Client(client_id="...", client_secret="...", page_size=50)
Lazy-loading related resources
Relationship ID fields (e.g., site_id, product_ids) have corresponding properties that lazily load the full resource on first access. Results are cached for the lifetime of the instance.
customer = client.customers.get("123")
# Single relation: customer.site_id -> customer.site
site = customer.site # fetches Site on first access, cached thereafter
# Plural relation: customer.product_ids -> customer.products
products = customer.products # fetches each Product, cached thereafter
Returns None for single relations when the ID is None, and an empty list for plural relations with no IDs. Accessing a relation descriptor on an async client raises KajabiError -- use explicit async calls instead.
Relationships
Some resources expose relationship operations:
# Contact tags
contact.list_tags()
contact.add_tags(["tag-id-1", "tag-id-2"])
contact.remove_tags(["tag-id-1"])
# Contact/Customer offers
contact.grant_offers(["offer-id-1"])
contact.revoke_offers(["offer-id-1"])
# Form submissions
form.submit(email="user@example.com", name="User")
# Purchase actions
purchase.reactivate()
purchase.deactivate()
purchase.cancel_subscription()
Convenience endpoints
me = client.me() # current user profile
ver = client.version() # API version info
Error handling
All errors inherit from KajabiError. HTTP errors are mapped to specific exception types:
| Status | Exception |
|---|---|
| 400 | BadRequestError |
| 401 | AuthenticationError |
| 403 | AuthorizationError |
| 404 | NotFoundError |
| 405 | MethodNotAllowed |
| 422 | ValidationError |
| 429 | RateLimitError |
| 5xx | ServerError |
from kajabi import Client, NotFoundError
try:
client.contacts.get("nonexistent")
except NotFoundError as e:
print(e)
Development
uv sync
uv run pytest
uv run ruff check .
uv run ty check
Pre-commit hooks are managed with prek:
uv run prek install
uv run prek run --all-files
License
Apache 2.0
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 kajabi_py-0.1.tar.gz.
File metadata
- Download URL: kajabi_py-0.1.tar.gz
- Upload date:
- Size: 114.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09648b145ab1dfa943a407bc00e36e1fca50fe0ad74c0f56ba653c34ab92695f
|
|
| MD5 |
540ef437c484d34e3a9932e8bb3829c5
|
|
| BLAKE2b-256 |
420e72087ec88c592573e67111761860803817f6f429472603a144e346540642
|
File details
Details for the file kajabi_py-0.1-py3-none-any.whl.
File metadata
- Download URL: kajabi_py-0.1-py3-none-any.whl
- Upload date:
- Size: 21.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c2b4678313fd6bb939922ed51ba64247ad01dea1921d49ef1af30d6adafc41b
|
|
| MD5 |
ad9351c2698b2b9884fec837168f63ee
|
|
| BLAKE2b-256 |
53cee5953731974b905f8552f9d40a28019136adf0514fbd2b76723c9338ac1a
|