CalendarConnect API
Access academic calendar data for 1,000+ US universities. Get event dates for class schedules, breaks, finals, registration deadlines, and more through a simple REST API.
Getting Started
The CalendarConnect API provides read-only access to academic calendar events across 1,000+ US universities. The API is free for development and personal use.
Make your first API call to list universities:
curl "https://calendarconnect.app/api/v1/universities?limit=5"All API endpoints are available at /api/v1/. Responses return JSON with a consistent format including data, optional pagination, and meta fields.
{
"data": [ ... ],
"pagination": {
"total": 532,
"page": 1,
"limit": 20,
"hasMore": true
},
"meta": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2026-02-27T12:00:00.000Z"
}
}Authentication
API keys provide higher rate limits and are recommended for production use. You can make unauthenticated requests for testing, but they are subject to lower rate limits.
curl -H "X-API-Key: your_api_key_here" \
"https://calendarconnect.app/api/v1/universities"curl "https://calendarconnect.app/api/v1/universities?api_key=your_api_key_here"Contact us to request an API key for production use. Include your use case and expected request volume.
Rate Limits
Rate limits are applied per minute using a sliding window.
| Tier | Limit | Identifier |
|---|---|---|
| Authenticated | 100 req/min | Per API key |
| Unauthenticated | 20 req/min | Per IP address |
Rate limit information is included in every response via headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Max requests per window |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | Seconds until rate limit window resets |
When rate limited, you will receive a 429 response:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again in 45 seconds."
},
"meta": { "requestId": "...", "timestamp": "..." }
}API Reference
All endpoints return JSON and support CORS for browser-based requests. The base URL for all API endpoints is /api/v1.
List Universities
/api/v1/universitiesReturns a paginated list of universities. Supports search and state filtering.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | Optional | Search query (matches name, aliases, domain) |
state | string | Optional | State abbreviation (e.g., "CA", "NY") |
page | integer | Optional | Page number (default: 1) |
limit | integer | Optional | Results per page, max 100 (default: 20) |
offset | integer | Optional | Offset from start (overrides page) |
curl "https://calendarconnect.app/api/v1/universities?q=mit&limit=2"{
"data": [
{
"id": "abc-123",
"name": "Massachusetts Institute of Technology",
"aliases": ["MIT"],
"domain": "mit.edu",
"location": "Cambridge, MA",
"primaryColor": "#8b0000",
"secondaryColor": "#54585a",
"lastIngestedAt": "2026-02-20T10:00:00.000Z",
"createdAt": "2026-01-15T08:30:00.000Z"
}
],
"pagination": { "total": 1, "page": 1, "limit": 2, "hasMore": false },
"meta": { "requestId": "...", "timestamp": "..." }
}Get University
/api/v1/universities/:idReturns detailed information about a specific university including event count and last ingestion date.
curl "https://calendarconnect.app/api/v1/universities/abc-123"{
"data": {
"id": "abc-123",
"name": "Massachusetts Institute of Technology",
"aliases": ["MIT"],
"domain": "mit.edu",
"location": "Cambridge, MA",
"primaryColor": "#8b0000",
"secondaryColor": "#54585a",
"calendarSourceUrls": [
"https://registrar.mit.edu/calendar"
],
"extractionStrategy": "general",
"lastIngestedAt": "2026-02-20T10:00:00.000Z",
"eventCount": 24,
"lastIngestionDate": "2026-02-20T10:00:00.000Z",
"createdAt": "2026-01-15T08:30:00.000Z",
"updatedAt": "2026-02-20T10:05:00.000Z"
},
"meta": { "requestId": "...", "timestamp": "..." }
}Returns 404 if the university ID does not exist.
{
"error": {
"code": "NOT_FOUND",
"message": "University with id 'invalid-id' not found"
},
"meta": { "requestId": "...", "timestamp": "..." }
}University Events
/api/v1/universities/:id/eventsReturns events for a specific university with filtering and pagination.
| Parameter | Type | Required | Description |
|---|---|---|---|
from | ISO date | Optional | Start of date range (e.g., 2026-01-01) |
to | ISO date | Optional | End of date range (e.g., 2026-06-30) |
category | string | Optional | Event category filter (comma-separated) |
term | string | Optional | Academic term filter (e.g., "Fall", "Spring 2026") |
page | integer | Optional | Page number (default: 1) |
limit | integer | Optional | Results per page, max 100 (default: 20) |
curl "https://calendarconnect.app/api/v1/universities/abc-123/events?from=2026-01-01&to=2026-06-30&category=spring_break,final_exams"{
"data": [
{
"id": "evt-456",
"title": "Spring Break",
"category": "spring_break",
"startDate": "2026-03-14T00:00:00.000Z",
"endDate": "2026-03-22T00:00:00.000Z",
"allDay": true,
"term": "Spring 2026",
"academicYear": "2025-2026",
"confidence": 0.95,
"sourceUrl": "https://registrar.mit.edu/calendar"
}
],
"pagination": { "total": 3, "page": 1, "limit": 20, "hasMore": false },
"meta": { "requestId": "...", "timestamp": "..." }
}Search Events
/api/v1/eventsSearch events across multiple universities with full filtering support.
| Parameter | Type | Required | Description |
|---|---|---|---|
universityIds | string | Optional | Comma-separated university IDs |
q | string | Optional | Text search on event titles |
from | ISO date | Optional | Start of date range |
to | ISO date | Optional | End of date range |
category | string | Optional | Event category filter (comma-separated) |
term | string | Optional | Academic term filter |
page | integer | Optional | Page number (default: 1) |
limit | integer | Optional | Results per page, max 100 (default: 20) |
curl "https://calendarconnect.app/api/v1/events?q=finals&from=2026-05-01&to=2026-06-01&limit=5"{
"data": [
{
"id": "evt-789",
"title": "Final Examinations Begin",
"category": "final_exams",
"startDate": "2026-05-11T00:00:00.000Z",
"endDate": "2026-05-22T00:00:00.000Z",
"allDay": true,
"term": "Spring 2026",
"academicYear": "2025-2026",
"confidence": 0.92,
"sourceUrl": "https://registrar.stanford.edu/academic-calendar",
"university": {
"id": "def-456",
"name": "Stanford University",
"primaryColor": "#8c1515",
"secondaryColor": "#2e2d29"
}
}
],
"pagination": { "total": 48, "page": 1, "limit": 5, "hasMore": true },
"meta": { "requestId": "...", "timestamp": "..." }
}Upcoming Events
/api/v1/events/upcomingReturns events for the next 30 days, grouped by date. Useful for building dashboards and notification systems.
| Parameter | Type | Required | Description |
|---|---|---|---|
universityIds | string | Optional | Comma-separated university IDs to filter |
curl "https://calendarconnect.app/api/v1/events/upcoming"{
"data": [
{
"date": "2026-03-02",
"count": 12,
"events": [
{
"id": "evt-100",
"title": "Spring Registration Opens",
"category": "registration",
"startDate": "2026-03-02T00:00:00.000Z",
"endDate": null,
"allDay": true,
"term": "Spring 2026",
"academicYear": "2025-2026",
"university": {
"id": "ghi-789",
"name": "University of Michigan",
"primaryColor": "#00274c",
"secondaryColor": "#ffcb05"
}
}
]
}
],
"meta": { "requestId": "...", "timestamp": "..." }
}Data Model
Event Categories
Events are classified into 20 categories:
start_of_classesStart of Classesend_of_classesEnd of Classesadd_drop_deadlineAdd/Drop Deadlinewithdrawal_deadlineWithdrawal Deadlinereading_periodReading Periodstudy_periodStudy Periodfinal_examsFinal ExamscommencementCommencementconvocationConvocationorientationOrientationmove_inMove-Infall_breakFall Breakthanksgiving_breakThanksgiving Breakwinter_breakWinter Breakspring_breakSpring Breaksummer_breakSummer BreakholidayHolidayregistrationRegistrationadvisingAdvisingspecialSpecialUniversity Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (UUID) |
name | string | Full university name |
aliases | string[] | Alternative names (e.g., ["MIT"]) |
domain | string | University website domain |
location | string | "City, State" format |
primaryColor | string | Hex color code |
secondaryColor | string | Hex color code |
lastIngestedAt | ISO datetime | null | When calendar data was last scraped |
createdAt | ISO datetime | When the record was created |
Event Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (UUID) |
title | string | Event title |
category | string | Event category (see list above) |
startDate | ISO datetime | Event start date |
endDate | ISO datetime | null | Event end date (null for single-day events) |
allDay | boolean | Whether this is an all-day event |
term | string | null | Academic term (e.g., "Fall 2025") |
academicYear | string | null | Academic year (e.g., "2025-2026") |
confidence | number | Extraction confidence score (0-1) |
sourceUrl | string | null | URL of the source calendar page |
Date Formats
All dates in responses are ISO 8601 format: 2026-03-14T00:00:00.000Z. When providing dates as query parameters, use YYYY-MM-DD format (e.g., 2026-01-15).
Code Examples
cURL
# List universities in California
curl -H "X-API-Key: your_key" \
"https://calendarconnect.app/api/v1/universities?state=CA&limit=10"
# Get spring break events for a university
curl -H "X-API-Key: your_key" \
"https://calendarconnect.app/api/v1/universities/UNIVERSITY_ID/events?category=spring_break&from=2026-01-01&to=2026-06-30"
# Search for finals across all universities
curl -H "X-API-Key: your_key" \
"https://calendarconnect.app/api/v1/events?q=final+exam&from=2026-05-01&to=2026-06-01"
# Get upcoming events for the next 30 days
curl -H "X-API-Key: your_key" \
"https://calendarconnect.app/api/v1/events/upcoming"JavaScript (fetch)
const API_KEY = "your_api_key";
const BASE_URL = "https://calendarconnect.app/api/v1";
async function getUniversities(query) {
const params = new URLSearchParams({ q: query, limit: "10" });
const res = await fetch(`${BASE_URL}/universities?${params}`, {
headers: { "X-API-Key": API_KEY },
});
const { data, pagination } = await res.json();
return { universities: data, pagination };
}
async function getEvents(universityId, options = {}) {
const params = new URLSearchParams();
if (options.from) params.set("from", options.from);
if (options.to) params.set("to", options.to);
if (options.category) params.set("category", options.category);
const res = await fetch(
`${BASE_URL}/universities/${universityId}/events?${params}`,
{ headers: { "X-API-Key": API_KEY } }
);
const { data } = await res.json();
return data;
}
// Usage
const { universities } = await getUniversities("Stanford");
const events = await getEvents(universities[0].id, {
from: "2026-01-01",
to: "2026-06-30",
category: "spring_break,final_exams",
});
console.log(events);Python (requests)
import requests
API_KEY = "your_api_key"
BASE_URL = "https://calendarconnect.app/api/v1"
headers = {"X-API-Key": API_KEY}
# List universities
response = requests.get(
f"{BASE_URL}/universities",
headers=headers,
params={"q": "Stanford", "limit": 5},
)
data = response.json()
universities = data["data"]
print(f"Found {data['pagination']['total']} universities")
# Get events for a university
uni_id = universities[0]["id"]
response = requests.get(
f"{BASE_URL}/universities/{uni_id}/events",
headers=headers,
params={
"from": "2026-01-01",
"to": "2026-06-30",
"category": "spring_break,final_exams",
},
)
events = response.json()["data"]
for event in events:
print(f"{event['title']} - {event['startDate'][:10]}")
# Search across all universities
response = requests.get(
f"{BASE_URL}/events",
headers=headers,
params={"q": "commencement", "from": "2026-05-01"},
)
for event in response.json()["data"]:
print(f"{event['university']['name']}: {event['title']}")Error Codes
| Status | Code | Description |
|---|---|---|
| 200 | Success | Request completed successfully |
| 400 | BAD_REQUEST | Invalid parameters (bad date, invalid category) |
| 401 | UNAUTHORIZED | Invalid API key |
| 404 | NOT_FOUND | Resource not found (university ID does not exist) |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests, slow down |
| 500 | INTERNAL_ERROR | Server error, please retry |
CalendarConnect API v1. Have questions or need a higher rate limit? Get in touch.