> ## 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 tweet event types or pause and resume it. The X search query stays fixed, so the monitor ID and stored events remain usable.

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

## When to update a keyword monitor

Use this route to change one keyword monitor's event types or active state. Use get to read one monitor. Use list to see every keyword monitor on the account.

<Callout icon="circle-check" color="#16a34a">
  **Free.** This endpoint 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="name" type="string | null">
  New label for the monitor, 1 to 80 characters. Send `null` to remove the label. The next event carries the new `monitorName`.
</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>
  You cannot change the monitored `query`. 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 query did not change. |
| 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` | Keep the original creation timestamp. |
| Billing checkpoint | Response `nextBillingAt` | Recalculate the next credit review. |
| Webhook alignment | `GET /webhooks` | Match subscriptions before you rely on 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 you must
    retain webhook delivery evidence. 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>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.