icaldav
A modern, local-first CalDAV client engine and embeddable ASGI server for Python, powered by ical.
icaldav transforms CalDAV from a notoriously brittle network protocol into a fast, reliable local synchronization pipeline. By pairing ical's type-safe data processing and recurrence engine with an asyncio-native sync engine and embeddable server router, icaldav completes the Python calendar ecosystem.
For a detailed breakdown of the local-first synchronization model, dual-path sync engine, and server scope, see the Design Document.
Why icaldav?
Interacting with CalDAV servers in Python has historically meant navigating fragmented RFC support, heavy server-side search bugs, and complex XML queries. icaldav redefines CalDAV integration by adopting a local-first synchronization architecture:
Rather than relying on distant servers to perform complex date filtering or recurrence expansions, icaldav efficiently streams raw .ics payloads into local storage and delegates data modeling, validation, and timeline generation to ical.
| Feature | icaldav |
python-caldav |
Raw WebDAV Tools |
|---|---|---|---|
Async Native (asyncio) |
✅ First-class (aiohttp) |
⚠️ Partial wrapper module | ❌ Sync only |
| Data Engine | ✅ ical (Pydantic v2) |
❌ icalendar / vobject |
❌ None (raw XML/strings) |
| Recurrence Handling | ✅ Local & exact (Timeline) |
⚠️ Relies on client (icalendar) |
❌ None |
| Incremental Sync | ✅ RFC 6578 + ETag Fallback | ⚠️ Experimental | ❌ Manual |
| Embeddable Server | ✅ ASGI Router included | ❌ Client only | ❌ Client only |
| Type Safety | ✅ Strict py.typed |
⚠️ In progress | ❌ None |
Key Features
- ⚡ Async Client (
CalDavClient): Built withasyncioandaiohttpfor high-performance HTTP communication and type-safeicalPydantic models. - 🔄 Resilient Sync Engine (
CalDavSyncManager): Supports RFC 6578 (WebDAV Collection Synchronization) for single-request delta fetches, with automatic ETag fallback for non-RFC 6578 servers (e.g. Radicale, iCloud). - 🧩 Local-First Recurrence & Queries: Performs time-range slicing and recurrence expansions locally via
ical'sTimeline, avoiding server-sidecalendar-querybugs. - 🚀 Embeddable ASGI Server Router (
CalDavRouter): Mount a minimalist CalDAV server endpoint into FastAPI, Starlette, oraiohttpapplications to expose calendars to native iOS, macOS, or Thunderbird clients.
Installation
uv add icaldav
Or with pip:
pip install icaldav
Requires Python 3.12+.
Quickstart
Syncing a Remote Calendar Down to Local Storage
import asyncio
from icaldav import CalDavClient, CalDavSyncManager
from icaldav.store import SQLiteStore
async def main():
async with CalDavClient(
url="https://caldav.example.com",
username="user",
password="password",
) as client:
# Discover calendar collections
calendars = await client.get_calendars()
calendar = calendars[0]
# Sync events down to a local store
store = SQLiteStore("calendar_cache.db")
sync_manager = CalDavSyncManager(client=client, collection=calendar, store=store)
await sync_manager.run()
# Query locally using `ical`'s timeline
local_calendar = await store.get_calendar()
for event in local_calendar.timeline.today():
print(f"{event.start}: {event.summary}")
asyncio.run(main())
Embedding a CalDAV Server Endpoint
from fastapi import FastAPI
from icaldav.server import CalDavRouter
from icaldav.provider import LocalCalendarProvider
app = FastAPI()
# Expose local calendars to native mobile & desktop clients
app.include_router(
CalDavRouter(provider=LocalCalendarProvider()),
prefix="/caldav",
)
License
Apache-2.0
Metadata
Release files for icaldav 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| icaldav-0.0.2.tar.gz | 7.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| icaldav-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 15.1 kB
Release files / icaldav-0.0.2.tar.gz
| Download URL | icaldav-0.0.2.tar.gz |
|---|---|
| Size | 7.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
23e3f917ffc325eeacb726895a0ff8cb6ba0b3c00f9f4c7d14711e29d2deff48
|
|
BLAKE2b-256 checksum How to use checksums |
a987a176862c57d84c1c84dca4eebde74e11b017c703700c54bb47b21d5357bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.
Transparency logRelease files / icaldav-0.0.2-py3-none-any.whl
| Download URL | icaldav-0.0.2-py3-none-any.whl |
|---|---|
| Size | 7.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f4533741434524922c1eed61947dbe94b12af5d72ef15994f618d0dd45be858
|
|
BLAKE2b-256 checksum How to use checksums |
2ee5a8d8136c14ccfedfe5aa880188667d3d4a0f9f0821d7ae14212fc4c8918a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.
Transparency log