A modern Python client for the Kanka API with full typing support
Project description
python-kanka
A modern Python client for the Kanka API, the collaborative worldbuilding and campaign management platform.
Originally inspired by/forked from Kathrin Weihe's python-kanka. Thank you to Kathrin for the foundation and inspiration.
Features
- Entity Support: Support for 12 core Kanka entity types:
- Characters, Locations, Organisations, Families
- Calendars, Events, Quests, Journals
- Notes, Tags, Races, Creatures
- Type Safety: Built with Pydantic v2 for data validation and type hints
- Python 3.9+: Full typing support for modern Python versions
- Pythonic API: Consistent interface patterns across all entity types
- Error Handling: Specific exception types for different API errors
- Rate Limit Handling: Automatic retry with exponential backoff
- Entity Posts: Support for entity posts/comments management
- Filtering and Search: Filter entities by various criteria and search across types
- Pagination: Built-in pagination support for large result sets
Installation
Install from PyPI:
pip install python-kanka
Or install from source:
git clone https://github.com/ervwalter/python-kanka.git
cd python-kanka
pip install -e .
Quick Start
from kanka import KankaClient
# Initialize the client
client = KankaClient(
token="your-api-token", # Get from https://app.kanka.io/settings/api
campaign_id=12345 # Your campaign ID
)
# Create a character
gandalf = client.characters.create(
name="Gandalf the Grey",
title="Wizard",
type="Istari",
age="2000+ years",
is_private=False
)
# Update the character
gandalf = client.characters.update(
gandalf,
name="Gandalf the White"
)
# Search across all entities
results = client.search("Dragon")
for result in results:
print(f"{result.name} ({result.type})")
# List characters with filters
wizards = client.characters.list(
type="Wizard",
is_private=False
)
# Delete when done
client.characters.delete(gandalf)
Common Use Cases
Working with Entity Posts
# Get a character
character = client.characters.get(character_id)
# Create a new post/note (pass the entity object, not just ID)
post = client.characters.create_post(
character, # Pass the full entity object
name="Background",
entry="*Born in the ancient times...*",
is_private=False
)
# List all posts for an entity
posts = client.characters.list_posts(character)
for post in posts:
print(f"{post.name}: {post.entry[:50]}...")
# Update a post (name field is required even if not changing)
update = client.characters.update_post(
character,
post.id,
name=post.name, # Required by API
entry="Updated content..."
)
Advanced Filtering
# Filter by multiple criteria
results = client.characters.list(
name="Gandalf", # Partial name match
type="Wizard", # Exact type match
is_private=False, # Only public entities
tags=[15, 23], # Has specific tags
page=1, # Pagination
limit=30 # Results per page
)
# Access the generic entities endpoint
entities = client.entities(
types=["character", "location"], # Multiple entity types
name="Dragon", # Name filter
tags=[15, 23], # Tag filter
is_private=False
)
Working with Multiple Entity Types
All entity types follow the same pattern:
# Locations
rivendell = client.locations.create(
name="Rivendell",
type="City",
parent_location_id=middle_earth.id
)
# Organizations
council = client.organisations.create(
name="The White Council",
type="Council"
)
# Journals
journal = client.journals.create(
name="Campaign Log",
date="3019-03-25"
)
# Notes (for DM/private notes)
note = client.notes.create(
name="DM Notes",
entry="Remember: Gandalf knows about the ring",
is_private=True
)
# Tags
tag = client.tags.create(
name="Important NPC",
colour="#ff0000"
)
Error Handling
from kanka.exceptions import (
NotFoundError,
ValidationError,
RateLimitError,
AuthenticationError
)
try:
character = client.characters.get(99999)
except NotFoundError:
print("Character not found")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after} seconds")
except ValidationError as e:
print(f"Invalid data: {e.errors}")
except AuthenticationError:
print("Invalid API token")
Rate Limiting
The client automatically handles API rate limits by retrying requests with exponential backoff:
# Default behavior - automatic retry on rate limits
client = KankaClient(token, campaign_id)
# Disable automatic retry
client = KankaClient(
token,
campaign_id,
enable_rate_limit_retry=False
)
# Customize retry behavior
client = KankaClient(
token,
campaign_id,
max_retries=5, # Try up to 5 times (default: 3)
retry_delay=2.0, # Initial delay in seconds (default: 1.0)
max_retry_delay=120.0 # Maximum delay between retries (default: 60.0)
)
The client parses rate limit headers from the API to determine retry delays and respects the API's rate limits.
Migration Guide
Upgrading from v0.x to v2.0
The v2.0 release introduces a new API design with Pydantic models and type safety. Here's how to migrate:
Old API (v0.x)
# Old way - procedural API
import kanka
client = kanka.KankaClient(token)
campaign = client.campaign(campaign_id)
char = campaign.character(char_id)
char.name = "New Name"
char.update()
New API (v2.0+)
# New way - object-oriented with managers
from kanka import KankaClient
client = KankaClient(token, campaign_id)
char = client.characters.get(char_id)
char = client.characters.update(char, name="New Name")
Key Differences
- Single Client: No more separate campaign object - everything through
KankaClient - Entity Managers: Each entity type has a dedicated manager (
client.characters,client.locations, etc.) - Immutable Models: Models are Pydantic objects - use manager methods to update
- Better Types: Full typing support with IDE autocomplete
- Consistent API: All entities follow the same CRUD pattern
Development Setup
For development, install additional dependencies:
# Clone the repository
git clone https://github.com/ervwalter/python-kanka.git
cd python-kanka
# Install dev dependencies
pip install -r dev-requirements.txt
pip install -e . # Install in editable mode
Development Tools
This project uses several tools to maintain code quality:
- black - Code formatter
- isort - Import sorter
- ruff - Fast Python linter
- pytest - Testing framework
- mypy - Static type checker
Use the Makefile for common development tasks:
make install # Install all dependencies
make format # Format code with black and isort
make lint # Run linting checks
make test # Run all tests
make coverage # Run tests with coverage report
make check # Run all checks (lint + test)
make clean # Clean up temporary files
Pre-commit Hooks (Optional)
To automatically run formatting and linting before each commit:
pre-commit install
API Documentation
See the API Reference for detailed documentation of all classes and methods.
Examples
Check out the examples/ directory for more detailed examples:
quickstart.py- Basic usage tutorialcrud_operations.py- Full CRUD examples for all entity typesfiltering.py- Advanced filtering and searchposts.py- Working with entity postserror_handling.py- Proper error handling patternsmigration.py- Migrating from the old API
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Links
- Kanka.io - The Kanka platform
- Kanka API Documentation - Official API docs
- GitHub Repository - Source code
- Issue Tracker - Report bugs or request features
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_kanka-2.1.0.tar.gz.
File metadata
- Download URL: python_kanka-2.1.0.tar.gz
- Upload date:
- Size: 96.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e1f32d32768de54c7ae08aafe6668c5651b148da0bf6f7f15da5d87e417f787c
|
|
| MD5 |
16904a366369151a9295d040d8cab09b
|
|
| BLAKE2b-256 |
d747151f5184b112d6f75afa1039b7d43c48dd54fe4b2ac7626656df31d7ded4
|
Provenance
The following attestation bundles were made for python_kanka-2.1.0.tar.gz:
Publisher:
publish.yml on ervwalter/python-kanka
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_kanka-2.1.0.tar.gz -
Subject digest:
e1f32d32768de54c7ae08aafe6668c5651b148da0bf6f7f15da5d87e417f787c - Sigstore transparency entry: 236540808
- Sigstore integration time:
-
Permalink:
ervwalter/python-kanka@f41b9bccdada453a6dddd8f26de30bd9d8a2b61d -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/ervwalter
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f41b9bccdada453a6dddd8f26de30bd9d8a2b61d -
Trigger Event:
release
-
Statement type:
File details
Details for the file python_kanka-2.1.0-py3-none-any.whl.
File metadata
- Download URL: python_kanka-2.1.0-py3-none-any.whl
- Upload date:
- Size: 24.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bba969cfbdc252c342e17b90d2e5b068a1c82a83ad5518d5b3e7594ae4d60ae
|
|
| MD5 |
b052510cb92ce85d994e7763cdf707b5
|
|
| BLAKE2b-256 |
1b7534a127f89cc74996a4be528597ccfbf7e32c395683f020475e2aecb4931b
|
Provenance
The following attestation bundles were made for python_kanka-2.1.0-py3-none-any.whl:
Publisher:
publish.yml on ervwalter/python-kanka
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_kanka-2.1.0-py3-none-any.whl -
Subject digest:
7bba969cfbdc252c342e17b90d2e5b068a1c82a83ad5518d5b3e7594ae4d60ae - Sigstore transparency entry: 236540810
- Sigstore integration time:
-
Permalink:
ervwalter/python-kanka@f41b9bccdada453a6dddd8f26de30bd9d8a2b61d -
Branch / Tag:
refs/tags/v2.1.0 - Owner: https://github.com/ervwalter
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f41b9bccdada453a6dddd8f26de30bd9d8a2b61d -
Trigger Event:
release
-
Statement type: