PyCalendar API: Official Python Client for Moroccan Holidays & Business Days
PyCalendar API is the official, fully-typed Python client wrapper for the Calendar-API.ma platform. It allows developers to programmatically fetch Moroccan public holidays, track Hijri calendar events, and compute local business day data with minimal dependencies.
🔗 Quick Links: 📝 Register for Free | 📚 Documentation | ⚡ Live API Docs | 🟢 Health Check
📑 Table of Contents
✨ Key Features
- Moroccan Public Holidays: Dynamically fetch national, religious, and exceptional holidays.
- Dual Calendar Support: Track
ReligiousandExceptionalholidays for accurate calculation and history sanitization. - Business Day Calculations: Calculate working days, skipping custom weekends and local banking closures.
- Modern Python Design: Built with
dataclasses, fully typed, and automatic JSON-to-Python object conversion. - Minimal Dependencies: Lightweight and fast.
- Advanced Configuration: Supports
.tomlfiles for timeout, proxy, and certificate management.
📦 Installation
Install the latest version from PyPI:
pip install pycalendar-api
Quick Start & Usage Examples
Ensure that you are registered, if not, you can Register for free
Instanciate the API Client by providing one the active APIKeys from your console
from pycalendar_api import CalendarApi, to_date # Util function for date creation
>>> api = CalendarApi('YOUR_API_KEY')
>>> print(api.health())
ApiHealth(api_status="UP", appname="calendar-api", timestamp=datetime.datetime.now())
Using the environment variable PYCALENDAR_APIKEY
# Ensure that the environment variable is defined
# export PYCALENDAR_APIKEY = YOUR_API_KEY
from pycalendar_api import CalendarApi, to_date # Util function for date creation
>>> api = CalendarApi.from_env()
>>> print(api.health())
ApiHealth(api_status="UP", appname="calendar-api", timestamp=datetime.datetime.now())
🌙 Holidays API
Holidays domain of Calendar API.
- Check if a date is a holiday
from pycalendar_api import CalendarApi, to_date
api = CalendarApi('YOUR_API_KEY') # or api = CalendarApi.from_env()
# Check if a date is a Holiday
>>> result = api.holidays.is_holiday(to_date('14/01/2025'))
>>> print(result)
HolidayCheckResult(
date=datetime.date(2025, 1, 14), is_holiday=True,
description="Nouvel An Amazigh", holiday_type=<CalHolidayType.NATIONAL: 'National'>,
status=<CalHolidayStatus.OFFICIAL: 'Official'>, country_code='MA'
)
- List holidays of a year or many years
>>> result = api.holidays.year(2025)
>>> print(result)
[
Holiday(description="Jour de l'AN", day=1, month=1, date=datetime.date(2025, 1, 1), holiday_type=<CalHolidayType.NATIONAL: 'National'>, status=<CalHolidayStatus.OFFICIAL: 'Official'>, country_code='MA'),
Holiday(description="Manifeste de l'Indépendance", day=11, month=1, date=datetime.date(2025, 1, 11), holiday_type=<CalHolidayType.NATIONAL: 'National'>, status=<CalHolidayStatus.OFFICIAL: 'Official'>, country_code='MA'),
Holiday(description="Nouvel An Amazigh", day=14, month=1, date=datetime.date(2025, 1, 14), holiday_type=<CalHolidayType.NATIONAL: 'National'>, status=<CalHolidayStatus.OFFICIAL: 'Official'>, country_code='MA'),
...,
]
# List Holidays of many years
>>> result = api.holidays.years(list(range(2017, 2027)))
>>> print(result)
[
Holiday(description="Jour de l'AN", day=1, month=1, date=datetime.date(2025, 1, 1), holiday_type=<CalHolidayType.NATIONAL: 'National'>, status=<CalHolidayStatus.OFFICIAL: 'Official'>, country_code='MA'),
Holiday(description="Manifeste de l'Indépendance", day=11, month=1, date=datetime.date(2025, 1, 11), holiday_type=<CalHolidayType.NATIONAL: 'National'>, status=<CalHolidayStatus.OFFICIAL: 'Official'>, country_code='MA'),
Holiday(description="Nouvel An Amazigh", day=14, month=1, date=datetime.date(2025, 1, 14), holiday_type=<CalHolidayType.NATIONAL: 'National'>, status=<CalHolidayStatus.OFFICIAL: 'Official'>, country_code='MA'),
...,
]
🏢 Business Days API
Business days domain of Calendar API.
Bdays operations
- Fetch the next or previous Business day
# Next Business day
result = api.bdays.next_date(to_date('13/01/2025'))
>>> print(result)
NextDate(date=datetime.date(2025, 1, 13), next_date=datetime.date(2025, 1, 15))
# Previous Business day
result = api.bdays.previous_date(to_date('15/01/2025'))
>>> print(result)
PreviousDate(date=datetime.date(2025, 1, 15), previous_date=datetime.date(2025, 1, 13))
- List Business days of a year or a month
# Business days of a year
result = api.bdays.bdays_of(2025)
>>> print(result)
DateSeries(
ref='bdays-2025', min_date='2025-01-02', max_date='2025-12-31', freq=<CalFreq.DAILY: 'D'>, nitems=247,
serie=SortedSet([datetime.date(2025, 1, 2), datetime.date(2025, 1, 3), datetime.date(2025, 1, 6), ..., datetime.date(2025, 12, 29), datetime.date(2025, 12, 30), datetime.date(2025, 12, 31)])
)
# Business days of a month
result = api.bdays.bdays_of(2025, month=6)
>>> print(result)
DateSeries(
ref='bdays-2025-06', min_date='2025-06-02', max_date='2025-06-30', freq=<CalFreq.DAILY: 'D'>, nitems=19,
serie=SortedSet([datetime.date(2025, 6, 2), datetime.date(2025, 6, 3), datetime.date(2025, 6, 4), datetime.date(2025, 6, 5), ..., datetime.date(2025, 6, 25), datetime.date(2025, 6, 26), datetime.date(2025, 6, 30)])
)
- Count or List Business days between 2 dates
# Count Business days between 2 dates
>>> count = api.bdays.count(to_date('02/01/2025'), to_date('31/12/2025'))
>>> print(count)
DaysCount(start_date=datetime.date(2025, 1, 2), end_date=datetime.date(2025, 12, 31), count=247, freq=<CalFreq.DAILY: 'D'>)
# List Business days between 2 dates
>>> between = api.bdays.between(to_date('02/01/2025'), to_date('31/12/2025'))
>>> print(between)
DateSeries(
ref='bdays', min_date='2025-01-02', max_date='2025-12-31', freq=<CalFreq.DAILY: 'D'>, nitems=247,
serie=SortedSet([datetime.date(2025, 1, 2), datetime.date(2025, 1, 3), datetime.date(2025, 1, 6), ..., datetime.date(2025, 12, 29), datetime.date(2025, 12, 30), datetime.date(2025, 12, 31)])
)
Spans: Start/End of periods
Calculate the interval composed of the start and end dates of a given period. The start and end dates are open business days.
Successive intervals can be linked
# =============== SPANS: Start/End of periods ===============
# Year 2025 span
>>> span_year = api.bdays.span(2025)
>>> print(span_year)
CalSpan(start_date=datetime.date(2024, 12, 31), end_date=datetime.date(2025, 12, 31), year=2025, semester=None, quarter=None, month=None, country_code='MA')
# 1st Semester of year 2025
>>> span_s1 = api.bdays.span(2025, semester=1)
>>> print(span_s1)
CalSpan(start_date=datetime.date(2024, 12, 31), end_date=datetime.date(2025, 6, 30), year=2025, semester=1, quarter=None, month=None, country_code='MA')
# 3rd Quarter of year 2025
>>> span_quarter = api.bdays.span(2025, quarter=3)
>>> print(span_quarter)
CalSpan(start_date=datetime.date(2025, 6, 30), end_date=datetime.date(2025, 9, 30), year=2025, semester=None, quarter=3, month=None, country_code='MA')
# Month 7 of year 2025 (July)
>>> span_month = api.bdays.span(2025, month=7)
>>> print(span_month)
CalSpan(start_date=datetime.date(2025, 6, 30), end_date=datetime.date(2025, 7, 31), year=2025, semester=None, quarter=None, month=7, country_code='MA')
🏷️ Enum Properties
Those properties are present in the Response objects and are used as query parameters.
🗂️ Holiday Type (StrEnum)
A Holiday has one of those types
| Value | Description |
|---|---|
| National | The date is a national Holiday |
| Religious | The date is a Religious Holiday (Check the status if it's Confirmed or not) |
| Exceptional | The date is an exceptional or bridge Holiday |
🌕 Holiday Status (StrEnum)
Those properties concern the religious holidays which are Estimated before the moon sighting,
once confirmed their status change to Official.
Consider using polling (Every Hour) for religious holidays which are estimated.
| Value | Description |
|---|---|
| Official | The Holiday is Official |
| Estimated | Religious Holidays are estimated before the moon sighting |
🛠️ Support
If you encounter any bugs or have questions, we are here to help:
- ✉️ Email Support: support@calendar-api.ma
- 🐛 Issue Tracker: GitLab Repository Issues
- 📝 Contact Form: calendar-api.ma/contacts.html
Metadata
Release files for pycalendar-api 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pycalendar_api-0.7.0.tar.gz | 245.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pycalendar_api-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 272.1 kB
Release files / pycalendar_api-0.7.0.tar.gz
| Download URL | pycalendar_api-0.7.0.tar.gz |
|---|---|
| Size | 245.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8ea1ca9ce07db52af042cda7c376b046c6c14c0adc01f1da97eade53822ef790
|
|
BLAKE2b-256 checksum How to use checksums |
98cf795c315728913703d071dcaf2c481efd65d5e365f38dbe137cb343d4d7e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.11
|
Release files / pycalendar_api-0.7.0-py3-none-any.whl
| Download URL | pycalendar_api-0.7.0-py3-none-any.whl |
|---|---|
| Size | 26.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bbb9036ab11f06702bdede42afd9608e9e8763cf271f59c683c6d92e3bb2d9ca
|
|
BLAKE2b-256 checksum How to use checksums |
1f59caa9885216eb8fb0f13df2f50fd528954e81889d7bf95875d912d7c7ec32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.11
|