# List watches

> Page through scheduled watches newest first, with status filters and cursor pagination.

`GET /v1/watches` returns the owner's watches, newest first, with cursor pagination.

## Tenant scope

Restricted (`sk_*`) keys see only their own watches. Internal keys can act on behalf of one tenant for watch reads **and writes** by sending `?owner=<ownerId>` or `X-AISearch-On-Behalf-Of: <ownerId>`; the query parameter wins when both are present. A restricted key's override is ignored.

When an override is applied, the response adds a top-level `owner` field and an `X-AISearch-Owner` header containing the enforced owner id.

## Query parameters

<ParamField query="limit" type="integer" default="20">
  Page size, `1`–`100`.
</ParamField>

<ParamField query="status" type="string">
  Filter by `active`, `paused`, or `canceled`.
</ParamField>

<ParamField query="cursor" type="string">
  Opaque cursor from a previous page's `nextCursor`. Omit for the first page.
</ParamField>

<ParamField query="owner" type="string">
  Internal keys only. Owner id to enforce for this read. Overrides the
  `X-AISearch-On-Behalf-Of` header when both are sent.
</ParamField>

## Request

<CodeGroup>

```bash cURL
curl "https://api.aisearchapi.dev/v1/watches?limit=20&status=active" \
  -H "Authorization: Bearer $AISEARCH_API_KEY"
```

```javascript Node
const res = await fetch(
  'https://api.aisearchapi.dev/v1/watches?limit=20&status=active',
  { headers: { Authorization: `Bearer ${process.env.AISEARCH_API_KEY}` } },
);
const page = await res.json();
```

```python Python
import os, requests

res = requests.get(
    "https://api.aisearchapi.dev/v1/watches",
    params={"limit": 20, "status": "active"},
    headers={"Authorization": f"Bearer {os.environ['AISEARCH_API_KEY']}"},
)
page = res.json()
```

</CodeGroup>

## Response

```json 200 OK
{
  "items": [
    {
      "id": "watch_8f2c1a7e",
      "name": "Startup CRM monitor",
      "query": "best crm for startups",
      "surfaces": ["chatgpt", "perplexity"],
      "regions": [{ "country": "US" }],
      "extract": { "targets": ["HubSpot", "attio.com"] },
      "webhookUrl": "https://example.com/hooks/aisearch",
      "schedule": { "every": "1d", "intervalMinutes": 1440 },
      "status": "active",
      "pausedReason": null,
      "nextRunAt": "2026-07-12T14:00:00Z",
      "lastRunAt": "2026-07-11T14:00:00Z",
      "lastJobId": "job_8t2q",
      "createdAt": "2026-07-01T14:00:00Z"
    }
  ],
  "nextCursor": "eyJvZmZzZXQiOjIwfQ"
}
```

<ResponseField name="items" type="object[]">
  Public Watch objects, newest first.

  <Expandable title="item">
    <ResponseField name="id" type="string">Watch id (`watch_...`).</ResponseField>
    <ResponseField name="name" type="string">Display name.</ResponseField>
    <ResponseField name="query" type="string">Saved query.</ResponseField>
    <ResponseField name="surfaces" type="string[]">Surfaces run on each tick.</ResponseField>
    <ResponseField name="regions" type="object[]">Saved region objects.</ResponseField>
    <ResponseField name="extract" type="object | null">Saved extraction config, or `null`.</ResponseField>
    <ResponseField name="webhookUrl" type="string | null">Delivery destination, or `null`.</ResponseField>
    <ResponseField name="schedule" type="object">`every` plus source-of-truth `intervalMinutes`.</ResponseField>
    <ResponseField name="status" type="string">`active`, `paused`, or `canceled`.</ResponseField>
    <ResponseField name="pausedReason" type="string | null">Why the watch is paused, when present.</ResponseField>
    <ResponseField name="nextRunAt" type="string">Next scheduled tick, ISO-8601.</ResponseField>
    <ResponseField name="lastRunAt" type="string | null">Most recent tick, or `null`.</ResponseField>
    <ResponseField name="lastJobId" type="string | null">Most recent parent job id, when present.</ResponseField>
    <ResponseField name="createdAt" type="string">Creation time, ISO-8601.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="nextCursor" type="string | null">
  Pass as `?cursor=` to fetch the next page. `null` on the last page.
</ResponseField>

## Paginate to the end

<CodeGroup>

```bash cURL
cursor=""
while true; do
  url="https://api.aisearchapi.dev/v1/watches?limit=100"
  [ -n "$cursor" ] && url="$url&cursor=$cursor"
  page=$(curl -s "$url" -H "Authorization: Bearer $AISEARCH_API_KEY")
  printf '%s\n' "$page" | jq -r '.items[] | [.id, .status] | @tsv'
  cursor=$(printf '%s\n' "$page" | jq -r '.nextCursor // empty')
  [ -z "$cursor" ] && break
done
```

```javascript Node
async function* allWatches() {
  let cursor;
  do {
    const url = new URL('https://api.aisearchapi.dev/v1/watches');
    url.searchParams.set('limit', '100');
    if (cursor) url.searchParams.set('cursor', cursor);
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.AISEARCH_API_KEY}` },
    });
    const page = await res.json();
    yield* page.items;
    cursor = page.nextCursor;
  } while (cursor);
}

for await (const watch of allWatches()) console.log(watch.id, watch.status);
```

```python Python
import os, requests

def all_watches():
    cursor = None
    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor
        res = requests.get(
            "https://api.aisearchapi.dev/v1/watches",
            params=params,
            headers={"Authorization": f"Bearer {os.environ['AISEARCH_API_KEY']}"},
        )
        page = res.json()
        yield from page["items"]
        cursor = page["nextCursor"]
        if not cursor:
            break

for watch in all_watches():
    print(watch["id"], watch["status"])
```

</CodeGroup>

## Errors

<ResponseField name="401" type="AUTH_INVALID">
  The API key is missing, invalid, or revoked.
</ResponseField>

```json 401
{
  "code": "AUTH_INVALID",
  "error": "missing or invalid API key; send Authorization: Bearer <API_KEY>",
  "request_id": "req_9f2c1a7e4b5d48f1a3c6e8d0b2a4f6c8",
  "docs_url": "https://docs.aisearchapi.dev/guides/errors#auth_invalid"
}
```

## Related

<CardGroup cols={2}>
  <Card title="Watches" icon="satellite-dish" href="/guides/watches">
    Scheduling, lifecycle, billing, and run history.
  </Card>
  <Card title="Create a watch" icon="plus" href="/api-reference/watches/create">
    Save a new scheduled query.
  </Card>
  <Card title="Get a watch" icon="file-lines" href="/api-reference/watches/get">
    Read one watch and its ten most recent runs.
  </Card>
  <Card
    title="Watch runs"
    icon="clock-rotate-left"
    href="/api-reference/watches/runs"
  >
    Page through full run history.
  </Card>
</CardGroup>
