> ## 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 post monitor API & engagement alerts

> Track 1 X post's replies, quotes, edits, deletion & 6 metrics. Send each change to signed webhooks. Costs 30 credits an hour, events & deliveries included.

**201**

```json theme={null}
{
  "id": "31",
  "postId": "2104984505677430978",
  "postUrl": "https://x.com/xquik/status/2104984505677430978",
  "authorId": "1661816432185823232",
  "authorUsername": "xquik",
  "authorName": "Xquik",
  "text": "We shipped post monitors.",
  "postedAt": "2026-09-29T17:19:49.000Z",
  "eventTypes": [
    "post.reply.new"
  ],
  "isActive": true
}
```

**400**

```json theme={null}
{
  "error": "invalid_input",
  "message": "Invalid input. Check the request body."
}
```

**401**

```json theme={null}
{
  "error": "unauthenticated",
  "message": "Authentication required. Provide a valid API key or bearer token."
}
```

**402**

```json theme={null}
{
  "error": "insufficient_credits",
  "message": "Insufficient credits. Top up or subscribe to continue."
}
```

**403**

```json theme={null}
{
  "error": "forbidden",
  "message": "This API key calls paid X read routes only, such as GET /api/v1/x/tweets/search. Create a Full access key at dashboard.xquik.com/account?tab=api-keys to call this route."
}
```

**404**

```json theme={null}
{
  "error": "tweet_not_found",
  "message": "Tweet not found. Check the tweet ID."
}
```

**409**

```json theme={null}
{
  "error": "monitor_already_exists",
  "message": "Monitor already exists."
}
```

**422**

```json theme={null}
{
  "error": "post_unavailable",
  "message": "This post's author limits who can see it. Try again once the post is public."
}
```

**429**

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

**502**

```json theme={null}
{
  "error": "x_api_unavailable",
  "message": "X data source temporarily unavailable. Try again later."
}
```

**503**

```json theme={null}
{
  "error": "x_api_unavailable",
  "message": "Xquik is busy right now. Retry shortly."
}
```

> For the complete documentation index, see [llms.txt](/llms.txt).

Create a post monitor when 1 X post needs live tracking. It reports new
replies, new quotes, edits & deletion. It also tracks views, likes, retweets,
replies, quotes & bookmarks.

**Requires 30 available credits.** Active post monitors bill 30 credits per hour.

> [!NOTE]
>
> The hourly price covers stored events & webhook deliveries. Xquik charges an
> hour only after a read that finds the post.

```bash cURL theme={null}
curl -X POST https://xquik.com/api/v1/monitors/posts \
  -H "x-api-key: xq_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "post": "https://x.com/xquik/status/2104984505677430978",
    "name": "Launch post"
  }' |
  jq -c '{
    post_monitor_id: .id,
    post_id: .postId,
    post_url: .postUrl,
    event_types: .eventTypes,
    is_active: .isActive,
    next_billing_at: .nextBillingAt,
    likes: .metrics.likes.current,
    verify_endpoint: "/api/v1/monitors/posts/\(.id)",
    metrics_endpoint: "/api/v1/monitors/posts/\(.id)/metrics",
    events_endpoint: "/api/v1/events?postMonitorId=\(.id)"
  }'
```

```javascript Node.js theme={null}
const response = await fetch("https://xquik.com/api/v1/monitors/posts", {
  method: "POST",
  headers: {
    "x-api-key": "xq_YOUR_KEY_HERE",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    post: "https://x.com/xquik/status/2104984505677430978",
    name: "Launch post",
  }),
});
const monitor = await response.json();
const monitorState = {
  post_monitor_id: monitor.id,
  post_id: monitor.postId,
  post_url: monitor.postUrl,
  event_types: monitor.eventTypes,
  is_active: monitor.isActive,
  next_billing_at: monitor.nextBillingAt,
  likes: monitor.metrics.likes.current,
  verify_endpoint: `/api/v1/monitors/posts/${monitor.id}`,
  metrics_endpoint: `/api/v1/monitors/posts/${monitor.id}/metrics`,
  events_endpoint: `/api/v1/events?postMonitorId=${monitor.id}`,
};
process.stdout.write(`${JSON.stringify(monitorState)}\n`);
```

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

response = requests.post(
    "https://xquik.com/api/v1/monitors/posts",
    headers={"x-api-key": "xq_YOUR_KEY_HERE"},
    json={
        "post": "https://x.com/xquik/status/2104984505677430978",
        "name": "Launch post",
    },
)
monitor = response.json()
monitor_state = {
    "post_monitor_id": monitor["id"],
    "post_id": monitor["postId"],
    "post_url": monitor["postUrl"],
    "event_types": monitor["eventTypes"],
    "is_active": monitor["isActive"],
    "next_billing_at": monitor["nextBillingAt"],
    "likes": monitor["metrics"]["likes"]["current"],
    "verify_endpoint": f"/api/v1/monitors/posts/{monitor['id']}",
    "metrics_endpoint": f"/api/v1/monitors/posts/{monitor['id']}/metrics",
    "events_endpoint": f"/api/v1/events?postMonitorId={monitor['id']}",
}
print(json.dumps(monitor_state))
```

Each example turns the new monitor into 1 state row. Store
`post_monitor_id`, `post_id`, `event_types` & `next_billing_at` before you
route alerts.

## Post monitor handoff

Create the monitor first. Then create a signed webhook with
[`POST /webhooks`](/api-reference/webhooks/create) for the same event types.
Test it with [`POST /webhooks/{id}/test`](/api-reference/webhooks/test).

> **Monitor ID**
>
> Store `id` as `post_monitor_id`. Use it with
> [Get Post Monitor](/api-reference/monitors/get-post.md),
> [Update Post Monitor](/api-reference/monitors/update-post.md) &
> [Delete Post Monitor](/api-reference/monitors/delete-post.md).

> **Tracked post**
>
> Store `postId`, `postUrl` & `authorUsername`. Every post event carries
> them, so receivers can route without a lookup.

> **Metric history**
>
> Read hourly values & gains with
> [Post Monitor Metrics](/api-reference/monitors/post-metrics.md). Xquik keeps
> 30 days of history.

> **Stored event join**
>
> Pass `id` as `postMonitorId` to
> [List Events](/api-reference/events/list.md). Post events carry
> `monitorType: "post"` & the same `monitorId`.

## How do I track a tweet's engagement with an API?

Send the post link or ID as `post`. Xquik reads the post once & returns its
current metrics. Each metric starts with a gain of `0`.

Xquik then reads the post every 2 to 4 seconds. A metric event fires when a count
holds a new value. Each metric fires at most once a minute. The event covers
the whole change since the last one.

New replies & quotes come as their own events. Each one carries the reply or
quote as `data`, like `tweet.new`. Xquik sends them oldest first.

## What happens when the author edits or deletes the post?

X moves engagement to the newest version of an edited post. The monitor
follows that version, so metrics keep counting after an edit.

`post.edited` carries `previousText`, `text` & `versionId`. `post.deleted`
fires once 3 reads in a row confirm the deletion. It carries the last text
the monitor saw.

After 3 missed reads, Xquik pauses the monitor. `pausedReason` says why.
A paused monitor costs nothing.

## Headers

**x-api-key** `string` *required*

Your API key. Session cookie authentication is also supported. Generate a key from the [dashboard](https://dashboard.xquik.com/en/account?tab=api-keys).

**Content-Type** `string` *required*

Must be `application/json`.

## Body

**post** `string` *required*

Post link or ID, up to 2,048 characters. x.com, twitter.com, mobile &
`/i/web/status/` links work with any query string.

**name** `string | null`

Your own label for the monitor, 1 to 80 characters. Every event the monitor
produces carries it as `monitorName`. Xquik normalizes whitespace.

**eventTypes** `string[]`

Post event types to send, 1 to 10. Without it, the monitor sends
`post.reply.new`, `post.quote.new`, `post.likes.changed`,
`post.retweets.changed`, `post.edited` & `post.deleted`.

## Valid event types

| Event type | Fires when | `data` |
| - | - | - |
| `post.reply.new` | A new direct reply appears. | The reply. |
| `post.quote.new` | A new quote of the post appears. | The quote. |
| `post.likes.changed` | The like count holds a new value. | `metric`, `previous`, `current`, `change`. |
| `post.retweets.changed` | The retweet count holds a new value. | `metric`, `previous`, `current`, `change`. |
| `post.replies.changed` | The reply count holds a new value. | `metric`, `previous`, `current`, `change`. |
| `post.quotes.changed` | The quote count holds a new value. | `metric`, `previous`, `current`, `change`. |
| `post.bookmarks.changed` | The bookmark count holds a new value. | `metric`, `previous`, `current`, `change`. |
| `post.views.changed` | The view count holds a new value. | `metric`, `previous`, `current`, `change`. |
| `post.edited` | The author edits the post. | `previousText`, `text`, `versionId`. |
| `post.deleted` | 3 reads in a row confirm the deletion. | `text`, the last text the monitor saw. |

The reply count can include replies X does not list. `post.reply.new`
reports the replies X lists.

## Response

### 201 Created

**id** `string`

Unique post monitor ID.

**name** `string`

Your label for the monitor, when set.

**postId** `string`

ID of the post you gave.

**postUrl** `string`

Link to the post.

**authorId** `string`

Permanent X user ID of the post's author.

**authorUsername** `string`

Username of the post's author.

**authorName** `string`

Display name of the post's author.

**text** `string`

Text of the newest version Xquik read.

**postedAt** `string`

ISO 8601 time X published the post.

**eventTypes** `string[]`

Post event types this monitor sends.

**isActive** `boolean`

Whether the monitor is active.

**createdAt** `string`

ISO 8601 creation timestamp.

**nextBillingAt** `string`

Next hourly credit charge time.

**pausedReason** `string`

Why Xquik paused the monitor. Absent while active.

**pausedAt** `string`

When Xquik paused the monitor.

**metrics** `object`

`views`, `likes`, `retweets`, `replies`, `quotes` & `bookmarks`. Each holds `current` & `gain`
since tracking began. Both are `null` when X shows no count, as for views on old posts.

### 400 Invalid input

`post` is missing or is not a post link or ID. The message says what to fix.

### 401 Unauthenticated

Missing or invalid API key.

### 402 Payment required

You need at least 30 available credits to create a post monitor.

### 404 Tweet not found

X shows no such post, so Xquik creates nothing. `reason` is `deleted`,
`author_suspended` or `protected` when X names it.

### 409 Duplicate

An active monitor already tracks this post. Use
[Update Post Monitor](/api-reference/monitors/update-post.md) to change it.

### 422 Post unavailable

The author limits who can see the post. Choose a public post.

### 429 Rate limited

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

### 502 X unavailable

X failed the read. Retry after a short wait.

### 503 Busy

Xquik is busy right now. Wait for `Retry-After`, then retry.

> [!NOTE]
>
> Creating a monitor for a paused post again turns it back on. It keeps its
> start values & history, and takes the new name & event types.

> [!NOTE]
>
> **Next steps.** [List Post Monitors](/api-reference/monitors/list-posts.md), [Post Monitor Metrics](/api-reference/monitors/post-metrics.md), [Create Webhook](/api-reference/webhooks/create.md), [List Events](/api-reference/events/list.md), or [Get Event](/api-reference/events/get.md).


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