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

# List events

> Cursor-paginated profile diffs visible on the authenticated organization's Lists and Monitored Target members.

Returns a newest-first, cursor-paginated stream of profile diffs for the authenticated organization. Without `list`, visibility is evaluated when the request runs and includes current `watching` members on the organization's Targets, shared default-orbit watches, and current members of the organization's Lists. With `list`, only members of that organization-owned List are considered. This is the membership-scoped Monitoring stream, not the graph-wide email or Slack notification fan-out.

Each response row is one stored profile diff. The API does not publish a retention SLA or delivery guarantee. The same person can appear more than once, and membership changes can change which events are visible on later requests.

## Query parameters

<ParamField query="since" type="string">
  Optional lower bound. Use an ISO-8601 date/time or a relative window written as digits plus one unit: `m` (minutes), `h` (hours), `d` (days), or `w` (weeks), for example `24h` or `2w`. Events satisfy `seen_at >= since`. There is no default when omitted. For pagination, compute an **absolute timestamp** at the start of a pass and reuse it for every page. Repeating `since=24h` recalculates the lower bound on each request.
</ParamField>

<ParamField query="type" type="string">
  Optional exact field filter: `open_to_work`, `between_roles`, `exploring`, `left_company`, `joined_company`, `removed_position`, `headline`, `about`, or `role_description`.
</ParamField>

<ParamField query="list" type="string">
  Optional List selector, at least one character: a List UUID or a case-insensitive List name. A missing List returns `404`.
</ParamField>

<ParamField query="cursor" type="string">
  Optional opaque value copied from the previous page's `meta.next`. It pages toward older events in the same pass. It is not a future-event polling cursor and must not replace a new `since` value on the next scheduled poll.
</ParamField>

<ParamField query="limit" type="integer" default="25">
  Number of rows, from `1` through `100` inclusive.
</ParamField>

Only the documented query parameters are supported. The current framework strips unknown query fields rather than returning a validation error; do not rely on this behavior to detect typos. `since`, `type`, `list`, and `cursor` must be non-empty when supplied.

## Response

`200` returns `data` plus `meta`. `before`, `after`, and every field in `person` except `linkedin_url` are nullable. `meta.next` is `null` when there is no next page.

```json 200 theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "data": [
    {
      "id": "3b241101-e2bb-4255-8caf-4136c566a962",
      "field": "left_company",
      "before": "Palantir",
      "after": "Anduril",
      "seen_at": "2026-08-23T10:00:00.000Z",
      "severity": "hot",
      "source": "snapshot_diff",
      "label": "Left Palantir → Anduril",
      "person": {
        "slug": "derek-morrow",
        "name": "Derek Morrow",
        "headline": null,
        "linkedin_url": "https://www.linkedin.com/in/derek-morrow",
        "current_company": "Anduril"
      }
    }
  ],
  "meta": {
    "count": 1,
    "limit": 25,
    "has_more": false,
    "next": null,
    "since": "2026-08-22T10:00:00.000Z"
  }
}
```

`severity` is `hot` or `quiet`. `source` is `ingest`, `snapshot_diff`, or `demo`. `meta.since` is the normalized ISO-8601 lower bound, or `null` when no `since` was supplied.

## Ordering, cursors, and polling

Events are ordered by `seen_at DESC, id DESC`. The cursor uses that pair to select strictly older rows; equal timestamps are ordered by UUID. Keep `since`, `type`, and `list` fixed throughout a pass. `meta.count` is the number of rows on this page, not a total. A `200` with `data: []` is valid.

| Field                      | Type           | Meaning                                                                |
| -------------------------- | -------------- | ---------------------------------------------------------------------- |
| `id`                       | string (UUID)  | Stored profile-diff ID; deduplication key                              |
| `field`                    | string enum    | One of the supported `type` values                                     |
| `before`, `after`          | string or null | Recorded values; not nested JSON objects                               |
| `seen_at`                  | string         | ISO timestamp of the recorded diff                                     |
| `severity`, `source`       | string enum    | Values listed above                                                    |
| `label`                    | string         | Human-readable change description                                      |
| `person`                   | object         | Display metadata and required `linkedin_url`; other values may be null |
| `meta.count`, `meta.limit` | integer        | Returned row count and page size                                       |
| `meta.has_more`            | boolean        | Whether an older page is available                                     |
| `meta.next`                | string or null | Opaque older-page cursor                                               |
| `meta.since`               | string or null | Normalized lower bound                                                 |

Checkpoint the **pass start time only after all pages and downstream writes succeed**. Use an idempotent destination or transactional outbox to handle a crash between the external write and checkpoint. There is no snapshot isolation across pages; changed membership or late-arriving diffs can affect a later pass.

Use a fixed lower bound for a complete pass, follow `meta.next` until `has_more` is false, and persist `event.id` as the event deduplication key. Start the next scheduled pass with a new lower bound based on the last successful checkpoint and a small overlap, such as five minutes. The overlap protects against boundary and clock differences; UUID deduplication makes the replay safe. Never carry the final page cursor into the next pass.

This read is safe to retry on a transient network failure, `429`, or `5xx` with bounded exponential backoff and jitter. Honor `Retry-After`. Recheck the request and fix the cause for `400`, `401`, `402`, or `404` instead of retrying unchanged.

## Errors

| Status | Meaning                                              | Recovery                                                                                   |
| ------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `400`  | Invalid `since`, enum, bounds, or cursor             | Correct the request; do not retry unchanged.                                               |
| `401`  | Missing, malformed, or revoked API key               | Supply a valid key.                                                                        |
| `402`  | Organization trial ended or subscription is required | Resolve organization access.                                                               |
| `404`  | The requested List is not found in this organization | Check the UUID/name and organization scope.                                                |
| `429`  | Shared Gateway limit exceeded                        | Honor `Retry-After` and back off.                                                          |
| `5xx`  | Gateway or dependency failure                        | Retry the bounded read with backoff; if completeness matters, rerun the pass with overlap. |

The Gateway's default rate limit is 300 requests per minute per client IP, not per API key. Framework errors can use a different envelope; branch on HTTP status first.

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --fail-with-body --max-time 30 --get "https://api.freshtalent.ai/v1/events" \
    --data-urlencode "since=24h" \
    --data-urlencode "type=left_company" \
    --data-urlencode "limit=100" \
    -H "Authorization: Bearer $FRESHTALENT_API_KEY"
  ```

  ```js JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const params = new URLSearchParams({ since: "24h", type: "left_company", limit: "100" });
  const response = await fetch(`https://api.freshtalent.ai/v1/events?${params}`, {
    signal: AbortSignal.timeout(30_000), headers: { Authorization: `Bearer ${process.env.FRESHTALENT_API_KEY}` },
  });
  if (!response.ok) throw new Error(`FreshTalent HTTP ${response.status}`);
  const page = await response.json();
  for (const event of page.data) console.log(event.id, event.person.linkedin_url);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import json
  import os
  from urllib.parse import urlencode
  from urllib.request import Request, urlopen

  query = urlencode({"since": "24h", "type": "left_company", "limit": 100})
  request = Request(
      "https://api.freshtalent.ai/v1/events?" + query,
      headers={"Authorization": "Bearer " + os.environ["FRESHTALENT_API_KEY"]},
  )
  with urlopen(request, timeout=30) as response:
      page = json.load(response)
  for event in page["data"]:
      print(event["id"], event["person"]["linkedin_url"])
  ```
</RequestExample>

## Related

* [Get account](/api-reference/account/me) for organization access and `watch_slots`.
* [Get notification preferences](/api-reference/signals/get-preferences) and [Update notification preferences](/api-reference/signals/update-preferences) for graph-wide delivery settings.
* [Build your first integration](/guides/first-integration) for a tested polling script.
* [ATS/CRM architecture](/connect/ats-crm) for a polling integration.
* [Errors](/errors) and [Rate limits](/rate-limits) for shared retry rules.
