Docs

Use casesCalendars and apps

Calendars that stay right as plans change.

Per-day counts for the month, every session of a recurring series, and a change feed that tells you what moved.

Real screens from TinyJawns, built on Tailend

/events/calendar · occurrences=expand · changed_since · /v1/changes · /classes

Oct 12–18Fishtown family calendar
Mon126 freeStory timeToddler gym
Tue159 freeLibrary LEGOSwim
Wed147 freeArt club
Thu188 freeMusic & meNature walk
Fri2210 freeFamily movie
Sat4122 freeStory timeSensory JumpFall fest
Sun2915 freeBird walkYoga
GET /v1/philadelphia/events/calendar?from=2026-10-12&to=2026-10-18Sample shown

The problem

A calendar is only as good as its last sync.

  • Month views are heavy.Counting a month by loading every event is slow and drifts from the list.
  • Series hide dates.A weekly class is one row on its page, but a calendar needs every date.
  • Things move.Times shift and events are cancelled after you have copied them.

How to build it

Month, day, then keep it fresh.

  1. Draw the month from counts

    One call returns every date in the window with how many events start that day, how many are free, and counts per category. Zeros included.

    Try it in the playground
    GET/v1/philadelphia/events/calendar?from=2026-10-05&to=2026-10-07Sample from the API reference
    {
      "data": [
        {
          "date": "2026-10-05",
          "count": 41,
          "free_count": 22,
          "categories": {
            "storytime_books": 14,
            "arts_creative": 9,
            "sports_active_play": 6
          }
        },
        {
          "date": "2026-10-06",
          "count": 0,
          "free_count": 0,
          "categories": {}
        },
        {
          "date": "2026-10-07",
          "count": 57,
          "free_count": 31,
          "categories": {
            "storytime_books": 19,
            "music_movement": 8
          }
        }
      ],
      "meta": {
        "metro": "philadelphia",
        "window": {
          "from": "2026-10-05",
          "to": "2026-10-07",
          "timezone": "America/New_York"
        },
        "total": 98
      }
    }
  2. Fill a day with every occurrence

    List the day with occurrences=expand. Recurring series appear once per date, and the list always matches the day's count.

    Try it in the playground
    GET/v1/philadelphia/events?from=2026-09-14&to=2026-09-15&occurrences=expandSample, trimmed
    {
      "data": [
        {
          "id": "b81a95d2-1d4e-46fd-9cff-322a41e429ad",
          "name": "Toddler Time (Ages 5 and Under)",
          "occurrence": {
            "starts_local": "2026-09-14T09:30:00",
            "starts_at": "2026-09-14T13:30:00.000Z",
            "ends_local": null,
            "index": 0
          }
        },
        {
          "id": "b81a95d2-1d4e-46fd-9cff-322a41e429ad",
          "name": "Toddler Time (Ages 5 and Under)",
          "occurrence": {
            "starts_local": "2026-09-15T09:30:00",
            "starts_at": "2026-09-15T13:30:00.000Z",
            "ends_local": null,
            "index": 1
          }
        }
      ],
      "meta": {
        "count": 2,
        "has_more": false
      }
    }
  3. Stay in sync with changes

    Poll /v1/changes for what moved since your last sync: a closed kind and the field names that changed. Removals appear too.

    Try it in the playground
    GET/v1/changes?since=2026-09-10T00:00:00Z&metro=philadelphiaSample from the API reference
    {
      "data": [
        {
          "version_id": 18231,
          "listing_id": "2f3c9d4e-3d2b-4b7d-9a3e-1b2c3d4e5f60",
          "slug": "storytime-in-the-park-2026-09-12",
          "type": "event",
          "metro": "philadelphia",
          "changed_at": "2026-09-10T06:12:05Z",
          "kind": "date_changed",
          "fields": [
            "starts_at"
          ]
        }
      ],
      "meta": {
        "limit": 1,
        "count": 1,
        "has_more": false
      }
    }

Key fields

What keeps a calendar honest.

count, free_count
Occurrences starting that local date, exactly what the day list returns under the same filters.
categories
Per-day counts by category, for colored dots or a filter row without a second call.
occurrence.index
Position in the series. id plus index names one session, so you can store and update it.
starts_local, timezone
Local wall-clock time in the metro's zone, plus the UTC starts_at for your own math.
changed_since
On /events, /classes, /camps and /places: rows edited after an instant. Delivery is at least once, so upsert by id.
kind, fields
On /v1/changes: date_changed, price_changed, archived and more, with the public field names that changed.
schedule.days, term
On /classes: the weekly days and the term dates. Filter with schedule_day and openings=true.
verified_at
When the source last confirmed the listing. Good for a quiet "confirmed this week" line.

Guides and newsletters

Draft "this weekend near you" from confirmed listings.

Filter by neighborhood and a weekend window, then link each item back to its organizer with the include=source add-on.

Anything that could not be confirmed is left out, so you are not sending readers to a cancelled event.

Parameters it uses

neighborhoodfrom / tofreeinclude=source add-on
TinyJawns weekly to: Philly parentsMid-Autumn Moved to Saturday, Plus a Sound Bath and Castanets
The TinyJawns weekly issue for October 3: a short opening note on the weekend weather, then picks with photos, times, places, ages and cost, starting with the Mid-Autumn Festival in Chinatown and a free gallery at the Weitzman.
TinyJawns weekly, built from Tailend listings. Read past issues
GET /v1/philadelphia/events?from=2026-10-03&to=2026-10-04&include=sourceRequest shape
CustomerPhiladelphia's family guidePowered by tailend

Built with Tailend

TinyJawns runs on it.

TinyJawns is a family guide for Philadelphia. Every listing on its calendar and in its weekly newsletter comes through the Tailend API.

The TinyJawns calendar in month view for October 2026: dots for each day, event chips with Free badges on the current week, and filters for indoor, free and weekend picks.

Month view. Per-day counts for the whole month, with a free count behind the Free filter.

GET /v1/philadelphia/events/calendar?from=2026-10-01&to=2026-10-31
The TinyJawns calendar in week view for October 12 to 18: all-day events and timed events by hour, with weather for each day and Free badges.

Week view. Every session in the week, with recurring series expanded.

GET /v1/philadelphia/events?from=2026-10-12&to=2026-10-18&occurrences=expand
The TinyJawns calendar showing only classes for the week, with Mini Masters selected: Thursday at 2pm in Fairmount, ages 3 to 5, $8, registration required.

Classes. Every class meeting that week, with its days, ages and cost.

GET /v1/philadelphia/classes?from=2026-10-12&to=2026-10-18

FAQ

Questions builders ask.

Do the per-day counts match the list?

Yes. Calendar counts are exactly the occurrences /events?occurrences=expand returns for that date under the same filters.

How do I find out an event was removed?

Removals appear on /v1/changes even though the listing itself then returns 404. Drop it from your calendar when you see one.

Can a sync deliver a row twice?

Yes, changed_since is at least once. Upsert by id and you are safe.

How wide can a window be?

Up to 62 days on /events/calendar, and up to 366 days on /events. On /events, to defaults to from plus 30 days.

Can I show weather on the calendar?

Add include=forecast and days with a forecast carry one.

Build the calendar families keep open.

Try the requests above with sample data, then get a key from the quickstart.