Official Python client for the 3Common Public API.
Project description
threecommon
Official Python client for the 3Common Public API. Sync and async, fully type-checked, Pydantic v2 models.
Install
pip install threecommon
Requires Python ≥ 3.10.
Quick start (sync)
from threecommon import ThreeCommon
from threecommon.events import ListParams, UpdateBody
with ThreeCommon(api_key="3co_...") as client:
# List
result = client.events.list(ListParams(status="open", page_size=50))
# Retrieve
ev = client.events.retrieve("evt_123")
# Update
updated = client.events.update("evt_123", UpdateBody(name="New name"))
# Auto-paginate
for ev in client.events.list_auto_paginate(ListParams(status="open")):
print(ev.name)
Quick start (async)
import asyncio
from threecommon import AsyncThreeCommon
from threecommon.events import ListParams
async def main() -> None:
async with AsyncThreeCommon(api_key="3co_...") as client:
result = await client.events.list(ListParams(status="open"))
async for ev in client.events.list_auto_paginate(ListParams(status="open")):
print(ev.name)
asyncio.run(main())
The API key may also be supplied via the THREECOMMON_API_KEY environment variable.
Configuration
from threecommon import ThreeCommon, RetryDelay
client = ThreeCommon(
api_key="3co_...", # required (or via env var)
base_url="https://api.3common.com", # default
api_version="2026-04-29", # pinned API version
timeout_seconds=30.0, # per-request deadline
max_retries=3, # automatic retries on 408/425/429/5xx
retry_delay=RetryDelay(
initial_seconds=0.5,
max_seconds=8.0,
jitter=True,
),
telemetry=True, # opt-out of anonymous telemetry
)
Error handling
Every error raised by the SDK inherits from threecommon.APIError. Catch the typed subclass you care about:
from threecommon import (
NotFoundError,
AuthError,
RateLimitError,
ConnectionError,
)
try:
client.events.retrieve("evt_missing")
except NotFoundError as e:
# 404 — e.request_id, e.code, e.details
...
except AuthError as e:
# 401 — bad or expired API key
...
except RateLimitError as e:
# 429 — e.retry_after_seconds tells you when to retry
...
except ConnectionError as e:
# network error; original cause via e.__cause__
...
Every error carries code, message, http_status, request_id, details, and raw_response. The default str(e) format includes the request ID for log correlation:
[not_found] Event evt_missing not found (request_id=req-dfx-abc)
Pagination
Two flavors:
# One page at a time
result = client.events.list(ListParams(page_size=50))
# All pages, lazy
for ev in client.events.list_auto_paginate(ListParams(status="open")):
print(ev.name)
# Async
async for ev in async_client.events.list_auto_paginate(ListParams(status="open")):
print(ev.name)
Filters
The filters subpackage provides a typed builder for the API's filters query parameter — never write the JSON by hand:
from threecommon import filters
from threecommon.events import ListParams
f = filters.and_(
filters.field("status").is_any_of(["open"]),
filters.field("ticket_sum").is_greater_than(10),
)
result = client.events.list(ListParams(filters=f.serialize()))
The full operator set is enumerated in threecommon.filters.types.
Retries
Idempotent methods (GET, PATCH, PUT) retry automatically on 408, 425, 429, 500, 502, 503, 504 and on network errors. Backoff is exponential with full jitter, capped at RetryDelay.max_seconds. The SDK honors a server-provided Retry-After header on 429.
POST and DELETE do not retry by default; pass an idempotency_key via per-request options to opt in (forward-compat — no v1 endpoints currently use this).
Telemetry
The SDK sends a small, anonymized Threecommon-Client-Telemetry header on every request (SDK version, language, last-request latency). This helps debug performance reports from real customers without instrumenting their code. Disable globally:
client = ThreeCommon(api_key="...", telemetry=False)
Or at runtime:
client.disable_telemetry()
The header never contains your API key, request bodies, or response bodies.
Repository layout
The package layout mirrors the Node and Go SDKs so behavior changes can be paired across languages:
sdk-python/
├── pyproject.toml
├── src/threecommon/
│ ├── __init__.py # public surface re-exports
│ ├── py.typed # PEP 561 marker
│ ├── client.py # ThreeCommon + AsyncThreeCommon
│ ├── config.py # ClientConfig, RetryDelay, defaults
│ ├── api_version.py # pinned API version + path
│ ├── version.py # SDK package version
│ ├── helpers.py # small utility helpers
│ ├── errors/ # exception tree
│ │ ├── base.py # APIError
│ │ └── classes.py # AuthError, NotFoundError, RateLimitError, ...
│ ├── pagination/ # auto-paginating iterators
│ │ └── auto_paginator.py # Iter[T] + AsyncIter[T]
│ ├── filters/ # typed filter builder (shared across resources)
│ ├── events/ # events resource (sync + async + Pydantic types)
│ ├── invoices/ # invoices resource (sync + async + Pydantic types)
│ ├── _core/ # private HTTP machinery (decomposed)
│ └── _generated/ # datamodel-code-generator output (re-run via `make gen`)
├── examples/
│ ├── events/
│ └── invoices/
└── tests/
Development
The project uses uv for venv + dependency management, ruff for lint and format, mypy and pyright for type checking, and pytest for testing.
Set up the dev environment
macOS and Linux:
# One-time: create a venv, activate, install runtime deps + dev tools
uv venv --python 3.10 .venv
source .venv/bin/activate
uv pip install -e ".[dev]"
# Verify:
uv pip list | grep threecommon # should print: threecommon 0.0.0.dev0 /path/to/sdk-python
pytest -q # all tests pass
Windows:
# One-time: create a venv, activate, install runtime deps + dev tools
uv venv --python 3.10 .venv
.\.venv\Scripts\activate.ps1
uv pip install -e ".[dev]"
# Verify:
uv pip list | Select-String threecommon # should print: threecommon 0.0.0.dev0 \path\to\sdk-python
pytest -q # all tests pass
If activation fails with an execution-policy error, run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned once and retry.
Note: with the virtual environment active, all further bash snippets should work as-is in PowerShell on Windows, except where a separate PowerShell snippet is provided.
Run tests
pytest # all tests
pytest tests/test_events.py # one file
pytest tests/test_events.py::test_list_decodes_response # one test
pytest -k "conformance" # match by name
pytest -q # quiet output
The conformance harness (tests/test_conformance.py) parametrizes over the shared YAML scenarios at ../conformance/scenarios/*.yaml and runs each one against both the sync and async clients.
Coverage
pytest --cov=src/threecommon --cov-report=term # term summary
pytest --cov=src/threecommon --cov-report=html # HTML report at htmlcov/
pytest --cov=src/threecommon --cov-fail-under=90 # CI gate (≥ 90% line + branch)
Lint and format
ruff check . # lint
ruff check --fix . # auto-fix
ruff format . # format
ruff format --check . # CI-style check (no changes)
Type check
Both run in CI; either failing blocks the PR.
mypy src/threecommon tests scripts # mypy --strict via pyproject
pyright src/threecommon scripts # pyright with project config
Regenerate OpenAPI models
src/threecommon/_generated/models.py is produced from ../openapi/spec.yaml. Re-run after every spec update:
macOS and Linux:
datamodel-codegen \
--input ../openapi/spec.yaml \
--input-file-type openapi \
--output src/threecommon/_generated/models.py \
--output-model-type pydantic_v2.BaseModel \
--target-python-version 3.10 \
--use-standard-collections --use-union-operator --use-double-quotes \
--field-constraints --use-schema-description --capitalise-enum-members \
--reuse-model --openapi-scopes paths schemas parameters
Windows:
datamodel-codegen `
--input ..\openapi\spec.yaml `
--input-file-type openapi `
--output .\src\threecommon\_generated\models.py `
--output-model-type pydantic_v2.BaseModel `
--target-python-version 3.10 `
--use-standard-collections --use-union-operator --use-double-quotes `
--field-constraints --use-schema-description --capitalise-enum-members `
--reuse-model --openapi-scopes paths schemas parameters
The generated package is treated as a contract reference; customer-facing types are hand-curated under src/threecommon/<resource>/types.py.
Live smoke (maintainer-only)
macOS and Linux:
THREECOMMON_API_KEY=3co_real_key \
SMOKE_EVENT_ID=evt_known \
python scripts/livesmoke.py
Windows:
$env:THREECOMMON_API_KEY = "3co_real_key"
$env:SMOKE_EVENT_ID = "evt_known"
python .\scripts\livesmoke.py
Runs ≤ 10 real API calls and verifies the happy path + 401/404 error paths. Set THREECOMMON_BASE_URL to override the default https://api.3common.com.
Build a wheel locally
uv build # produces sdist + wheel under dist/
Versioning
The SDK follows SemVer. The pinned API version (sent as Threecommon-Version) is independent — the API can evolve without breaking already-deployed SDKs. Bump api_version to opt into newer server behavior.
PyPI distribution: threecommon. Tags use the path-prefixed form sdk-python/vX.Y.Z to share the monorepo with the Node and Go SDKs.
Examples
End-to-end runnable examples live under examples/events/:
python examples/events/list_sync.py
python examples/events/list_async.py
python examples/events/retrieve.py
python examples/events/update.py
python examples/events/auto_paginate.py
python examples/events/error_handling.py
python examples/events/filters_demo.py
Replace 3co_your_api_key_here and evt_replace_with_real_id with real values before running.
Contributing
See the repository CONTRIBUTING guide. Issues and PRs welcome.
License
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 threecommon-0.11.0.tar.gz.
File metadata
- Download URL: threecommon-0.11.0.tar.gz
- Upload date:
- Size: 54.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c09ba352dedb999aeb2a4d44b1d2fd7b4eb8457c87d3b2cc141f4105c329a6b1
|
|
| MD5 |
7749a7d714783b1ce4f2c027de46ee3b
|
|
| BLAKE2b-256 |
bae9513e259b155430ae8c0fe005de6013cf5b2bad2e36248bc4c062ae059982
|
Provenance
The following attestation bundles were made for threecommon-0.11.0.tar.gz:
Publisher:
release-python.yml on 3-Common/sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threecommon-0.11.0.tar.gz -
Subject digest:
c09ba352dedb999aeb2a4d44b1d2fd7b4eb8457c87d3b2cc141f4105c329a6b1 - Sigstore transparency entry: 1872750997
- Sigstore integration time:
-
Permalink:
3-Common/sdk@10506aaedbcce305b6e9cf8fc422ffdbce235d4c -
Branch / Tag:
refs/tags/sdk-python/v0.11.0 - Owner: https://github.com/3-Common
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@10506aaedbcce305b6e9cf8fc422ffdbce235d4c -
Trigger Event:
push
-
Statement type:
File details
Details for the file threecommon-0.11.0-py3-none-any.whl.
File metadata
- Download URL: threecommon-0.11.0-py3-none-any.whl
- Upload date:
- Size: 77.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cea37a3b4db2622ff818828206146ef98c7fc5e141140ea76f4af8059d44cbbf
|
|
| MD5 |
e4eaad5e5f912c507492ed75e35309e9
|
|
| BLAKE2b-256 |
36cabc1d66b09407c265d9760501545e6f298e7441921328abc5d78694fe4d0d
|
Provenance
The following attestation bundles were made for threecommon-0.11.0-py3-none-any.whl:
Publisher:
release-python.yml on 3-Common/sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
threecommon-0.11.0-py3-none-any.whl -
Subject digest:
cea37a3b4db2622ff818828206146ef98c7fc5e141140ea76f4af8059d44cbbf - Sigstore transparency entry: 1872751600
- Sigstore integration time:
-
Permalink:
3-Common/sdk@10506aaedbcce305b6e9cf8fc422ffdbce235d4c -
Branch / Tag:
refs/tags/sdk-python/v0.11.0 - Owner: https://github.com/3-Common
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@10506aaedbcce305b6e9cf8fc422ffdbce235d4c -
Trigger Event:
push
-
Statement type: