---
name: twitr
description: Automate X/Twitter for AI agents via twitr.sh — read tweets/profiles, search, timelines, bulk extractions, real-time monitors on accounts or keywords, webhook event delivery, and posting/liking/replying through a connected X account. Pay per call in USDC (x402 on Base/Solana, MPP on Tempo) — no signup, the wallet is the account; or use an API key with a prepaid card balance. Use whenever the user wants X/Twitter data, wants to watch an account or topic in real time, wants tweet events pushed to their endpoint, or wants an agent that posts/replies on X.
---

# twitr.sh — X/Twitter automation for agents

Every paid call is one POST to `https://twitr.sh/api/tools/{tool}`. First call without
credentials returns HTTP 402 with payment options. Per-result reads use x402 `upto` on Base: authorize a maximum, pay only for returned records, and pay zero for empty results. Fixed-price operations also support exact x402 on Base/Solana and MPP on Tempo. Extractions, monitors and writes are prepaid before dispatch; their receipt remains valid even if downstream work fails.

Identity for the free endpoints (history, events, webhooks, account linking) is SIWX — a
wallet signature, no password. The first SIWX call sets a 1h session cookie; subsequent free
calls reuse it with no extra signing.

## Or: API key + card balance

No wallet? Sign in at https://twitr.sh/dashboard (wallet or Google), add a card balance, and
create an API key. Send it on any tool call — HTTP or MCP — as
`Authorization: Bearer tw_live_…`. Calls are charged to the balance at the same prices;
per-result reads charge only the records returned, and the response `payment` block shows
`balance_remaining_usd`. The key also works on the free endpoints (snapshots, monitors, events,
webhooks, media, balance). Too little balance → 402 `payment-insufficient`, nothing charged;
unknown or revoked key → 401.

## Calling the API

Use AgentCash MCP — it handles the 402, signing, and retry automatically:

```
mcp__agentcash__fetch({
  url: "https://twitr.sh/api/tools/x_search",
  method: "POST",
  body: { type: "search-tweets", q: "from:vercel launch", resultsLimit: 50 }
})
```

AgentCash surfaces the 402 body BEFORE signing. Read `amount` and `breakdown`, and confirm
with the user when the price is non-trivial (~$0.10+), and ALWAYS before creating a monitor
or posting as their account.

Run paid calls **sequentially from the same wallet**: wait for the receipt before starting the next call. Overlapping payments receive a free HTTP 429; wait for the other call to finish, then make a fresh AgentCash fetch call with the same input. Do not replay its old payment credential.

For research, start with **3–5 results per search**. Posts include rich nested context and large batches can exceed the agent's tool-output limit. Use larger limits for datasets, and read the saved tool-result file when your agent client provides one.

## Pricing

Per returned record: **$0.0012**, with a **$0.0024 minimum for non-empty calls**.
Empty per-result reads cost **$0**. These reads use x402 `upto` on **Base**, including gasless approval. Exact-only and MPP clients cannot use per-result reads; use current AgentCash.
`resultsLimit` sets the maximum result count and payment authorization (reads are capped at 2,500).
The receipt's `payment.amount` is the actual charge; the 402 amount is the maximum.
Example: request 20 tweets, receive 2, pay $0.0024; receive 0, pay $0; receive 20, pay $0.024.
Fixed calls have a $0.0024 minimum. Bulk extractions, monitors and connected-account operations are prepaid at their quoted amount.

### Date-sensitive questions

For "What is @vercel posting about this week?", calculate the current week's Monday
and send `type: "search-tweets"`, `q: "from:vercel"`, `sinceDate: "YYYY-MM-DD"`,
`queryType: "Latest"`, `resultsLimit: 20`. State the exact date window in the answer.
For "last seven days", use today's date minus seven days instead.
An empty result means no matching posts in that window. Say so. You may then fetch the
latest posts with `x_timeline` (`kind: "user-tweets"`, `id: "vercel"`, `resultsLimit: 5`)
and clearly label them as older context. Never silently broaden the date filter or count
older posts as this week's activity. Include post dates, links, and actual paid amount.

## Tool catalog (POST /api/tools/{tool})

| Tool | What it does | Price | Notes |
|---|---|---|---|
| `x_read` | Single tweet, profile, article, follow-check, trends, media, batch (≤100) | ~$0.0024+ | |
| `x_search` | Tweet search (full operator set) or user search | per item | `resultsLimit` required |
| `x_timeline` | User tweets/replies/likes/media/mentions, follower graphs, engagement lists | per item | `resultsLimit` required |
| `x_lists` | List timelines, members, followers | per item | `resultsLimit` required |
| `x_communities` | Community info, members, tweets, in-community search | per item | |
| `x_extract` | Bulk datasets (followers, replies, threads, spaces…) — async | up to $60 | `Idempotency-Key` required; poll snapshot |
| `x_monitor` | Real-time monitor on an account or keyword | ~$0.025/hour | see Monitors |
| `x_draws` | Crypto-random giveaway winner picks from replies | varies | `Idempotency-Key` required |
| `x_write` | Post, reply, like, retweet, follow, DM, delete, media upload | $0.012–$0.036 | connected account + `Idempotency-Key` |
| `x_inbox` | Connected account's bookmarks, notifications, DM history | per item | connected account |
| `x_profile` | Edit connected account's profile (bio, avatar, banner…) | ~$0.012 | connected account |
| `x_community` | Create/join/leave/delete a community as connected account | ~$0.012–$0.036 | connected account |
| `x_radar` | Trending news and topics by region/category | ~$0.0024 | |
| `x_compose` | AI tweet drafting: generate → refine → score | ~$0.0024 | text only, posts nothing |

MCP door: `POST /api/mcp` (JSON-RPC; unpaid calls return error `-32042` with the same
payment terms; credential goes in `params._meta["org.paymentauth/credential"]`).

## Monitors — watch an account or keyword in real time

Prepaid by the hour (~$0.025/hr; 24h ≈ $0.61). A monitor STOPS at `expires_at` unless
extended; early deletion does not refund. Max 168 hours per purchase, 720 prepaid total.
Pay on **Base (x402) or Tempo (MPP)** — Solana is not offered for monitors (the owner
wallet must be recoverable). `Idempotency-Key` header required on create.

```
POST /api/tools/x_monitor  { "action": "create", "username": "vercel", "hours": 24 }
POST /api/tools/x_monitor  { "action": "create", "query": "site:x.com \"launch week\"", "hours": 24 }
POST /api/tools/x_monitor  { "action": "extend", "monitorId": "mn_...", "hours": 48 }
GET  /api/monitors                      (free, SIWX — list + status)
DELETE /api/monitors/{id}               (free, SIWX)
```

Event types: `tweet.new`, `tweet.reply`, `tweet.retweet`, `tweet.quote`, `tweet.media`,
`tweet.link`, `tweet.poll`, `tweet.mention`, `tweet.hashtag`, `tweet.longform`, and (account
monitors only) `profile.*.changed` (avatar, banner, name, username, bio, location, url,
verified, protected, pinned_tweet, unavailable). A terminal `monitor.expired` /
`monitor.deleted` event fires once when a monitor ends — silence is never ambiguous.

## Getting events — poll or push

**Poll (free, SIWX):**
```
GET /api/monitors/{id}/events?after={last_event_id}&limit=50
```
Cursor semantics: pass the last event id you processed; you get strictly newer events,
oldest→newest. Retention: 500 events / 24h per monitor. Rate limit 60 reads/min. Poll at
most 1/s, ideally only when the user asks — don't spin in a loop.

**Push (webhooks — free, SIWX, max 3 per wallet):**
```
POST /api/webhooks  { "url": "https://your-agent.example.com/hooks", "eventTypes": ["tweet.new"] }
```
- `eventTypes` omitted = all events (including lifecycle). HTTPS hostnames only — no IPs,
  no localhost.
- The 201 response contains the signing `secret` EXACTLY ONCE — store it immediately
  (`POST /api/webhooks/{id}/rotate` if lost).
- **Verify every delivery**: recompute `sha256=HMAC-SHA256(secret, "{X-Twitr-Timestamp}.{rawBody}")`
  and constant-time-compare to the `X-Twitr-Signature` header. Reject timestamps older than
  5 minutes. Dedupe on `delivery_id` (per retry) and `id` (per event).
- Respond 2xx within 10s — do slow work async. Retries back off over ~36 min (6 attempts);
  50 consecutive failures auto-pause the webhook and a successful `POST /api/webhooks/{id}/test`
  re-activates it. Delivery log: `GET /api/webhooks/{id}/deliveries`.

Envelope: `{ type, id, delivery_id, monitor_id, occurred_at, received_at, source, data }`.

## Connecting an X account (for writes) — free, SIWX

**Never ask the user for their X password, email, or 2FA secret, and never accept one if
offered.** Hand them a link and let them sign in themselves:

1. `POST /api/x-accounts/start` `{ "username": "myhandle" }` → `{connect_url, expires_in}`.
2. Show the user `connect_url`: *open this in your browser and sign in to X there.* They enter
   their password on twitr.sh, in their own browser — it never passes through you. The link is
   single-use and expires in 15 minutes; mint a fresh one if it lapses.
3. Poll `GET /api/x-accounts` until the handle shows `linked`. (If X emails a confirmation code,
   the user enters it on that same page — you do not need to collect it.)

One real X handle can be owned by exactly ONE wallet. Disconnect with
`DELETE /api/x-accounts/{handle}`.

## Writing as the connected account

```
POST /api/tools/x_write
Idempotency-Key: <uuid>            ← MANDATORY
{ "action": "post", "account": "myhandle", "text": "shipping day 🚀" }
```

Actions: `post`, `reply`, `like`, `unlike`, `retweet`, `unretweet`, `follow`, `unfollow`,
`delete_tweet`, `send_dm`, `remove_follower`, `upload_media`. Ownership is enforced
server-side — a wallet can only write as handles IT connected; the `account` you pass is
re-validated cryptographically against the paying wallet.

A 202 `{"status":"pending_confirmation","action_id":"..."}` means the write was accepted and
is being applied — **do NOT resend** (it's already paid). Retries with the same
`Idempotency-Key` replay the original result instead of double-posting.

## The full loop — an auto-reply agent in 4 calls

1. **Connect** the user's X account (free): connect → confirm code → `linked`.
2. **Monitor** the trigger: `x_monitor` create on `"query": "@myhandle"` or a keyword, 24h.
3. **Listen**: register a webhook for `["tweet.mention", "tweet.new"]` — or poll
   `/api/monitors/{id}/events` when the user asks.
4. **React**: on an event, draft with `x_compose` (~$0.001), show the user, then
   `x_write` `{action:"reply", account, reply_to_tweet_id, text}` ($0.036).

Total running cost: ~$0.61/day for the monitor + ~$0.04 per reply.

## Async extractions

`x_extract` returns `{ "snapshot_id": "sd_...", "status_url": "/api/snapshots/sd_..." }`.
Poll the status URL (free, SIWX) — when `ready`, the data is a **download URL**, not inline
records. Hand the URL to the user; never paste a huge dataset into context. Wallet history:
`GET /api/snapshots` and `GET /api/monitors` list everything this wallet has paid for.

## Don't do

- Don't auto-poll events or snapshots in a loop without the user asking — burns tokens.
- Don't skip the 402 price check — surface any non-trivial cost before signing.
- Don't resend a write after a 202 or a timeout — reuse the same `Idempotency-Key`.
- Don't create a monitor without telling the user the total prepaid cost and expiry.
- Don't keep calling after 3 consecutive failures — surface the error and stop.

## Discovery

- OpenAPI 3.1 + payment discovery: `https://twitr.sh/openapi.json`
- Agent guide: `https://twitr.sh/llms.txt`
- This skill: `https://twitr.sh/skill.md`
- MCP: `POST https://twitr.sh/api/mcp` · x402: `/.well-known/x402` · MPP: `/.well-known/mpp.json`
- Dashboard (browser): `https://twitr.sh/dashboard`

## Installable skills

Six focused skills (Agent Skills format — Claude Code, Hermes, OpenClaw, Cursor, and others):

```
npx skills add lnvestor/twitr-skills
```

- `twitter-automation` — the builder: describe an automation and it designs, prices, and wires it
- `x-twitter-data` — live reads, search, timelines, lists, communities, trends
- `x-twitter-research` — bulk extractions returning downloadable datasets
- `x-twitter-monitor` — real-time account/keyword watch; free polling or signed webhooks
- `x-twitter-publish` — post, reply, quote, media, profile via a connected account
- `x-presence` — unattended posting + monitoring routine for cron
