# Calendar API

> Earnings schedule API: report dates with session timing, fiscal quarter, confirmation status, and consensus EPS estimates.

Base URL: `https://app.sentisense.ai/api/v1/calendar`
Tier: PRO (free preview available)

All API access requires an API key via the `X-SentiSense-API-Key` header. Keys look like `ss_live_...`.

> SDKs: [Python](https://github.com/SentiSenseApp/sentisense#earnings) · [Node](https://github.com/SentiSenseApp/sentisense-node#earnings)

## Overview

The Calendar API exposes scheduled market events. It currently includes a discovery endpoint and an earnings calendar with report dates, session timing, fiscal quarters, confirmation status, and consensus EPS estimates.

**Use cases:**
- Build a pre-earnings watchlist: which of my tickers report in the next two weeks?
- Screen for catalysts in a date window (e.g. all confirmed reports between two dates)
- Feed earnings dates into a research agent so it knows when a thesis has a catalyst
- Cross-reference upcoming earnings with sentiment, insider, and institutional signals
- Separate before-open vs after-close reporters for session-aware planning

**Scope: schedule data only.** The default lower bound is the beginning of the current week, so the response can include dates that have already passed. An earlier `from` bound can reach older schedule rows that remain in the current snapshot. These rows contain schedule fields only, never actual reported results. Use the [Earnings Analysis API](/docs/api/earnings) for reported quarters: `GET /api/v1/earnings/recent` lists recent reports, and `GET /api/v1/stocks/{ticker}/earnings-summaries` returns the quarter's headline, KPI highlights, guidance, and call summary. For standardized figures use `GET /api/v1/stocks/fundamentals` and `GET /api/v1/stocks/{ticker}/kpis`.

**Access:** Every call requires an API key. FREE-tier keys receive the first week of the requested window with `isPreview: true`. PRO keys receive all matching rows available in the current snapshot. With no date parameters, the public lower bound is the Monday of the current US Eastern week. Both tiers receive the same event fields. Anonymous calls return `401 api_key_required`.

Working in Python? Five runnable scripts: [Python earnings examples](/blog/python-earnings-examples/).

---

## GET /api/v1/calendar

Discovery endpoint. Lists the calendars exposed by this family so an agent can find them without hardcoding paths.

**Authentication:** API key required. This call does not consume monthly quota (metadata only); the per-minute rate limit still applies.

**Parameters:** none.

**Example Request:**

```bash
curl -H "X-SentiSense-API-Key: ss_live_YOUR_KEY" \
  "https://app.sentisense.ai/api/v1/calendar"
```

**Example Response:**

```json
{
  "calendars": [
    {
      "type": "earnings",
      "path": "/api/v1/calendar/earnings",
      "description": "Upcoming company earnings dates (forward window)"
    }
  ]
}
```

---

## GET /api/v1/calendar/earnings

Returns company earnings schedule rows that match the requested filters. Each entry carries the report date, session timing, fiscal quarter, confirmation status, and consensus EPS estimate.

By default the response covers the current week onward, measured from the Monday of the current US Eastern week rather than from today, so it can include dates earlier in the week that have already passed. To reach further back than the current week, pass an explicit `from`. Entries are schedule data in every case (report date, session timing, fiscal quarter, confirmation status, consensus EPS) and never carry what a company actually reported.

**Authentication:** API key required. FREE tier returns the first week of the requested window with `isPreview: true`; PRO returns all matching rows available in the current snapshot. `metadata.windowStart` and `metadata.windowEnd` report the resolved response window, and `metadata.count` always equals `data.earnings.length`.

**Parameters:**

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `ticker` | string | No | - | Filter to a single ticker (e.g. `AAPL`) |
| `week` | string | No | - | Shorthand date window. `this` is the Monday-to-Sunday week containing the current US Eastern (America/New_York) date; `next` is the seven days immediately after that week ends; `last` is the seven days immediately before it starts. The window rolls over at midnight Eastern. Used only when both `from` and `to` are absent |
| `from` | string | No | start of current week | Inclusive lower date bound, ISO `YYYY-MM-DD`. Pass an earlier date to reach older schedule rows still present in the current snapshot |
| `to` | string | No | - | Inclusive upper date bound, ISO `YYYY-MM-DD`. Setting `to` equal to `from` is a valid single-day query |
| `confirmed` | boolean | No | - | Two-sided. `true` returns only company-confirmed dates, `false` returns only the still-estimated ones. Omit it to get both |
| `time` | string | No | - | Filter by session: `before_open`, `after_close`, `during_market`, or `unknown` |

**Example Request:**

```bash
curl -H "X-SentiSense-API-Key: ss_live_YOUR_KEY" \
  "https://app.sentisense.ai/api/v1/calendar/earnings?week=next&confirmed=true"
```

```python
from sentisense import SentiSenseClient

client = SentiSenseClient(api_key="ss_live_YOUR_KEY")
cal = client.get_earnings_calendar(week="next")
for e in cal.earnings:
    eps = f"est. ${e['estimatedEps']}" if e.get("estimatedEps") is not None else "no estimate"
    print(f"{e['earningsDate']} {e['ticker']} ({e['earningsTime']}) {eps}")
```

**Response Schema:**

| Field | Type | Description |
|-------|------|-------------|
| `isPreview` | boolean | `true` when limited to one week (FREE tier) |
| `previewReason` | string | `"PRO_REQUIRED"` or `null` |
| `totalCount` | int | On a preview, the number of matching events before the FREE one-week limit |
| `upgrade` | object | Present only when `isPreview` is `true`. Carries `plan`, `message`, `price`, `url` and `relay`: surface `message` and `url` to your user in one line, then continue with the preview `data` |
| `data` | object | `{ earnings: [...], metadata: {...} }` |

**Earnings event object:**

| Field | Type | Description |
|-------|------|-------------|
| `ticker` | string | Stock ticker symbol |
| `companyName` | string | Company name |
| `earningsDate` | string | Report date, ISO `YYYY-MM-DD` |
| `earningsTime` | string | `before_open`, `after_close`, `during_market`, or `unknown`. Never null or absent. `unknown` means no session claim applies, either because the timing is unpublished or because none is possible: some issuers release on a Saturday or Sunday ahead of a Monday call, and a weekend has no open or close for the report to sit against. A weekend `earningsDate` is legitimate data, not an error to filter out |
| `fiscalQuarter` | string \| null | Fiscal quarter the report covers, in the company's own fiscal calendar (e.g. `Q2 2026`), or `null` when it cannot be determined, as for a recent listing |
| `confirmed` | boolean | Whether the company has confirmed the date (vs. estimated/projected) |
| `estimatedEps` | number | Consensus EPS estimate, nullable |

Session timing is filled in for reports in the current and next week; reports further out usually carry `earningsTime: "unknown"` until their timing is published.

**Metadata object:**

| Field | Type | Description |
|-------|------|-------------|
| `generatedAt` | int \| null | Snapshot build time in epoch seconds, or null if unavailable; it is not a content revision, so compare event rows to detect changes |
| `windowStart` | string \| null | Inclusive lower bound of the resolved response window, ISO `YYYY-MM-DD` |
| `windowEnd` | string \| null | Inclusive upper bound of the resolved response window, ISO `YYYY-MM-DD` |
| `count` | int | Number of events in this response |
| `source` | string | Always `"sentisense"` |

**Example Response (PRO):**

```json
{
  "isPreview": false,
  "previewReason": null,
  "data": {
    "earnings": [
      {
        "ticker": "AAPL",
        "companyName": "Apple Inc.",
        "earningsDate": "2026-04-30",
        "earningsTime": "after_close",
        "fiscalQuarter": "Q2 2026",
        "confirmed": true,
        "estimatedEps": 1.62
      },
      {
        "ticker": "MSFT",
        "companyName": "Microsoft Corp.",
        "earningsDate": "2026-04-29",
        "earningsTime": "after_close",
        "fiscalQuarter": "Q3 2026",
        "confirmed": false,
        "estimatedEps": 3.05
      }
    ],
    "metadata": {
      "generatedAt": 1776528000,
      "windowStart": "2026-04-20",
      "windowEnd": "2026-05-20",
      "count": 2,
      "source": "sentisense"
    }
  }
}
```

FREE tier: same shape with `isPreview: true`, `previewReason: "PRO_REQUIRED"`, a `totalCount` of the full-window event count, and `data.earnings` limited to one week (the first week of the window you requested).

**Errors:**

These are the query-validation failures. Each returns `400` with
`{"error": "invalid_parameter", "message": "..."}`:

| Condition | Message |
|-----------|---------|
| `time` is not one of the four session values | `time must be one of [...]` |
| `week` is not `this`, `next` or `last` | `week must be 'this', 'next' or 'last'` |
| `from` or `to` is not an ISO `YYYY-MM-DD` date | `from/to must be ISO dates (YYYY-MM-DD)` |
| `to` is earlier than `from`, with both supplied explicitly | `to must not be before from` |
| `confirmed` cannot be converted to a boolean | `Parameter 'confirmed' must be of type Boolean` |

Authentication and rate limiting are separate from query validation. A request with no key returns
`401` before controller validation, and one past the per-minute limit returns `429` with
`Retry-After: 60`.

Additional behavior:

- **Unknown query parameters are ignored**, not rejected. `?typo=value` returns the same
  calendar as the request without it, so a misspelled filter silently does nothing rather
  than narrowing the result or erroring.
- **`from` and `to` accept an empty value as absent**, not as a malformed date. Sending
  `?from=` is the same as omitting it, and the default lower bound still applies.
- **A filter that matches no reports is a `200`**, with an empty `earnings` array and
  `count: 0`. A ticker, date range, or filter combination with no matching row is not an error.

### Coverage

The calendar covers the curated US equity universe, not the whole market. `GET /api/v1/stocks`
returns the current covered ticker list. Use that list when interpreting calendar results:

- A ticker present in `/api/v1/stocks` can still have no matching calendar row in the current
  snapshot, date window, or filter set. An empty result does not establish that no report is scheduled.
- A ticker absent from `/api/v1/stocks` is outside current coverage, so no calendar row is expected
  for it in any window. If you need a ticker we do not cover, ask us and we will look at adding it.

### Identifying events and detecting changes

Events have no id or revision counter. Use `ticker` to correlate rows across snapshots, then compare
the full rows. A later snapshot can change any schedule field or omit a row.
`metadata.generatedAt` reports build time, not a content revision.

There is no cancellation or postponement status in an event. If a row disappears, the response does
not distinguish cancellation, postponement, expiry from the snapshot window, or a coverage change.
An unconfirmed date remains estimated regardless of how long it persists.

### Refresh and caching

The calendar is rebuilt a few times a week, and dates change in between when companies announce.
No fixed schedule is guaranteed, so polling once a day is ample for a pre-earnings workflow and
polling faster will not surface dates sooner.

`metadata.generatedAt` is the build time carried by the current snapshot. It is not a content
revision, so treat a moved value as "worth re-reading" rather than an unchanged value as proof
that nothing changed.

Cache responses for as long as your application needs them. The constraint is redistribution, not
retention: see the [API Terms of Service](https://sentisense.ai/agreement/api-terms/).

Related: [Earnings Analysis API](/docs/api/earnings) for the per-quarter analysis of reports already filed.

Comparing providers? See [the best earnings calendar APIs in 2026](/blog/best-earnings-calendar-apis-2026/).
