# Event counts per day

> For each local date in [from, to] (both required, at most 62 days inclusive), how many event occurrences start that day, how many of them are free, and how many fall in each category — every date included, with zeros.

Source: https://api.tailend.dev/docs/api/get-event-calendar

```http
GET https://api.tailend.dev/v1/{metro}/events/calendar
X-API-Key: $TAILEND_API_KEY
```

For each local date in `[from, to]` (both required, at most 62 days
inclusive), how many event occurrences start that day, how many of them
are free, and how many fall in each category — every date included,
with zeros. Occurrences are exactly the ones `/v1/{metro}/events` lists
with `occurrences=expand` under the same filters, so a month view's
counts always agree with the week or day list behind them.

Takes the same filters as `/events`. `weekday`, `start_after`,
`start_before` and `season` apply to each occurrence. `cursor`, `page`,
`fields`, `expand`, `changed_since`, `q` and `facets` are not available.

`include=forecast` (a paid add-on, entitled as on `/events`) adds a
`forecast` to today and each of the next seven days.

Authorization: send your key in the `X-API-Key` header. Available on every plan.

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `metro` | string | yes | Metro slug from /v1/metros (e.g. philadelphia). |

## Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | string (date) | yes | First local date of the window, YYYY-MM-DD. |
| `to` | string (date) | yes | Last local date of the window (inclusive), at most 61 days after from. |
| `min_confidence` | number | no | Drop rows whose confidence is below this. |
| `category` | array of string | no | Keep rows whose categories contain any of these. |
| `audience` | array of string | no | Keep rows whose audience is one of these. |
| `season` | array of string | no | Events, classes and camps only. |
| `occasion` | string | no | Events and classes tagged for a seasonal occasion. |
| `team` | string | no | Events built around a local pro sports team (watch parties, game-day themed events, team or mascot appearances). |
| `age` | integer | no | A child's age in years. |
| `neighborhood` | string | no | Neighborhood slug or free-text name (case-insensitive). |
| `lat` | number | no | Center latitude for a radius search. |
| `lng` | number | no | Center longitude for a radius search. |
| `radius_m` | integer | no | Radius in metres around lat/lng (or near_zip, where enabled). |
| `zip` | array of string | no | Beta — may not be enabled for all keys yet. |
| `near_zip` | string | no | Beta — may not be enabled for all keys yet. |
| `include_approximate` | boolean | no | Beta — may not be enabled for all keys yet. |
| `place_id` | string (uuid) | no | Keep rows held at this place (place.id). |
| `free` | boolean | no | Events, classes, and camps only; ignored for places. |
| `has_time` | boolean | no | true keeps only events with a stated clock time. |
| `recurring` | boolean | no | Filter on is_recurring. |
| `weekday` | array of string | no | Keep rows on any of these local days. |
| `start_after` | string | no | Local start time at or after this, HH:MM (or HH:MM:SS). |
| `start_before` | string | no | Local start time strictly before this, HH:MM; 09:00–12:00 is the morning and 12:00 belongs to the afternoon. |
| `max_price` | number | no | A budget. |
| `organization_id` | string (uuid) | no | Keep rows published by this organization (organization.id). |
| `include` | array of string | no | Opt into a block the lean default response leaves out. |
| `include_mode` | string | no | How an include= the key is not entitled to is handled. |

## Example request

```bash
curl -sS \
  -H "X-API-Key: $TAILEND_API_KEY" \
  "https://api.tailend.dev/v1/philadelphia/events/calendar?from=2026-10-01&to=2026-10-31"
```

## Responses

### 200

One entry per date in the window.

```json
{
  "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
      },
      "forecast": {
        "summary": "Chance Showers",
        "temperature_f": {
          "min": 54,
          "max": 66
        },
        "precipitation_chance_max": 60,
        "wet_hours": [
          [
            13,
            16
          ]
        ],
        "issued_at": "2026-10-05T14:00:00Z",
        "expires_at": "2026-10-05T15:00:00Z"
      }
    }
  ],
  "meta": {
    "metro": "philadelphia",
    "window": {
      "from": "2026-10-05",
      "to": "2026-10-07",
      "timezone": "America/New_York"
    },
    "min_confidence": 0.7,
    "total": 98,
    "applied_filters": {
      "from": "2026-10-05",
      "to": "2026-10-07",
      "min_confidence": 0.7,
      "audience": [
        "not_adults"
      ]
    },
    "defaults_applied": [
      "min_confidence",
      "audience"
    ],
    "generated_at": "2026-10-07T12:00:00.000Z"
  }
}
```

### 400

Malformed or contradictory parameters (lat without lng, to before from, unknown category), or a cursor this API did not issue for this exact request — replayed from another endpoint, resource, metro or filter set, hand-crafted, or altered.

```json
{
  "error": {
    "code": "bad_request",
    "message": "radius_m: must be at least 100",
    "details": {
      "param": "radius_m"
    },
    "request_id": "req_dfbc3295a8382b05"
  }
}
```

### 401

Missing, unknown or revoked X-API-Key.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "missing X-API-Key header",
    "request_id": "req_90e63908273f6bff"
  }
}
```

### 403

Two different reasons share this status.

```json
{
  "error": {
    "code": "forbidden",
    "message": "key is not scoped to metro 'boston'",
    "details": {
      "metros": [
        "philadelphia"
      ]
    },
    "request_id": "req_9e7a750acef48ab2"
  }
}
```

### 404

No published, active resource with that id (or unknown metro).

```json
{
  "error": {
    "code": "not_found",
    "message": "unknown metro 'boston'",
    "request_id": "req_7431b51e10ec2b77"
  }
}
```

### 405

The path exists; the method does not.

```json
{
  "error": {
    "code": "method_not_allowed",
    "message": "POST is not allowed on this path; this API is read-only",
    "details": {
      "allow": [
        "GET",
        "HEAD",
        "OPTIONS"
      ]
    },
    "request_id": "req_01J9Z3"
  }
}
```

### 429

A limit was reached.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "3 requests per minute exceeded",
    "request_id": "req_06bfd3c2d5e8aa9f"
  }
}
```

### 500

A bug on our side.

```json
{
  "error": {
    "code": "internal",
    "message": "internal error",
    "request_id": "req_01J9Z3"
  }
}
```

### 501

This endpoint or feature is not available and will not become available without a configuration change — for ?address=, this means no geocoder is configured.

```json
{
  "error": {
    "code": "not_implemented",
    "message": "this endpoint is not enabled on this deployment; it is not a fault and retrying will not help",
    "request_id": "req_01J9Z3"
  }
}
```

### 503

A transient outage: a dependency could not be reached or did not answer in time.

```json
{
  "error": {
    "code": "unavailable",
    "message": "the service is temporarily unable to serve this request; retry after the interval in Retry-After",
    "request_id": "req_01J9Z3"
  }
}
```
