---
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, no API keys, the wallet is the account. 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 BOTH an x402 challenge (USDC on Base/Solana) and an MPP
challenge (USDC on Tempo). Pay with either, retry with the credential, get data + an on-chain
settlement receipt. Failed calls are not charged.

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.

## 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: { kind: "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.

## Pricing

Per-unit rate ≈ **$0.0012**, floor $0.001/call. Volume tools bill per returned item and
REQUIRE `resultsLimit` (1–10000) — you never pay for more than you asked for. The exact
price is always in the 402 before you commit.

## 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.0012+ | |
| `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.001 | |
| `x_compose` | AI tweet drafting: generate → refine → score | ~$0.001 | 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

Two steps; credentials are relayed to the platform for the login session and never stored
by twitr.sh:

1. `POST /api/x-accounts/connect` `{ "username": "myhandle", "email": "...", "password": "...", "totp_secret": "..." }`
   → 202 `{status:"connecting"}`. The login runs in the background (30–50s).
2. Poll `GET /api/x-accounts` until the handle shows `linked` — or `pending_code`, which
   means X emailed a confirmation code: ask the user for it, then
   `POST /api/x-accounts/confirm` `{ "challenge_id": "...", "code": "123456" }`.

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`
