Modern Python client for CiviCRM API v4 with web framework integration
Project description
civicrm-py
A Python client for CiviCRM's API v4. Async by default, with a query interface that'll feel familiar if you've used Django's ORM.
What it does
- Async HTTP via httpx (sync wrappers available)
- Type-safe models using msgspec
- Chainable queries:
client.Contact.filter(is_deleted=False).limit(10).all() - Integrations for Django, Litestar, FastAPI, Flask, and others
Install
pip install civicrm-py
With a framework:
pip install civicrm-py[django]
pip install civicrm-py[litestar]
pip install civicrm-py[fastapi]
Optional extras:
pip install civicrm-py[sqlspec] # Local database caching
pip install civicrm-py[workflows] # Workflow automation
See docs for sqlspec, workflows, and pytest-databases.
Setup
export CIVI_BASE_URL=https://your-site.org/civicrm/ajax/api4
export CIVI_API_KEY=your-api-key
export CIVI_SITE_KEY=your-site-key
Usage
import asyncio
from civicrm_py import CiviClient
async def main():
async with CiviClient() as client:
# Get all active contacts
contacts = await client.Contact.filter(is_deleted=False).all()
# Single contact by ID
contact = await client.Contact.get(id=123)
# Create
new_contact = await client.Contact.create(
first_name="John",
last_name="Doe",
contact_type="Individual",
)
# Update
await client.Contact.update(id=123, first_name="Jane")
# Delete
await client.Contact.delete(id=123)
asyncio.run(main())
Queries
# Select specific fields, filter, sort, paginate
contacts = await (
client.Contact
.select("id", "display_name", "email_primary.email")
.filter(is_deleted=False, contact_type="Individual")
.order_by("-created_date")
.limit(25)
.all()
)
# Operators
contacts = await (
client.Contact
.filter_by("created_date", ">=", "2024-01-01")
.filter_by("display_name", "LIKE", "%Smith%")
.all()
)
# Count and exists
total = await client.Contact.filter(is_deleted=False).count()
exists = await client.Contact.filter(id=123).exists()
Litestar
from litestar import Litestar, get
from civicrm_py import CiviClient
from civicrm_py.contrib.litestar import CiviPlugin
@get("/contacts")
async def get_contacts(civi: CiviClient) -> list[dict]:
return await civi.Contact.filter(is_deleted=False).all()
app = Litestar(
route_handlers=[get_contacts],
plugins=[CiviPlugin()],
)
Django
# settings.py
CIVI_SETTINGS = {
"BASE_URL": "https://example.org/civicrm/ajax/api4",
"API_KEY": env("CIVI_API_KEY"),
"SITE_KEY": env("CIVI_SITE_KEY"),
}
# views.py
from civicrm_py.contrib.django import get_civi_client
async def contact_list(request):
async with get_civi_client() as client:
contacts = await client.Contact.all()
return JsonResponse({"contacts": contacts})
Development
git clone https://github.com/JacobCoffee/civicrm-py.git
cd civicrm-py
make install # dependencies
make test # tests
make check # lint + typecheck
Docs
Alternatives
- civipy - Another Python client for CiviCRM. No built-in web framework integrations.
License
MIT
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 civicrm_py-0.1.2.tar.gz.
File metadata
- Download URL: civicrm_py-0.1.2.tar.gz
- Upload date:
- Size: 211.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a914cc5ab0a70afd8ff0d534085d2370dcd7d75cb35a3c6037c5525a809859f7
|
|
| MD5 |
f7d93e2e8f7a4a6db001281bcb9be7ee
|
|
| BLAKE2b-256 |
c4e03437a0f742b52a9f2b8bd8537b75494379606f42cb5e8d9f75586f182f4f
|
Provenance
The following attestation bundles were made for civicrm_py-0.1.2.tar.gz:
Publisher:
publish.yml on JacobCoffee/civicrm-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
civicrm_py-0.1.2.tar.gz -
Subject digest:
a914cc5ab0a70afd8ff0d534085d2370dcd7d75cb35a3c6037c5525a809859f7 - Sigstore transparency entry: 849519661
- Sigstore integration time:
-
Permalink:
JacobCoffee/civicrm-py@2b27cff4af2ad9ca31a4a3697adb735ccc8f5ed7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/JacobCoffee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2b27cff4af2ad9ca31a4a3697adb735ccc8f5ed7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file civicrm_py-0.1.2-py3-none-any.whl.
File metadata
- Download URL: civicrm_py-0.1.2-py3-none-any.whl
- Upload date:
- Size: 261.2 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 |
45d679417e3d9204562e31d77867a205a6fd62809f4fb0d864b8508ddcb3b4ed
|
|
| MD5 |
c4f14ce26c3bffd7c26d9c2fb33bbd23
|
|
| BLAKE2b-256 |
f8ab70845e19bbb4fd0b5f46901640d62ab120f9e3d27754a25b719ca325c75c
|
Provenance
The following attestation bundles were made for civicrm_py-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on JacobCoffee/civicrm-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
civicrm_py-0.1.2-py3-none-any.whl -
Subject digest:
45d679417e3d9204562e31d77867a205a6fd62809f4fb0d864b8508ddcb3b4ed - Sigstore transparency entry: 849519671
- Sigstore integration time:
-
Permalink:
JacobCoffee/civicrm-py@2b27cff4af2ad9ca31a4a3697adb735ccc8f5ed7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/JacobCoffee
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2b27cff4af2ad9ca31a4a3697adb735ccc8f5ed7 -
Trigger Event:
push
-
Statement type: