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
Metadata
Release files for kajabi-py 0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| kajabi_py-0.1.tar.gz | 114.5 kB | Without provenance | Details for kajabi_py-0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| kajabi_py-0.1-py3-none-any.whl | Python 3 | none | any | Details for kajabi_py-0.1-py3-none-any.whl |
Total release size: 136.1 kB
Release files / kajabi_py-0.1.tar.gz
| Download URL | kajabi_py-0.1.tar.gz |
|---|---|
| Size | 114.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09648b145ab1dfa943a407bc00e36e1fca50fe0ad74c0f56ba653c34ab92695f
|
|
BLAKE2b-256 checksum How to use checksums |
420e72087ec88c592573e67111761860803817f6f429472603a144e346540642
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Release files / kajabi_py-0.1-py3-none-any.whl
| Download URL | kajabi_py-0.1-py3-none-any.whl |
|---|---|
| Size | 21.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3c2b4678313fd6bb939922ed51ba64247ad01dea1921d49ef1af30d6adafc41b
|
|
BLAKE2b-256 checksum How to use checksums |
53cee5953731974b905f8552f9d40a28019136adf0514fbd2b76723c9338ac1a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.15
|
Security for kajabi-py 0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.