> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shoal.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Efficient Polling with since

> How to use the since parameter to only fetch new events

## Overview

The `since` parameter lets you fetch only events **newer** than a given timestamp instead of re-fetching everything.

<Note>
  `since` is **required** on `/v1/radar/all` and `/v1/signal/all`. Those bulk feed endpoints may be limited to higher-tier plans. It is optional on filtered endpoints like `byOrganizationId` and `byCategory`.
</Note>

<Warning>
  `/v1/radar/all` and `/v1/signal/all` are recent-monitoring surfaces. Their `since` value cannot be older than 7 days. For deeper history, use scoped routes or contact Shoal about export access.
</Warning>

If you need a merged premium historical surface across both feeds, use
`/v1/replay/global` instead of trying to stretch the bulk feed endpoints into a
replay API.

## How It Works

1. **First call** - fetch recent events:

```bash theme={null}
curl "https://api.shoal.xyz/v1/signal/all?since=2026-04-04T00:00:00Z" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

2. **Save the cursor** - note the `latestPostTimestamp` from the most recent event in the response (e.g. `2026-02-28T06:18:44Z`).

3. **Poll for updates** - pass it as the `since` parameter:

```bash theme={null}
curl "https://api.shoal.xyz/v1/signal/all?since=2026-02-28T06:18:44Z" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

4. **Handle the response** - if `data` is empty, nothing new has happened. Otherwise, process the new events and update your cursor.

If you only need one organization or one category, prefer the filtered endpoints instead of the bulk feed. They are usually a better fit for operational monitoring.

## Supported Endpoints

| Endpoint                          | `since`      |
| --------------------------------- | ------------ |
| `GET /v1/radar/all`               | **Required** |
| `GET /v1/signal/all`              | **Required** |
| `GET /v1/radar/byOrganizationId`  | Optional     |
| `GET /v1/radar/byCategory`        | Optional     |
| `GET /v1/signal/byOrganizationId` | Optional     |
| `GET /v1/signal/byCategory`       | Optional     |

## Format

The `since` parameter accepts any valid ISO 8601 timestamp:

* `2026-02-28T06:18:44Z`
* `2026-02-28T06:18:44+00:00`
* `2026-02-28`

Invalid timestamps (e.g. `since=garbage`) are rejected with `400 Bad Request`.

## Recommended Pattern

```javascript theme={null}
const API_KEY = process.env.SHOAL_API_KEY;
let cursor = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); // start 24h ago

async function poll() {
  const url = `https://api.shoal.xyz/v1/signal/all?since=${cursor}`;

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  const { data } = await res.json();

  if (data.length > 0) {
    // Update cursor to the newest event's timestamp
    cursor = data[0].latestPostTimestamp;
    // Process new events...
  }
}

// Poll every 60 seconds
setInterval(poll, 60_000);
```

## Cursor-Based Pagination

If a poll returns a full page of results, the response includes a `next_cursor` field. You can pass this as the `cursor` query parameter (along with the same `since` value) to fetch the next page without using `offset`. This is especially useful when there are more results than offset limits allow. See the [Pagination Guide](/guides/pagination) for details.

## Tips

* **Recommended polling interval:** 60 seconds for most use cases
* **Combine with `limit`:** Use `since` together with `limit` and `offset` for paginated polling, or use `cursor` for cursor-based pagination
* **First call:** use a reasonable recent start time (for example 24 hours ago), not an older historical date
