"Add to calendar" is a small feature that removes real friction — instead of relying on your app's own push notifications (which users may have muted), you can drop a kickoff time directly into whatever calendar app someone already checks every day. This guide covers generating standard .ics files from match data.
Why .ics Instead of a Custom Reminder System
The .ics format (iCalendar) is supported natively by Google Calendar, Apple Calendar, Outlook, and virtually every calendar app. Generating one server-side means zero dependency on push notification permissions or your own reminder infrastructure — the user's own calendar app handles the reminder.
Step 1: Getting the Match Data You Need
Pull the match from /matches, which gives you everything required for a calendar entry — teams, kickoff time, and competition name:
import requests
response = requests.get(
'https://live-football-api.com/api/v1/matches',
params={'api_key': 'YOUR_KEY', 'date': '2026-09-05', 'lang': 'en'}
).json()
match = response['data']['matches'][0]
Step 2: Building the .ics File Content
An .ics file is plain text following a specific structure. Here's a minimal generator for a single match:
from datetime import datetime, timedelta
def build_ics(match):
start = datetime.strptime(match['date_time'], '%Y-%m-%d %H:%M:%S')
end = start + timedelta(hours=2) # matches typically run under 2 hours
summary = f"{match['home']['name']} vs {match['away']['name']}"
description = f"{match['league']['name']} — kickoff at {match['kickoff']}"
ics = f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//YourApp//Football Matches//EN
BEGIN:VEVENT
UID:{match['id']}@yourapp.com
DTSTAMP:{datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')}
DTSTART:{start.strftime('%Y%m%dT%H%M%S')}
DTEND:{end.strftime('%Y%m%dT%H%M%S')}
SUMMARY:{summary}
DESCRIPTION:{description}
LOCATION:{match.get('venue', '')}
END:VEVENT
END:VCALENDAR"""
return ics
Step 3: Serving the File for Download
Set the correct MIME type so browsers and mobile devices recognize it as a calendar file rather than plain text:
# Flask example
from flask import Response
@app.route('/match/<match_id>/calendar.ics')
def match_calendar(match_id):
match = get_match_by_id(match_id) # your own lookup
ics_content = build_ics(match)
return Response(
ics_content,
mimetype='text/calendar',
headers={'Content-Disposition': f'attachment; filename="match-{match_id}.ics"'}
)
Step 4: Handling Time Zones Correctly
Kickoff times from the API are typically in the competition's local time or UTC depending on the endpoint — always confirm which, and convert explicitly rather than assuming. A mismatch here is the most common bug in calendar exports, since a wrongly-timed calendar entry is worse than no entry at all:
import pytz
def to_utc_ics_format(date_str, source_tz='Europe/London'):
naive = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
localized = pytz.timezone(source_tz).localize(naive)
utc_time = localized.astimezone(pytz.utc)
return utc_time.strftime('%Y%m%dT%H%M%SZ')
Using the UTC format (with a trailing Z) in DTSTART/DTEND is the safest choice — every calendar app converts it to the viewer's local time zone automatically, so you don't need to guess where your user actually is.
Step 5: Adding Multiple Matches (a Team's Full Fixture List)
A single .ics file can contain multiple VEVENT blocks — useful for a "subscribe to my team's whole season" feature built from /team_matches:
def build_season_ics(matches, team_name):
events = '\n'.join(build_vevent_block(m) for m in matches)
return f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//YourApp//{team_name} Fixtures//EN
{events}
END:VCALENDAR"""
def build_vevent_block(match):
start = parse_and_convert_to_utc(match['date_time'])
return f"""BEGIN:VEVENT
UID:{match['id']}@yourapp.com
DTSTAMP:{datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')}
DTSTART:{start}
SUMMARY:{match['home']['name']} vs {match['away']['name']}
END:VEVENT"""
Step 6: A "Live" Subscription Link (Optional, Advanced)
Rather than a one-time download, you can serve a stable URL that calendar apps periodically re-fetch — this keeps postponed or rescheduled matches automatically updated in the user's calendar without them re-downloading anything:
<a href="webcal://yourapp.com/team/arsenal/calendar.ics">
Subscribe to Arsenal fixtures
</a>
The webcal:// scheme tells the OS to treat it as a live calendar subscription rather than a one-off file download — most calendar apps then re-check that URL periodically for changes.
Frequently Asked Questions
Does adding a calendar event require any special API access?
No, this is entirely built from standard /matches or /team_matches data on your own server — no special endpoint or extra credits are needed beyond the normal match data call.
What happens if a match gets postponed after a user has already added it to their calendar?
A one-time downloaded .ics file won't update automatically — this is exactly what the webcal:// subscription approach solves, since the calendar app re-fetches the source periodically.
Should I include venue information in the calendar event?
Yes if available — the LOCATION field lets the user's calendar app show a map link or travel time estimate, which is a nice touch for fans attending in person.
Can I generate .ics files client-side instead of on my server?
Yes, the same string-building logic works in JavaScript in the browser — generate a Blob with the .ics content and trigger a download via an anchor tag, without needing a server round-trip.