# How to get new tweets from any X account by webhook

> Watch public X accounts and receive every new tweet as a signed webhook within 60, 5 or 1 seconds. Setup in Python, signature check, test event and catch-up after downtime.

HTML page: https://www.twittrapi.com/blog/twitter-webhook-new-tweets
Published 2026-10-14.

X has no free webhook that tells you when an account posts. The usual workaround is polling: ask for the account's newest tweets every few seconds and compare. That works for one account. For fifty it gets expensive, and you still miss tweets between polls.

Account monitoring turns this around. You add the accounts once, and every new tweet arrives at your server as a signed `POST`. This guide sets it up in Python, from the first account to handling downtime.

## What you get

- Any public account, whatever its size. An account with 50 followers is checked as often as one with 5 million.
- One event per new tweet, with the full tweet in the [simple format](https://www.twittrapi.com/docs/simple-format): text, author, counts, media, and whether it is a reply, quote or retweet.
- A check interval of at most 60 s, 5 s or 1 s, depending on the plan.
- Events are kept for 7 days, so you can read what you missed through the pull API.

| Class | Guarantee | Typical |
| --- | --- | --- |
| Standard | We check at least every 60 s | 15–60 s |
| Fast | We check at least every 5 s | 2–5 s |
| Real-time | We check at least every 1 s | 0.5–1 s |

The interval is how often we look. X itself takes a moment before a new tweet shows up in its timelines, and that delay comes on top. Every event carries `tweetCreatedAt` and `detectedAt`, so you can measure the real latency.

## Step 1: Get an API key and a plan

Create an account at [/start](https://www.twittrapi.com/start) and copy your key from the dashboard. Then book a plan under **Monitors**. The monthly fee comes out of your prepaid balance, and calls to `/monitors/*` are not billed per request.

| Accounts | Standard (60 s) | Fast (5 s) | Real-time (1 s) |
| --- | --- | --- | --- |
| 10 | $5 | $9 | $19 |
| 50 | $15 | $29 | $59 |
| 250 | $49 | $99 | $199 |
| 1,000 | $149 | $299 | $599 |
| 5,000 | $499 | $999 | $1,999 |

The smallest plan watches 10 accounts for $5 a month. Store the key in an environment variable:

```bash
export TWITTRAPI_API_KEY="your-key"
```

## Step 2: Add the accounts to watch

Send up to 100 usernames or user IDs per request:

```python
import os
import requests

API = "https://api.twittrapi.com"
HEADERS = {"X-API-KEY": os.environ["TWITTRAPI_API_KEY"]}

r = requests.post(
    f"{API}/monitors/accounts",
    headers=HEADERS,
    json={"usernames": ["XDevelopers", "NASA", "OpenAI"]},
    timeout=30,
)
r.raise_for_status()
for result in r.json()["data"]:
    print(result["input"], result.get("status") or result.get("error"))
```

Each account gets its own result: `added`, `already_watching`, or an error such as not found or protected. Protected accounts cannot be watched. Delivery starts with the first tweet after monitoring begins; older tweets are not sent. For history, use the [user tweets endpoint](https://www.twittrapi.com/blog/get-tweets-from-twitter-account-python).

## Step 3: Set your webhook

The URL must be public and use `https`. Private and local addresses are refused, and redirects are not followed.

```python
r = requests.put(
    f"{API}/monitors/webhook",
    headers=HEADERS,
    json={"url": "https://example.com/hooks/twittrapi"},
    timeout=30,
)
r.raise_for_status()
print(r.json()["webhook"]["secret"])  # whsec_...
```

The first call returns a signing secret that starts with `whsec_`. Save it as `TWITTRAPI_WEBHOOK_SECRET` on your server. Changing the URL later keeps the secret.

To test on your laptop, expose your local port with a tunnel such as ngrok or Cloudflare Tunnel and use its https address as the webhook URL.

## Step 4: Receive and verify the events

Every new tweet is one `POST` with a JSON body. The `X-TwittrAPI-Signature` header holds a timestamp and an HMAC-SHA256 of `<timestamp>.<raw body>`, keyed with your secret. Check it on the raw bytes before you parse the JSON, and reject old timestamps so a captured request cannot be replayed.

```python
import hashlib
import hmac
import os
import time

from flask import Flask, abort, request

SECRET = os.environ["TWITTRAPI_WEBHOOK_SECRET"].encode()
app = Flask(__name__)

def verify(header: str, raw_body: bytes, tolerance: int = 300) -> bool:
    parts = dict(p.strip().split("=", 1) for p in header.split(",") if "=" in p)
    t, sig = parts.get("t", ""), parts.get("v1", "")
    if not t.isdigit() or not sig or abs(time.time() - int(t)) > tolerance:
        return False
    expected = hmac.new(SECRET, t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

@app.post("/hooks/twittrapi")
def hook():
    if not verify(request.headers.get("X-TwittrAPI-Signature", ""), request.get_data()):
        abort(401)
    event = request.get_json()
    if event["event"] == "tweet":
        tweet = event["tweet"]
        print(tweet["author"]["username"], tweet["url"], tweet["text"][:80])
    return "", 204
```

Answer with any `2xx` within 5 seconds. If you need to do more than print, put the event on a queue and answer first. The [monitoring docs](https://www.twittrapi.com/docs/monitoring#verify-the-signature) have the same handler in Node.js and Go.

A tweet event looks like this:

```json
{
  "event": "tweet",
  "id": "1849203",
  "monitoredUserId": "2244994945",
  "tweetId": "2019881223666233717",
  "tweetCreatedAt": "2026-02-06T21:09:45Z",
  "detectedAt": "2026-02-06T21:09:46.212Z",
  "tweet": {
    "id": "2019881223666233717",
    "url": "https://x.com/XDevelopers/status/2019881223666233717",
    "text": "Officially launching X API Pay-Per-Use …",
    "author": { "id": "2244994945", "username": "XDevelopers" },
    "replyTo": null,
    "quoted": null,
    "retweetOf": null
  }
}
```

Replies and retweets are included. If you only want original posts, skip events where `replyTo` or `retweetOf` is set.

## Step 5: Send a test event

Before you wait for a real tweet, check that your endpoint answers in time:

```python
r = requests.post(f"{API}/monitors/webhook/test", headers=HEADERS, timeout=30)
print(r.json())  # {"ok": true, "statusCode": 204, "latencyMs": 84}
```

The test event is signed like a real one and has `X-TwittrAPI-Event: test`.

## Step 6: Catch up after downtime

If your server is down, failed deliveries are retried with growing pauses for up to 24 hours. After 20 failures in a row the webhook is switched off and you get an email; monitoring keeps running. Events stay readable for 7 days through the pull API:

```python
since = None  # store the last nextSince somewhere durable
while True:
    params = {"limit": 500}
    if since:
        params["since"] = since
    page = requests.get(f"{API}/monitors/events", headers=HEADERS, params=params, timeout=30).json()
    for event in page["data"]:
        print(event["tweet"]["url"])
    since = page["nextSince"] or since
    if not page["hasMore"]:
        break
```

Retries can arrive out of order and, rarely, twice. Use the `X-TwittrAPI-Delivery` header to drop duplicates and `tweetCreatedAt` to sort. When the endpoint works again, send `PUT /monitors/webhook` once more to switch the webhook back on.

## Webhook or polling?

| | Polling the timeline | Account monitoring |
| --- | --- | --- |
| Cost | One billed call per account per poll | Flat monthly fee per plan |
| Delay | Your poll interval | 60 s, 5 s or 1 s |
| Work on your side | Scheduler, state, deduplication | One endpoint |
| Missed tweets | Possible between polls | Kept 7 days for catch-up |

Polling is fine for a few accounts checked every few minutes. Checking one account every 5 seconds is about 518,000 calls a month, which costs more than a 50-account Fast plan at $29. For keywords and hashtags, which monitoring does not cover, poll [search](https://www.twittrapi.com/blog/search-twitter-without-developer-account) with `since_id` so each call returns only new tweets.

## Frequently asked questions

### Does X have a webhook for new tweets?

Yes, through its Activity API, billed per event. We compare it with a flat monitoring plan in [X API webhooks for new posts](https://www.twittrapi.com/blog/x-activity-api-alternative). There is no free option on X.

### Can I watch accounts with few followers?

Yes. There is no follower minimum. Every public account in your plan is checked at the plan's interval.

### Are deleted tweets reported?

No. An event is sent when a tweet is detected. Deletions do not create a separate event.

### What happens when my balance runs out?

The plan is paused at renewal. Your watched accounts are kept, but no events are created until you top up and renew.

## Sources

- [Account monitoring docs](https://www.twittrapi.com/docs/monitoring) (API, events, signatures, retries, limits)
- [Simple format: tweet fields](https://www.twittrapi.com/docs/simple-format#tweet)
- [Twitter Streaming API alternative](https://www.twittrapi.com/twitter-streaming-api)

TwittrAPI is not affiliated with X Corp. It reads only public data.
