> ## 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 Updates & Alert Controls

> Update a keyword monitor's matching tweet event types, polling state, and active status without recreating its X search query. Includes request fields.

<Panel>
  <Tabs defaultTabIndex={0} sync={false}>
    <Tab title="200" id="response-monitors-update-keyword-200">
      ```json theme={null}
      {
        "id": "21",
        "query": "xquik OR \"x api\"",
        "eventTypes": [
          "tweet.new"
        ],
        "isActive": true,
        "createdAt": "2025-01-15T12:00:00Z",
        "nextBillingAt": "2025-01-15T13:00:00Z"
      }
      ```
    </Tab>

    <Tab title="400" id="response-monitors-update-keyword-400">
      ```json theme={null}
      {
        "error": "invalid_input",
        "message": "Invalid input. Check the request body."
      }
      ```
    </Tab>

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

    <Tab title="404" id="response-monitors-update-keyword-404">
      ```json theme={null}
      {
        "error": "not_found",
        "message": "Resource not found."
      }
      ```
    </Tab>

    <Tab title="429" id="response-monitors-update-keyword-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>

## Choose Keyword Monitor Updates

Use this route to change one keyword monitor's event types or active state. Use get for a read-only checkpoint. Use list for account-wide keyword monitor inventory.

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

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

  ```javascript Node.js theme={null}
  const monitorId = "21";
  const response = await fetch(
    `https://xquik.com/api/v1/monitors/keywords/${monitorId}`,
    {
      method: "PATCH",
      headers: {
        "x-api-key": "xq_YOUR_KEY_HERE",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        eventTypes: ["tweet.new", "tweet.reply"],
        isActive: true,
      }),
    },
  );
  const monitor = await response.json();
  const monitorState = {
    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,
    verify_endpoint: `/api/v1/monitors/keywords/${monitor.id}`,
    delete_endpoint: `/api/v1/monitors/keywords/${monitor.id}`,
    events_endpoint: `/api/v1/events?keywordMonitorId=${monitor.id}`,
    event_detail_endpoint_pattern: "/api/v1/events/{event_id}",
    webhooks_endpoint: "/api/v1/webhooks",
    deliveries_endpoint_pattern: "/api/v1/webhooks/{webhook_id}/deliveries",
  };
  process.stdout.write(`${JSON.stringify(monitorState)}\n`);
  ```

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

  monitor_id = "21"
  response = requests.patch(
      f"https://xquik.com/api/v1/monitors/keywords/{monitor_id}",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      json={"eventTypes": ["tweet.new", "tweet.reply"], "isActive": True},
  )
  monitor = response.json()
  monitor_state = {
      "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"],
      "verify_endpoint": f"/api/v1/monitors/keywords/{monitor['id']}",
      "delete_endpoint": f"/api/v1/monitors/keywords/{monitor['id']}",
      "events_endpoint": f"/api/v1/events?keywordMonitorId={monitor['id']}",
      "event_detail_endpoint_pattern": "/api/v1/events/{event_id}",
      "webhooks_endpoint": "/api/v1/webhooks",
      "deliveries_endpoint_pattern": "/api/v1/webhooks/{webhook_id}/deliveries",
  }
  print(json.dumps(monitor_state))
  ```

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

  import (
      "bytes"
      "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 KeywordMonitorState 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"`
      VerifyEndpoint             string   `json:"verify_endpoint"`
      DeleteEndpoint             string   `json:"delete_endpoint"`
      EventsEndpoint             string   `json:"events_endpoint"`
      EventDetailEndpointPattern string   `json:"event_detail_endpoint_pattern"`
      WebhooksEndpoint           string   `json:"webhooks_endpoint"`
      DeliveriesEndpointPattern  string   `json:"deliveries_endpoint_pattern"`
  }

  func main() {
      body, _ := json.Marshal(map[string]interface{}{
          "eventTypes": []string{"tweet.new", "tweet.reply"},
          "isActive":   true,
      })

      monitorID := "21"
      req, err := http.NewRequest(
          "PATCH",
          "https://xquik.com/api/v1/monitors/keywords/"+monitorID,
          bytes.NewReader(body),
      )
      if err != nil {
          log.Fatal(err)
      }
      req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
      req.Header.Set("Content-Type", "application/json")

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

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

The cURL, Node.js, Python, and Go examples convert the updated keyword monitor
into one state row. Store `keyword_monitor_id`, `query`, `event_types`,
`is_active`, `next_billing_at`, `verify_endpoint`, `delete_endpoint`,
`events_endpoint`, `event_detail_endpoint_pattern`, `webhooks_endpoint`, and
`deliveries_endpoint_pattern` before resuming alerts or webhook checks.

## Path parameters

<ParamField path="id" type="string" required>
  The unique keyword monitor ID.
</ParamField>

## 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>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

## Body

At least 1 field is required.

<ParamField body="eventTypes" type="string[]">
  Updated array of event types. Must contain at least 1 valid keyword monitor
  type: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`,
  `tweet.media`, `tweet.link`, `tweet.poll`, `tweet.mention`,
  `tweet.hashtag`, `tweet.longform`.
</ParamField>

<ParamField body="isActive" type="boolean">
  Set to `false` to pause monitoring, or `true` to resume. Paused keyword monitors do not consume hourly monitor credits.
</ParamField>

<Info>
  The monitored `query` is immutable. Delete this monitor and create another one to track a different query.
</Info>

## Update handoff

Use this endpoint when a keyword alert changes scope, needs a temporary pause,
or must resume without creating a new monitor ID.

| Keyword monitor update column | Request or response source        | Verification rule                              |
| ----------------------------- | --------------------------------- | ---------------------------------------------- |
| Monitor ID                    | Path `{id}` and response `id`     | Require both IDs to match.                     |
| X search query                | Response `query`                  | Confirm the immutable query stayed unchanged.  |
| Tweet event filter            | Request and response `eventTypes` | Confirm the complete replacement filter.       |
| Polling state                 | Request and response `isActive`   | Confirm the intended pause or resume state.    |
| Creation time                 | Response `createdAt`              | Preserve the original configuration timestamp. |
| Billing checkpoint            | Response `nextBillingAt`          | Recalculate the next credit review.            |
| Webhook alignment             | `GET /webhooks`                   | Match subscriptions before trusting alerts.    |

<CardGroup cols={2}>
  <Card title="Returned State" icon="clipboard-check">
    Store returned `id`, `query`, `eventTypes`, `isActive`, `createdAt`, and
    `nextBillingAt` as the current keyword monitor configuration.
  </Card>

  <Card title="Event Filter" icon="funnel">
    `eventTypes` replaces the current filter. Keep
    [List Webhooks](/api-reference/webhooks/list) subscriptions aligned with the
    monitor event types you expect to deliver.
  </Card>

  <Card title="Pause Monitoring" icon="circle-pause">
    `isActive: false` pauses keyword polling, stored events, future webhook
    deliveries, and hourly monitor billing for this monitor.
  </Card>

  <Card title="Resume Monitoring" icon="circle-play">
    `isActive: true` resumes polling for matching future tweets. Check
    `nextBillingAt`, then run [Test Webhook](/api-reference/webhooks/test)
    before relying on production alerts.
  </Card>

  <Card title="Query Change" icon="search-x">
    PATCH cannot change `query`. Delete this monitor and create a new keyword
    monitor when the X search query changes.
  </Card>

  <Card title="Downstream Join" icon="link">
    Keep using `keywordMonitorId` and `query` from
    [List Events](/api-reference/events/list) to reconcile stored events and
    signed webhook payloads after the update. 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="Delete Path" icon="trash-2">
    Use [Delete Keyword Monitor](/api-reference/monitors/delete-keyword) only
    when the query should stop permanently. Export event and delivery evidence
    first when support or audit workflows need history.
  </Card>
</CardGroup>

## Response

### 200 OK

<ResponseField name="id" type="string">Unique keyword monitor ID.</ResponseField>
<ResponseField name="query" type="string">Normalized X search query.</ResponseField>
<ResponseField name="eventTypes" type="string[]">Updated event types.</ResponseField>
<ResponseField name="isActive" type="boolean">Current active status after update.</ResponseField>
<ResponseField name="createdAt" type="string">ISO 8601 creation timestamp.</ResponseField>
<ResponseField name="nextBillingAt" type="string">Next hourly credit charge time for active monitor billing.</ResponseField>

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

### 400 Invalid Input

```json theme={null}
{ "error": "invalid_input", "message": "Empty body, invalid event types, or invalid isActive type" }
```

Empty body, invalid `eventTypes` values, or invalid `isActive` type.

```json theme={null}
{ "error": "invalid_id", "message": "Invalid ID format." }
```

The provided monitor ID is not a valid format.

### 401 Unauthenticated

```json theme={null}
{ "error": "unauthenticated", "message": "Missing or invalid API key" }
```

Missing or invalid API key.

### 404 Not Found

```json theme={null}
{ "error": "not_found", "message": "Monitor not found" }
```

No keyword monitor exists with this ID, or it belongs to a different account.

### 429 Rate Limited

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

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

<Note>
  **Related:** [Get Keyword Monitor](/api-reference/monitors/get-keyword) to verify state, [Delete Keyword Monitor](/api-reference/monitors/delete-keyword) to remove it, [List Events](/api-reference/events/list), [Get Event](/api-reference/events/get), [List Webhooks](/api-reference/webhooks/list), or [List Deliveries](/api-reference/webhooks/deliveries).
</Note>
