Skip to main content

ical

PyPI version CI Documentation

A modern Python RFC 5545 iCalendar library with built-in recurring event support — no companion libraries needed.

from ical.calendar_stream import IcsCalendarStream

cal = IcsCalendarStream.calendar_from_ics(ics_content)

# Recurring events are automatically expanded — just iterate
for event in cal.timeline:
    print(event.start, event.summary)

Why ical?

Most Python iCalendar libraries require two separate packages to handle recurring events:

# The typical ecosystem approach — two libraries, two APIs
import icalendar
import recurring_ical_events

cal = icalendar.Calendar.from_ical(ics_content)
events = recurring_ical_events.of(cal).between(start, end)

ical handles this natively with a single, unified Timeline interface, with a Pythonic API and validated inputs.

Feature ical icalendar ics.py
Built-in recurrence expansion ✅ ❌ needs recurring-ical-events ❌
Pythonic attribute access (event.start, event.summary) ✅ ❌ (event.get('DTSTART').dt) ✅
Input validation with clear error messages ✅ ❌ ❌
Full type annotations (py.typed) ✅ strict ty ✅ v7+ ❌
Application-level store API ✅ ❌ ❌
RFC 7986 / RFC 6868 / RFC 8536 ✅ partial ❌
Active maintenance ✅ ✅ ⚠️ stalled

Used By

Installation

uv add ical

Or with pip:

pip install ical

Requires Python 3.11+.

Fetching calendars from a url asynchronously (see below) requires the optional async extra, which pulls in aiohttp:

pip install ical[async]

Quickstart

Reading an .ics file

Parse a calendar file and iterate over events in chronological order, with recurring events automatically expanded:

from pathlib import Path
from ical.calendar_stream import IcsCalendarStream
from ical.exceptions import CalendarParseError

filename = Path("calendar.ics")
with filename.open() as ics_file:
    try:
        cal = IcsCalendarStream.calendar_from_ics(ics_file.read())
    except CalendarParseError as err:
        print(f"Failed to parse '{filename}': {err}")
    else:
        for event in cal.timeline:
            print(event.start, event.summary)

Fetching a remote .ics url

Fetching a remote calendar over HTTP without blocking the event loop requires the optional ical[async] extra:

from ical.calendar_stream import IcsCalendarStream

cal = await IcsCalendarStream.calendar_from_url("https://example.com/calendar.ics")
for event in cal.timeline:
    print(event.start, event.summary)

An existing aiohttp.ClientSession can be passed in via the session argument to reuse connection pooling; otherwise a session is created and closed automatically for the request.

Creating a calendar

from datetime import date
from ical.calendar import Calendar
from ical.event import Event

cal = Calendar()
cal.events.append(
    Event(summary="Team standup", start=date(2024, 1, 15), end=date(2024, 1, 16)),
)
for event in cal.timeline:
    print(event.summary)

Writing an .ics file

from pathlib import Path
from ical.calendar_stream import IcsCalendarStream

with Path("output.ics").open("w") as f:
    f.write(IcsCalendarStream.calendar_to_ics(cal))

Recurring events

Recurring events are stored once in the calendar but automatically expanded by the Timeline:

from datetime import date
from ical.calendar import Calendar
from ical.event import Event
from ical.types.recur import Recur

cal = Calendar()
cal.events.append(
    Event(
        summary="Weekly standup",
        start=date(2024, 1, 15),
        end=date(2024, 1, 16),
        rrule=Recur.from_rrule("FREQ=WEEKLY;COUNT=10"),
    )
)

# All 10 occurrences are expanded automatically
for event in cal.timeline:
    print(event.start, event.summary)

Application-level API

For managing calendar state in an application (ensuring timezones are set correctly, editing individual instances of recurring events, etc.), use ical.store:

from ical.store import EventStore
from ical.event import Event
from datetime import datetime, timezone

store = EventStore()
store.add(Event(summary="Meeting", start=datetime(2024, 1, 15, 9, tzinfo=timezone.utc)))

See the full documentation for the complete API reference.

Technical Guides

We publish detailed technical guides exploring the library's design:

Supported RFCs

  • RFC 5545 — Internet Calendaring and Scheduling Core Object Specification (iCalendar)
  • RFC 6868 — Parameter Value Encoding in iCalendar and vCard
  • RFC 7986 — New Properties for iCalendar
  • RFC 8536 — The Time Zone Information Format (TZif)

Comparison with other libraries

ical is designed for applications that need a complete, modern solution. You may prefer an alternative if:

  • icalendar — You need low-level control over raw iCalendar components, jCal (JSON) support, or maximum ecosystem compatibility. You prefer to expand recurring events manually and don't mind a second library (recurring-ical-events). You need to support legacy Python versions (3.8+) that ical does not target.
  • ics.py — You need a simple read-only script and do not require recurrence support. Note: no stable release since 0.7.2 (August 2021).

Contributing

See CONTRIBUTING.md for setup and development instructions.

Metadata

Release files for ical 14.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ical 14.3.1
File Size Uploaded Provenance Actions
ical-14.3.1.tar.gz 163.1 kB With provenance Details for ical-14.3.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ical 14.3.1
File Interpreter ABI Platform Actions
ical-14.3.1-py3-none-any.whl Python 3 none any Details for ical-14.3.1-py3-none-any.whl

Total release size: 310.4 kB

Release files / ical-14.3.1.tar.gz

Download URL ical-14.3.1.tar.gz
Size 163.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2a6ea215effa6a1c9cae93570b648f105fcaeba9677d0f7b3b7d63d0a18085b1
BLAKE2b-256 checksum
How to use checksums
b9c8e5a94d5da640a71958d3033f0ff4a1fd9c39e599e1033b3c5c59c49b259c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.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 Oct 20, 2026.

Transparency log

Release files / ical-14.3.1-py3-none-any.whl

Download URL ical-14.3.1-py3-none-any.whl
Size 147.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a70435a53d8c1adfaa6a74a3417d79b3cd871b7e090f27fb0bb83f5e089e2009
BLAKE2b-256 checksum
How to use checksums
305079753d726ea6c28ca728c60be0b426681931be286ad459ca2f31389d0e39
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.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 Oct 20, 2026.

Transparency log

Security for ical 14.3.1

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ical-14.3.1.tar.gz

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 Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

14.3.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page