recurrence.dev logo

API Documentation

Use the recurrence.dev API to expand, validate, and manipulate RFC5545 recurrence rules with sub-millisecond response times. Perfect for calendar applications, scheduling systems, and anything that needs reliable recurring events.

Quickstart

Get started in under a minute. Create an account, grab your API key, and make your first request.

1

Get your API key

Sign up for free and generate your API key from the dashboard.

2

Make a request

Call the /expand endpoint with your RRULE string.

3

Get occurrences

Receive an array of ISO 8601 dates back in milliseconds.

Get your API key

Authentication

All API requests require authentication via an API key in the Authorization header.

Request Header
Authorization: Bearer your_api_key_here

Keep your API key secure

Never expose your API key in client-side code. Use environment variables and server-side requests.

Guides

RRULE Basics

Learn the fundamentals of RFC5545 recurrence rules and how to construct them.

Read more →

Working with Timezones

Coming Soon

Handle timezone-aware recurrence rules and DST transitions correctly.

Exclusions & Overrides

Coming Soon

Use EXDATE, EXRULE, and RDATE to customize occurrence sets.

Error Handling

Understand error codes and implement robust error handling.

Read more →

RRULE Basics

RRULE (Recurrence Rule) is part of the RFC5545 iCalendar specification for defining repeating events. An RRULE describes a pattern for recurring dates using a set of properties.

Common Properties

Property Description Example
FREQ Frequency of recurrence DAILY, WEEKLY, MONTHLY, YEARLY
INTERVAL Interval between occurrences INTERVAL=2 (every 2nd)
BYDAY Days of the week BYDAY=MO,WE,FR
BYMONTHDAY Days of the month BYMONTHDAY=1,15
BYMONTH Months of the year BYMONTH=1,6,12
BYSETPOS Position in the set (e.g., 2nd Tuesday) BYSETPOS=2 or BYSETPOS=-1 (last)
COUNT Total number of occurrences COUNT=10
UNTIL End date for recurrence UNTIL=20261231T235959Z

Examples

FREQ=DAILY

Every day

FREQ=WEEKLY;BYDAY=MO,WE,FR

Every Monday, Wednesday, and Friday

FREQ=MONTHLY;BYDAY=TU;BYSETPOS=2

Second Tuesday of every month

FREQ=YEARLY;BYMONTH=12;BYMONTHDAY=25

Every December 25th (Christmas)

Try it out

Use the Playground to experiment with different RRULE patterns and see the generated occurrences in real-time.

POST

/v1/expand

Expand a recurrence rule into a list of occurrence dates. This is the core endpoint for generating dates from RRULE strings.

Endpoint
POST https://api.recurrence.dev/v1/expand

Request Body (JSON)

Parameter Type Required Description
rrule string Yes The RRULE string to expand
dtstart string Yes Start date in ISO 8601 format
limit integer No Max occurrences to return (default: 100, max: 1000)
until string No End boundary in ISO 8601 format
after string No Cursor for pagination (use moreItemsAfter from previous response)

Example Request

cURL
curl -X POST "https://api.recurrence.dev/v1/expand" \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"rrule": "FREQ=WEEKLY;BYDAY=MO,WE,FR", "dtstart": "2026-01-01T09:00:00Z", "limit": 10}'

Example Response

200 OK
{
  "occurrences": [
    "2026-01-02T09:00:00Z",
    "2026-01-05T09:00:00Z",
    "2026-01-07T09:00:00Z",
    "2026-01-09T09:00:00Z",
    "2026-01-12T09:00:00Z",
    "2026-01-14T09:00:00Z",
    "2026-01-16T09:00:00Z",
    "2026-01-19T09:00:00Z",
    "2026-01-21T09:00:00Z",
    "2026-01-23T09:00:00Z"
  ],
  "count": 10,
  "moreItemsAfter": "2026-01-23T09:00:00Z"
}

Pagination

When moreItemsAfter is present, pass its value as the after parameter to fetch the next page of results.

POST

/v1/expand/stream

Stream occurrences as newline-delimited JSON (NDJSON) for large datasets. Backed by a high-performance Rust engine capable of ~875K occurrences/second with constant memory usage.

Endpoint
POST https://api.recurrence.dev/v1/expand/stream

When to use streaming

Use /expand/stream when you need more than 1,000 occurrences or want to process results incrementally. For smaller datasets, the standard /expand endpoint with pagination is simpler. If your RRULE has a COUNT, it must be at least 1,000 to use streaming.

Request Body (JSON)

Parameter Type Required Description
rrule string Yes The RRULE string to expand
dtstart string Yes Start date in ISO 8601 format
until string No End boundary in ISO 8601 format (recommended for infinite rules)
batch_size integer No Occurrences per chunk: 10, 25, 50, or 100 (default: 50)

Example Request

cURL
curl -X POST "https://api.recurrence.dev/v1/expand/stream" \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"rrule": "FREQ=DAILY", "dtstart": "2024-01-01T09:00:00Z", "until": "2024-01-05T23:59:59Z"}'

Response Format (NDJSON)

The response is streamed as newline-delimited JSON. Each line is a complete JSON object. The stream ends with a metadata line.

200 OK (chunked)
Content-Type: application/x-ndjson
{"t":"2024-01-01T09:00:00Z"}
{"t":"2024-01-02T09:00:00Z"}
{"t":"2024-01-03T09:00:00Z"}
{"t":"2024-01-04T09:00:00Z"}
{"t":"2024-01-05T09:00:00Z"}
{"_meta":{"count":5,"status":"complete","units_charged":1,"quota_remaining":999}}

Client Example (JavaScript)

JavaScript
const response = await fetch('https://api.recurrence.dev/v1/expand/stream', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    rrule: 'FREQ=DAILY',
    dtstart: '2024-01-01T09:00:00Z',
    until: '2024-12-31T23:59:59Z'
  })
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const lines = decoder.decode(value).split('\n');
  for (const line of lines) {
    if (!line) continue;
    const data = JSON.parse(line);
    if (data._meta) {
      console.log(`Complete: ${data._meta.count} occurrences`);
    } else {
      console.log(`Occurrence: ${data.t}`);
    }
  }
}

Quota counting

Streaming uses occurrence-based metering: 1,000 occurrences = 1 unit (using ceiling). For example:

  • 999 occurrences = 1 unit
  • 1,000 occurrences = 1 unit
  • 5,432 occurrences = 6 units

The /expand endpoint always costs 1 unit per request regardless of result count.

Rate Limits

Limit /expand /expand/stream
Requests/min 1,000 100
Concurrent streams N/A 10 per token

Availability

Streaming is available on Developer and Enterprise plans. It is not available in the public demo API.

POST

/v1/parse

Parse an RRULE string into its structured components. Useful for validation and understanding rule structure without generating occurrences.

Endpoint
POST https://api.recurrence.dev/v1/parse

Request Body (JSON)

Parameter Type Required Description
rrule string Yes The RRULE string to parse

Example Request

cURL
curl -X POST "https://api.recurrence.dev/v1/parse" \
  -H "Authorization: Bearer your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"rrule": "FREQ=DAILY;INTERVAL=2;BYDAY=MO,WE,FR"}'

Example Response

200 OK
{
  "parsed": {
    "freq": "DAILY",
    "interval": 2,
    "byDay": ["MO", "WE", "FR"]
  },
  "input": "FREQ=DAILY;INTERVAL=2;BYDAY=MO,WE,FR"
}

Error Handling

All API errors return a consistent JSON structure with an error object containing a code and message.

Error Response Format

Error Response
{
  "error": {
    "code": "invalid_rrule",
    "message": "Invalid RRULE: unknown frequency 'DAILYY'"
  }
}

Error Codes

Code Status Description
missing_token 401 Authorization header is missing
invalid_token 401 API token is invalid or expired
quota_exhausted 402 Monthly quota exhausted, upgrade plan or wait for reset
not_found 404 The requested resource was not found
duplicate_stream 409 Stream with this ID is already in progress
missing_param 422 A required parameter is missing
invalid_rrule 422 The RRULE string is malformed or invalid
invalid_datetime 422 The datetime value is not valid ISO 8601 format
invalid_batch_size 422 The batch_size parameter is invalid (streaming endpoint)
count_too_small_for_stream 422 COUNT is below 1,000; use /expand with pagination instead
rrule_limit_exceeded 422 RRULE component values exceed allowed limits
config_not_supported 422 RRULE configuration exceeds plan limits
rate_limited 429 Too many requests/minute, check Retry-After header
concurrent_limit 429 Too many concurrent streams, check max_concurrent in response
internal_error 500 An unexpected server error occurred

Rate Limits

Rate limits are enforced per API key. Limits reset at the start of each calendar month.

Plan Monthly Limit Burst Rate
Developer 1,000 requests 10/sec
Enterprise Custom Custom

Rate limit headers

Check X-RateLimit-Remaining and X-RateLimit-Reset headers to track your usage.