Official Python SDK for the ChangeCrab API
Project description
ChangeCrab Python SDK
The official Python client for the ChangeCrab API. Manage your changelogs and release notes programmatically.
For complete API documentation, see the ChangeCrab API Overview.
Installation
pip install changecrab
Or install from source:
git clone https://github.com/changecrab/python_sdk.git
cd python_sdk
pip install .
Quick Start
from changecrab import ChangeCrab
# Initialize the client with your API key
client = ChangeCrab(api_key="your_api_key_here")
# List all your changelogs
changelogs = client.list_changelogs()
for changelog in changelogs:
print(f"{changelog.name} ({changelog.access_id})")
# Create a new post
post = client.create_post(
changelog_id="abc123def4",
summary="🚀 New Feature: Dark Mode",
markdown="We've added dark mode support! Toggle it in your settings.",
team=1,
public=True
)
print(f"Created post #{post.id}")
Authentication
Get your API key from your ChangeCrab account settings. All API requests require authentication.
For detailed authentication information, see the ChangeCrab API Overview.
from changecrab import ChangeCrab
client = ChangeCrab(api_key="your_api_key_here")
You can also configure the client with custom settings:
client = ChangeCrab(
api_key="your_api_key_here",
base_url="https://changecrab.com/api", # Custom API URL (optional)
timeout=60, # Request timeout in seconds (default: 30)
max_retries=3, # Max retry attempts for transient errors (default: 3)
retry_delay=1.0, # Initial retry delay in seconds (default: 1.0, exponential backoff)
)
Retry Logic
The SDK automatically retries requests on transient errors (timeouts, connection errors) with exponential backoff:
- Default: 3 retries with delays of 1s, 2s, 4s
- Only retries on
TimeoutandConnectionErrorexceptions - Non-retryable errors (authentication, validation, etc.) are raised immediately
Usage Guide
Changelogs
List Changelogs
Retrieve all changelogs accessible to your account:
# Get all changelogs
changelogs = client.list_changelogs()
# Filter by team ID
team_changelogs = client.list_changelogs(team_id=123)
Get a Changelog
Retrieve a specific changelog by its access ID:
changelog = client.get_changelog("abc123def4")
print(f"Name: {changelog.name}")
print(f"Subdomain: {changelog.subdomain}")
print(f"Created: {changelog.created_at}")
Create a Changelog
changelog = client.create_changelog(
name="My Product Changelog",
team=1,
subdomain="myproduct",
accent="#007bff",
auto_notify=True,
show_social=True
)
print(f"Created: {changelog.access_id}")
All available options:
| Parameter | Type | Description |
|---|---|---|
name |
str | Required. Name of the changelog |
team |
int | Required. Team ID that owns the changelog |
subdomain |
str | Subdomain (e.g., "myapp" for myapp.changecrab.com) |
domain |
str | Custom domain |
accent |
str | Accent color in hex format (e.g., "#E33597") |
private |
bool | Whether the changelog is private |
auto_notify |
bool | Auto-notify subscribers of new posts |
hide_subscriber |
bool | Hide subscriber count |
show_brand |
bool | Show ChangeCrab branding |
subscribe_active |
bool | Enable subscriptions |
return_url |
str | Return URL for your site |
slack_url |
str | Slack webhook URL |
teams_url |
str | Microsoft Teams webhook URL |
discord_webhook |
str | Discord webhook URL |
site_url |
str | Your main website URL |
twitter |
str | Twitter handle |
ga_tracking |
str | Google Analytics tracking ID |
logo |
str | Logo URL |
favicon |
str | Favicon URL |
extra_css |
str | Custom CSS |
show_creator |
bool | Show post creator names |
show_filters |
bool | Show category filters |
suggestion |
bool | Enable suggestions |
guest_voting |
bool | Allow guest voting |
guest_commenting |
bool | Allow guest comments |
guest_creation |
bool | Allow guest suggestions |
downvotes |
bool | Enable downvoting |
Update a Changelog
changelog = client.update_changelog(
"abc123def4",
name="Updated Changelog Name",
accent="#ff5722",
auto_notify=False
)
Delete a Changelog
client.delete_changelog("abc123def4")
Categories
Categories help organize posts within a changelog. Retrieve them to get category IDs for tagging posts.
categories = client.list_categories("abc123def4")
for cat in categories:
print(f"{cat.title} (ID: {cat.id}, Color: {cat.colour})")
Posts
List Posts
posts = client.list_posts("abc123def4")
for post in posts:
status = "Draft" if post.draft else "Published"
print(f"[{status}] {post.summary}")
Create a Post
post = client.create_post(
changelog_id="abc123def4",
summary="Bug Fix: Login Issue Resolved",
markdown="""
## What's Fixed
We've resolved an issue where some users couldn't log in using social authentication.
### Details
- Fixed OAuth callback handling
- Improved error messages for failed logins
- Added retry logic for transient failures
Thanks to everyone who reported this issue!
""",
team=1,
public=True,
announced=True, # Notify subscribers
categories=[1, 2] # Category IDs from list_categories()
)
All available options:
| Parameter | Type | Description |
|---|---|---|
changelog_id |
str | Required. Changelog access ID |
summary |
str | Required. Title/summary (max 2555 chars) |
markdown |
str | Required. Content in Markdown format |
team |
int | Required. Team ID |
public |
bool | Public visibility (default: True) |
announced |
bool | Notify subscribers (default: False) |
draft |
bool | Save as draft (default: False) |
link |
str | Related URL |
record |
str | Record identifier (single char) |
categories |
list[int] | Category IDs to tag the post |
Update a Post
post = client.update_post(
changelog_id="abc123def4",
post_id=123,
markdown="Updated content here with more details.",
team=1,
summary="Updated Title",
announced=True, # Announce to subscribers
publish_date="2024-01-15",
categories=[1, 3]
)
Delete a Post
client.delete_post("abc123def4", post_id=123)
Error Handling
The SDK provides specific exception classes for different error scenarios. All exceptions include status codes, request IDs (if available), and truncated response bodies for debugging.
Basic Error Handling
from changecrab import (
ChangeCrab,
ChangeCrabError,
ApiError, # Alias for ChangeCrabError
AuthenticationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
)
client = ChangeCrab(api_key="your_api_key")
try:
post = client.create_post(
changelog_id="abc123def4",
summary="", # Empty summary will fail validation
markdown="Content here",
team=1
)
except ValidationError as e:
# Handle validation errors with field-level details
print(f"Validation failed: {e.message}")
print(f"Status: {e.status_code}")
if e.request_id:
print(f"Request ID: {e.request_id}")
# Access field-level validation errors
for field, messages in e.errors.items():
print(f" {field}: {', '.join(messages)}")
# Response body is automatically truncated and sanitized
# (no API keys or sensitive data leaked)
if e.response_data:
print(f"Response: {e.response_data}")
except AuthenticationError as e:
# Invalid or missing API key
print(f"Auth failed: {e.message} (status: {e.status_code})")
if e.request_id:
print(f"Request ID: {e.request_id}")
except NotFoundError as e:
# Resource doesn't exist
print(f"Not found: {e.message}")
except RateLimitError as e:
# Rate limit exceeded - SDK automatically retries with backoff
# This exception is only raised if all retries are exhausted
print(f"Rate limited: {e.message}")
print("The SDK automatically retried with exponential backoff")
except ServerError as e:
# Server error - SDK automatically retries transient 5xx errors
print(f"Server error: {e.message} (status: {e.status_code})")
except (ChangeCrabError, ApiError) as e:
# Catch-all for any SDK error (ApiError is an alias)
print(f"Error: {e.message}")
print(f"Status: {e.status_code}")
if e.request_id:
print(f"Request ID: {e.request_id}")
Exception Properties
All exceptions provide the following properties:
message: Human-readable error messagestatus_code: HTTP status code (if available)request_id: Request ID from API response headers (if available)response_data: Sanitized and truncated response body (max 500 chars, API keys redacted)errors: Field-level validation errors (ValidationError only)
Automatic Retries
The SDK automatically retries on:
- Network errors: Timeouts and connection errors (exponential backoff)
- HTTP 429 (Rate Limit): Respects
Retry-Afterheader if present - HTTP 5xx (Server Errors): 500, 502, 503, 504 (exponential backoff)
Retries are capped at max_retries (default: 3). Exceptions are only raised after all retries are exhausted.
Exception Hierarchy
ChangeCrabError (base) / ApiError (alias)
├── AuthenticationError # 401, 403
├── NotFoundError # 404
├── ValidationError # 422 (includes field errors)
├── RateLimitError # 429 (after retries exhausted)
└── ServerError # 5xx (after retries exhausted)
Data Models
The SDK returns typed dataclass objects for easy access to resource properties:
Changelog
changelog = client.get_changelog("abc123def4")
# Access properties directly
print(changelog.id) # Numeric ID
print(changelog.access_id) # String ID used in API URLs
print(changelog.name) # Display name
print(changelog.team) # Team ID
print(changelog.subdomain) # Subdomain
print(changelog.accent) # Accent color
print(changelog.created_at) # datetime object
# Convert to dictionary
data = changelog.to_dict()
Post
post = client.list_posts("abc123def4")[0]
print(post.id) # Numeric ID
print(post.summary) # Title
print(post.markdown) # Content
print(post.public) # bool
print(post.announced) # bool
print(post.draft) # bool
print(post.categories) # List of PostCategory objects
print(post.created_at) # datetime object
Category
category = client.list_categories("abc123def4")[0]
print(category.id) # Numeric ID
print(category.title) # Display name
print(category.colour) # Hex color code
Environment Variables
You can set your API key as an environment variable:
export CHANGECRAB_API_KEY="your_api_key_here"
Then initialize the client:
import os
from changecrab import ChangeCrab
client = ChangeCrab(api_key=os.environ["CHANGECRAB_API_KEY"])
Type Hints
This SDK includes full type annotations and a py.typed marker for static type checkers like mypy:
from typing import Optional
from changecrab import ChangeCrab, Changelog, Post
def get_latest_post(client: ChangeCrab, changelog_id: str) -> Optional[Post]:
posts = client.list_posts(changelog_id)
return posts[0] if posts else None
Testing
The SDK includes a comprehensive test suite. To run tests:
# Install dev dependencies
pip install -e ".[dev]"
# Run all tests
pytest
# Run with coverage
pytest --cov=changecrab --cov-report=term
# Run specific test file
pytest tests/test_client.py
The test suite includes:
- Unit tests for all client methods (with mocked API responses)
- Model tests for data parsing and conversion
- Exception handling tests
- Error scenario tests
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/your-feature) - Install dev dependencies (
pip install -e ".[dev]") - Make your changes
- Run tests (
pytest) - Run linting (
ruff check .) - Run type checking (
mypy changecrab) - Commit your changes
- Push to the branch
- Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- API Documentation: ChangeCrab API Overview
- Issues: GitHub Issues
- Email: support@changecrab.com
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 changecrab-0.1.1.tar.gz.
File metadata
- Download URL: changecrab-0.1.1.tar.gz
- Upload date:
- Size: 21.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1235a1344e6452b1735b489ebc6dc2548e6c8c5f4e615729cbd7fbf510bc87cd
|
|
| MD5 |
c26d1b62878d57f48355124d6211ce7c
|
|
| BLAKE2b-256 |
bb39d137b74794d2b9253285c00d9165a18d9ad77c001fa8c1738c7aa0bbff6f
|
Provenance
The following attestation bundles were made for changecrab-0.1.1.tar.gz:
Publisher:
publish.yml on changecrab/python_sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
changecrab-0.1.1.tar.gz -
Subject digest:
1235a1344e6452b1735b489ebc6dc2548e6c8c5f4e615729cbd7fbf510bc87cd - Sigstore transparency entry: 798357135
- Sigstore integration time:
-
Permalink:
changecrab/python_sdk@7ddb1689c4d6acdcf0156da766067e19c92d1f72 -
Branch / Tag:
refs/tags/newversion - Owner: https://github.com/changecrab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ddb1689c4d6acdcf0156da766067e19c92d1f72 -
Trigger Event:
release
-
Statement type:
File details
Details for the file changecrab-0.1.1-py3-none-any.whl.
File metadata
- Download URL: changecrab-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.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 |
ce264c4e23e01bb8a6c8efa42158d62788dd1e98b89ea2b11a2d5e50fdb69e31
|
|
| MD5 |
45371eb849a7f90965b9aeb5b13b2ee4
|
|
| BLAKE2b-256 |
b813fad186debbd792804e5a5c5a985e848c815f95a8a4a06a7eabec9825ea5e
|
Provenance
The following attestation bundles were made for changecrab-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on changecrab/python_sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
changecrab-0.1.1-py3-none-any.whl -
Subject digest:
ce264c4e23e01bb8a6c8efa42158d62788dd1e98b89ea2b11a2d5e50fdb69e31 - Sigstore transparency entry: 798357140
- Sigstore integration time:
-
Permalink:
changecrab/python_sdk@7ddb1689c4d6acdcf0156da766067e19c92d1f72 -
Branch / Tag:
refs/tags/newversion - Owner: https://github.com/changecrab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ddb1689c4d6acdcf0156da766067e19c92d1f72 -
Trigger Event:
release
-
Statement type: