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

# Twitter Keyword Monitor List & Tracked Searches

> List keyword monitors with X search queries, matching tweet event types, active states, polling intervals, billing state, and timestamps. See event fields.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-monitors-list-keywords-200">
      ```json theme={null}
      {
        "monitors": [],
        "total": 0
      }
      ```
    </Tab>

    <Tab title="401" id="response-monitors-list-keywords-401">
      ```json theme={null}
      {
        "error": "unauthenticated",
        "message": "Authentication required. Provide a valid API key or bearer token."
      }
      ```
    </Tab>

    <Tab title="429" id="response-monitors-list-keywords-429">
      ```json theme={null}
      {
        "error": "rate_limit_exceeded",
        "message": "Too many requests. Try again later.",
        "retryAfter": 60
      }
      ```
    </Tab>
  </Tabs>
</Panel>

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

## Reconcile the Entire Keyword Monitor Portfolio

Use this endpoint when the exact monitor ID is unknown. It returns up to 200
stored searches plus `total`. It does not paginate the inventory.

| Portfolio check        | Inventory calculation                  | Reconciliation decision                                  |
| ---------------------- | -------------------------------------- | -------------------------------------------------------- |
| Active searches        | Count `monitors[].isActive === true`   | Include only active rows in current polling reviews.     |
| Paused searches        | Count `monitors[].isActive === false`  | Preserve them for history or later reactivation.         |
| Duplicate queries      | Group by normalized `monitors[].query` | Compare `eventTypes` before treating rows as duplicates. |
| Webhook gaps           | Compare every `eventTypes` array       | Add only missing receiver subscriptions.                 |
| Billing order          | Sort active `nextBillingAt` values     | Review the nearest credit checkpoints first.             |
| Inventory completeness | Compare `monitors.length` with `total` | Escalate when the returned collection is incomplete.     |

Use the detail endpoint only after choosing one monitor ID from this inventory.

<Callout icon="circle-check" color="#16a34a">
  **Free** - does not consume credits
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET https://xquik.com/api/v1/monitors/keywords \
    -H "x-api-key: xq_YOUR_KEY_HERE" |
    jq -c '.monitors[] | {
      keyword_monitor_id: .id,
      query: .query,
      event_types: .eventTypes,
      is_active: .isActive,
      next_billing_at: .nextBillingAt,
      events_endpoint: "/api/v1/events?keywordMonitorId=\(.id)",
      event_detail_endpoint_pattern: "/api/v1/events/{event_id}",
      verify_endpoint: "/api/v1/monitors/keywords/\(.id)",
      update_endpoint: "/api/v1/monitors/keywords/\(.id)",
      delete_endpoint: "/api/v1/monitors/keywords/\(.id)",
      webhooks_endpoint: "/api/v1/webhooks",
      deliveries_endpoint_pattern: "/api/v1/webhooks/{webhook_id}/deliveries"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/monitors/keywords", {
    method: "GET",
    headers: { "x-api-key": "xq_YOUR_KEY_HERE" },
  });
  const payload = await response.json();
  for (const monitor of payload.monitors) {
    const monitorRow = {
      keyword_monitor_id: monitor.id,
      query: monitor.query,
      event_types: monitor.eventTypes,
      is_active: monitor.isActive,
      created_at: monitor.createdAt,
      next_billing_at: monitor.nextBillingAt,
      events_endpoint: `/api/v1/events?keywordMonitorId=${monitor.id}`,
      event_detail_endpoint_pattern: "/api/v1/events/{event_id}",
      verify_endpoint: `/api/v1/monitors/keywords/${monitor.id}`,
      update_endpoint: `/api/v1/monitors/keywords/${monitor.id}`,
      delete_endpoint: `/api/v1/monitors/keywords/${monitor.id}`,
      webhooks_endpoint: "/api/v1/webhooks",
      deliveries_endpoint_pattern: "/api/v1/webhooks/{webhook_id}/deliveries",
    };
    process.stdout.write(`${JSON.stringify(monitorRow)}\n`);
  }
  ```

  ```python Python theme={null}
  import json
  import requests

  response = requests.get(
      "https://xquik.com/api/v1/monitors/keywords",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
  )
  payload = response.json()
  for monitor in payload["monitors"]:
      monitor_row = {
          "keyword_monitor_id": monitor["id"],
          "query": monitor["query"],
          "event_types": monitor["eventTypes"],
          "is_active": monitor["isActive"],
          "created_at": monitor["createdAt"],
          "next_billing_at": monitor["nextBillingAt"],
          "events_endpoint": f"/api/v1/events?keywordMonitorId={monitor['id']}",
          "event_detail_endpoint_pattern": "/api/v1/events/{event_id}",
          "verify_endpoint": f"/api/v1/monitors/keywords/{monitor['id']}",
          "update_endpoint": f"/api/v1/monitors/keywords/{monitor['id']}",
          "delete_endpoint": f"/api/v1/monitors/keywords/{monitor['id']}",
          "webhooks_endpoint": "/api/v1/webhooks",
          "deliveries_endpoint_pattern": "/api/v1/webhooks/{webhook_id}/deliveries",
      }
      print(json.dumps(monitor_row))
  ```

  ```go Go theme={null}
  package main

  import (
      "encoding/json"
      "log"
      "net/http"
      "os"
  )

  type KeywordMonitor struct {
      ID            string   `json:"id"`
      Query         string   `json:"query"`
      EventTypes    []string `json:"eventTypes"`
      IsActive      bool     `json:"isActive"`
      CreatedAt     string   `json:"createdAt"`
      NextBillingAt string   `json:"nextBillingAt"`
  }

  type KeywordMonitorListResponse struct {
      Monitors []KeywordMonitor `json:"monitors"`
      Total    int              `json:"total"`
  }

  type KeywordMonitorRow struct {
      KeywordMonitorID           string   `json:"keyword_monitor_id"`
      Query                      string   `json:"query"`
      EventTypes                 []string `json:"event_types"`
      IsActive                   bool     `json:"is_active"`
      CreatedAt                  string   `json:"created_at"`
      NextBillingAt              string   `json:"next_billing_at"`
      EventsEndpoint             string   `json:"events_endpoint"`
      EventDetailEndpointPattern string   `json:"event_detail_endpoint_pattern"`
      VerifyEndpoint             string   `json:"verify_endpoint"`
      UpdateEndpoint             string   `json:"update_endpoint"`
      DeleteEndpoint             string   `json:"delete_endpoint"`
      WebhooksEndpoint           string   `json:"webhooks_endpoint"`
      DeliveriesEndpointPattern  string   `json:"deliveries_endpoint_pattern"`
  }

  func main() {
      req, err := http.NewRequest("GET", "https://xquik.com/api/v1/monitors/keywords", nil)
      if err != nil {
          log.Fatal(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")

      resp, err := http.DefaultClient.Do(req)
      if err != nil {
          log.Fatal(err)
      }
      defer resp.Body.Close()

      var payload KeywordMonitorListResponse
      if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
          log.Fatal(err)
      }
      encoder := json.NewEncoder(os.Stdout)
      for _, monitor := range payload.Monitors {
          row := KeywordMonitorRow{
              KeywordMonitorID:           monitor.ID,
              Query:                      monitor.Query,
              EventTypes:                 monitor.EventTypes,
              IsActive:                   monitor.IsActive,
              CreatedAt:                  monitor.CreatedAt,
              NextBillingAt:              monitor.NextBillingAt,
              EventsEndpoint:             "/api/v1/events?keywordMonitorId=" + monitor.ID,
              EventDetailEndpointPattern: "/api/v1/events/{event_id}",
              VerifyEndpoint:             "/api/v1/monitors/keywords/" + monitor.ID,
              UpdateEndpoint:             "/api/v1/monitors/keywords/" + monitor.ID,
              DeleteEndpoint:             "/api/v1/monitors/keywords/" + monitor.ID,
              WebhooksEndpoint:           "/api/v1/webhooks",
              DeliveriesEndpointPattern:  "/api/v1/webhooks/{webhook_id}/deliveries",
          }
          if err := encoder.Encode(row); err != nil {
              log.Fatal(err)
          }
      }
  }
  ```
</CodeGroup>

The cURL, Node.js, Python, and Go examples convert each keyword monitor into one
inventory row. Store `keyword_monitor_id`, `query`, `event_types`, `is_active`,
`next_billing_at`, `events_endpoint`, `event_detail_endpoint_pattern`,
`verify_endpoint`, `update_endpoint`, `delete_endpoint`, `webhooks_endpoint`,
and `deliveries_endpoint_pattern` before reconciling events, webhooks, or
paused queries.

## Inventory handoff

Use `GET /monitors/keywords` after create, update, pause, or delete operations
to rebuild your keyword monitor inventory. The response returns up to 200
keyword monitors ordered by creation time and a `total` count for the returned
set.

| Keyword monitor inventory column | Response source            | Reconciliation rule                             |
| -------------------------------- | -------------------------- | ----------------------------------------------- |
| Monitor ID                       | `monitors[].id`            | Use this ID for status, updates, and events.    |
| X search query                   | `monitors[].query`         | Preserve the exact stored keyword expression.   |
| Tweet event filter               | `monitors[].eventTypes`    | Compare these types with webhook subscriptions. |
| Polling state                    | `monitors[].isActive`      | Separate active and paused keyword monitors.    |
| Creation time                    | `monitors[].createdAt`     | Track configuration age and drift.              |
| Billing checkpoint               | `monitors[].nextBillingAt` | Schedule the next active monitor credit check.  |
| Inventory count                  | `total`                    | Compare this count with emitted monitor rows.   |

<CardGroup cols={2}>
  <Card title="Active Burn" icon="activity">
    Filter monitors where `isActive` is `true`. Each active keyword monitor
    bills 21 credits per active monitor-hour; use `nextBillingAt` to schedule
    credit checks or pause stale alerts.
  </Card>

  <Card title="Webhook Alignment" icon="webhook">
    Compare each monitor's `eventTypes` with
    [List Webhooks](/api-reference/webhooks/list) before relying on signed
    alerts.
  </Card>

  <Card title="Event Backfill" icon="database">
    Use `id` as `keywordMonitorId` with
    [List Events](/api-reference/events/list) to audit stored monitor events.
    Use [Get Event](/api-reference/events/get) for one event's full payload.
  </Card>

  <Card title="Delivery Audit" icon="activity">
    Use [List Deliveries](/api-reference/webhooks/deliveries) when webhook
    delivery evidence must be retained. Join delivery `streamEventId` to event
    IDs. Do not use `x_event_id` as the delivery join key.
  </Card>

  <Card title="Detail Check" icon="search-check">
    Use [Get Keyword Monitor](/api-reference/monitors/get-keyword) before
    changing a row. Store the returned `query`, `eventTypes`, `isActive`, and
    `nextBillingAt`.
  </Card>

  <Card title="State Repair" icon="sliders-horizontal">
    Use [Update Keyword Monitor](/api-reference/monitors/update-keyword) to
    replace `eventTypes` or toggle `isActive`. Use
    [Delete Keyword Monitor](/api-reference/monitors/delete-keyword) only when
    the query should stop permanently.
  </Card>
</CardGroup>

## Review a Portfolio of Tracked Search Rules

Use the returned list to inspect every stored Twitter keyword query. Start with
`total`, then count the emitted `monitors` rows. Store the snapshot time beside
both values. A later comparison needs a complete inventory.

Classify each query by its actual search intent. Separate brand handles,
product names, campaign hashtags, support phrases, and competitor terms. Keep
the original `query` text. Do not replace it with a dashboard label.

Group identical normalized queries before changing anything. Two active rows
can watch the same phrase with different event types. Compare `eventTypes`
before calling them duplicates. Preserve intentional routing differences.

Next, separate active and paused rows. Active rows can create new matching
events. Paused rows remain useful for history and later reactivation. Never
infer activity from an empty event window alone.

Compare every active row with webhook subscriptions. Flag missing event types,
unused webhook event types, and absent receiver ownership. Use the monitor ID
to inspect stored events. Use event IDs to inspect delivery attempts.

Finish with one inventory record per keyword monitor. Keep the monitor ID,
query, event types, active state, creation time, and billing checkpoint. Add
the responsible team and review date outside the API response.

## Prepare a Keyword Monitor Budget Report

Count only active keyword monitors when estimating hourly monitor use. Each
active monitor bills 21 credits per active monitor-hour. Listing the inventory
remains free.

Sort active rows by `nextBillingAt`. This reveals the next billing checkpoints
without changing monitor state. Join each row with its owner and campaign end
date. Pause expired campaigns through the update route.

Keep paused monitors outside the active-burn total. Keep deleted monitors
outside the current inventory. Preserve separate history when compliance or
support needs older event evidence.

Rebuild the report after every create, update, pause, resume, or delete. Compare
complete snapshots by monitor ID. Report new, reactivated, paused, and removed
queries separately. This prevents one aggregate count from hiding state
changes.

## Headers

<ParamField header="x-api-key" type="string" required>
  Your API key. Session cookie authentication is also supported. Generate a key from the [dashboard](https://xquik.com/dashboard).
</ParamField>

## Response

### 200 OK

<ResponseField name="monitors" type="object[]">Array of keyword monitor objects.</ResponseField>
<ResponseField name="monitors[].id" type="string">Unique keyword monitor ID.</ResponseField>
<ResponseField name="monitors[].query" type="string">Normalized X search query.</ResponseField>
<ResponseField name="monitors[].eventTypes" type="string[]">Subscribed event types.</ResponseField>
<ResponseField name="monitors[].isActive" type="boolean">Whether the monitor is currently active.</ResponseField>
<ResponseField name="monitors[].createdAt" type="string">ISO 8601 creation timestamp.</ResponseField>
<ResponseField name="monitors[].nextBillingAt" type="string">Next hourly credit charge time for active monitor billing.</ResponseField>
<ResponseField name="total" type="number">Total number of keyword monitors.</ResponseField>

```json theme={null}
{
  "monitors": [
    {
      "id": "21",
      "query": "xquik OR \"x api\"",
      "eventTypes": ["tweet.new"],
      "isActive": true,
      "createdAt": "2026-02-24T10:30:00.000Z",
      "nextBillingAt": "2026-02-24T11:30:00.000Z"
    }
  ],
  "total": 1
}
```

### 401 Unauthenticated

```json theme={null}
{ "error": "unauthenticated" }
```

Missing or invalid API key.

### 429 Rate Limited

```json theme={null}
{ "error": "rate_limit_exceeded", "message": "Too many requests. Try again later.", "retryAfter": 1 }
```

Too many requests. Wait for the `Retry-After` header before retrying.

<Info>
  Returns up to 200 keyword monitors. There is no pagination. [Contact support](mailto:support@xquik.com) if you need more.
</Info>

<Note>
  **Related:** [Create Keyword Monitor](/api-reference/monitors/create-keyword) to add a query, [Get Keyword Monitor](/api-reference/monitors/get-keyword) to fetch one monitor, [Update Keyword Monitor](/api-reference/monitors/update-keyword) to pause or edit it, [Delete Keyword Monitor](/api-reference/monitors/delete-keyword) to remove it, [List Events](/api-reference/events/list) and [Get Event](/api-reference/events/get) to audit events, or [List Webhooks](/api-reference/webhooks/list) and [List Deliveries](/api-reference/webhooks/deliveries) to audit delivery evidence.
</Note>
