Official skill for twitterapi.io — query Twitter/X data (tweets, profiles, followers, advanced search, trends, spaces, communities, lists) and perform authenticated actions (post, reply, like, retweet, follow, DM) via the twitterapi.io REST API using a single `x-api-key` header — no OAuth. Use when the user needs to scrape, analyze, monitor, or automate X/Twitter without going through the official developer portal.
---
name: twitterapi-io
description: Official skill for twitterapi.io — query Twitter/X data (tweets, profiles, followers, advanced search, trends, spaces, communities, lists) and perform authenticated actions (post, reply, like, retweet, follow, DM) via the twitterapi.io REST API using a single `x-api-key` header — no OAuth. Use when the user needs to scrape, analyze, monitor, or automate X/Twitter without going through the official developer portal.
---
# twitterapi.io
**Official skill** maintained by the [twitterapi.io](https://twitterapi.io) team. Paths, parameters, and body fields in this skill are verified against the live backend.
## When to use this skill
Trigger when the user wants any of:
- Fetch tweets, user profiles, followers/following, trends, replies, quote-tweets, retweeters, articles
- Advanced tweet search (X operators, date ranges, engagement filters)
- Monitor users or filter rules in near real time
- Post / delete / like / retweet / bookmark / quote-tweet / reply / follow / DM / schedule tweets
- Update profile / avatar / banner, upload media
- Create/join/leave communities, manage lists, report content
- Anything involving `twitterapi.io`, `api.twitterapi.io`, `x-api-key`, `login_cookies`, or the X API without OAuth
## Core facts
| | |
|---|---|
| Base URL | `https://api.twitterapi.io` |
| Prefix | `/twitter/...` most endpoints; `/oapi/x_user_stream/...` and `/oapi/tweet_filter/...` for real-time monitoring/webhooks; `/oapi/my/info` for balance |
| Auth header | `x-api-key: YOUR_KEY` |
| Dashboard | https://twitterapi.io/dashboard |
| Docs | https://docs.twitterapi.io |
| Rate limit | ~200 req/s per client |
| Pricing | ~$0.15/1k tweets, ~$0.18/1k profiles, $0.00015 minimum |
## ⚠️ Two rules that catch people
### Rule 1 — Parameter naming is per-endpoint
No universal snake_case or camelCase rule. Examples (all correct):
- `/twitter/user/followers?userName=` (camel)
- `/twitter/user/verifiedFollowers?user_id=` (snake)
- `/twitter/user/articles?username=` (all-lowercase)
- `/twitter/tweets?tweet_ids=` (snake) but `/twitter/tweet/replies?tweetId=` (camel)
- `/twitter/list/tweets_timeline?listId=` (camel) but `/twitter/list/members?list_id=` (snake)
**Copy the exact parameter name from [references/endpoints.md](references/endpoints.md). Don't normalize.**
### Rule 2 — Writes need three things in body, not two
Every write endpoint requires:
1. `login_cookies` (**plural**, not `login_cookie`) — base64-encoded JSON from `/twitter/user_login_v2`
2. `proxy` (HTTP/SOCKS proxy URL — configure in dashboard)
3. Action-specific fields, almost always **snake_case** (`tweet_id`, `user_id`)
Plus key field-name traps:
- `create_tweet_v2` text field is `tweet_text` (not `text`); reply field is `reply_to_tweet_id` (not `in_reply_to_tweet_id`)
- `update_profile_v2` uses `description` (not `bio`)
- `bookmarks_v2` uses `count` (not `pageSize`)
- `send_dm_to_user`: `user_id` + `text`; optional `media_id` (singular)
- Monitoring add uses `x_user_name`; remove uses `id_for_user` (different fields per endpoint!)
See [references/write-operations.md](references/write-operations.md) for full body shapes.
### Rule 3 — Response shape varies per endpoint
- `data`-wrapped: `user/info`, `user_about`, `last_tweets`, `tweet_timeline`, `trends`, `check_follow_relationship` → `r["data"]["..."]`
- Flat with envelope: `followers`, `followings`, `replies`, `mentions`, `community/tweets` → `r["followers"]`, `r["tweets"]`, etc.
- Flat without envelope: `advanced_search`, `thread_context`, `user/search`, `get_tweets_from_all_community` → just `{tweets[], has_next_page, next_cursor}`
- Named top-level field: `community/info` → `r["community_info"]`; `batch_info_by_ids` → `r["users"]`
- `oapi/my/info` → `{recharge_credits, total_bonus_credits}` (no status wrapper)
Prefer defensive access: `r.get("tweets", r.get("data", {}).get("tweets", []))`.
## Security
**Never** hardcode the API key. Read from env `TWITTERAPI_IO_KEY`. If missing, ask the user.
## Minimal working example
```bash
curl -s "https://api.twitterapi.io/twitter/user/info?userName=elonmusk" \
-H "x-api-key: $TWITTERAPI_IO_KEY"
```
```python
import os, requests
BASE = "https://api.twitterapi.io"
HEADERS = {"x-api-key": os.environ["TWITTERAPI_IO_KEY"]}
r = requests.get(f"{BASE}/twitter/user/info",
headers=HEADERS,
params={"userName": "elonmusk"},
timeout=30)
r.raise_for_status()
d = r.json()["data"]
print(f"{d['userName']} — {d['followers']:,} followers")
```
## Endpoint quick reference
Reads (API key only). **Exact param names — copy as shown.**
| Capability | Method | Path & key param |
|---|---|---|
| User by screen name | GET | `/twitter/user/info?userName=` |
| Extended bio | GET | `/twitter/user_about?userName=` |
| Batch users by IDs | GET | `/twitter/user/batch_info_by_ids?userIds=` |
| Search users | GET | `/twitter/user/search?query=` (**not** `keyword`) |
| Recent tweets | GET | `/twitter/user/last_tweets?userName=` (or `userId=`) |
| Timeline by ID | GET | `/twitter/user/tweet_timeline?userId=` |
| User's articles | GET | `/twitter/user/articles?username=` (**all-lowercase**) |
| Mentions of user | GET | `/twitter/user/mentions?userName=` |
| Followers | GET | `/twitter/user/followers?userName=&pageSize=200` |
| Verified followers | GET | `/twitter/user/verifiedFollowers?user_id=` (**snake**) |
| Followings | GET | `/twitter/user/followings?userName=` |
| Check follow | GET | `/twitter/user/check_follow_relationship?source_user_name=&target_user_name=` |
| Tweets by IDs | GET | `/twitter/tweets?tweet_ids=` (**snake**) |
| Tweet replies | GET | `/twitter/tweet/replies?tweetId=` |
| Replies (sortable) | GET | `/twitter/tweet/replies/v2?tweetId=&queryType=Latest` |
| Quote tweets | GET | `/twitter/tweet/quotes?tweetId=` |
| Retweeters | GET | `/twitter/tweet/retweeters?tweetId=` |
| Thread context | GET | `/twitter/tweet/thread_context?tweetId=` |
| Article content | GET | `/twitter/article?tweet_id=` (**snake**) |
| Advanced search | GET | `/twitter/tweet/advanced_search?query=` |
| Bulk advanced search | POST | `/twitter/tweet/bulk_advanced_search` body `{queries:[{query,queryType?,cursor?}]}` |
| Trends | GET | `/twitter/trends?woeid=1&count=30` |
| Space detail | GET | `/twitter/spaces/detail?space_id=` (**snake**) |
| List tweets (with filters) | GET | `/twitter/list/tweets?listId=` |
| List tweets_timeline | GET | `/twitter/list/tweets_timeline?listId=` |
| List members / followers | GET | `/twitter/list/{members,followers}?list_id=` (**snake**) |
| Community info | GET | `/twitter/community/info?community_id=` |
| Community tweets/members/mods | GET | `/twitter/community/{tweets,members,moderators}?community_id=` |
| All-community firehose | GET | `/twitter/community/get_tweets_from_all_community?query=` (required!) |
| Account balance | GET | `/oapi/my/info` |
Real-time monitoring (`/oapi/x_user_stream/*`). **Field names differ per endpoint!**
| Capability | Method | Path & body |
|---|---|---|
| Add monitor | POST | `/oapi/x_user_stream/add_user_to_monitor_tweet` body `{x_user_name}` ← **not `user_id`** |
| List monitors | GET | `/oapi/x_user_stream/get_user_to_monitor_tweet` (+ `query_type` 0/1/2) |
| Remove monitor | POST | `/oapi/x_user_stream/remove_user_to_monitor_tweet` body `{id_for_user}` ← **different field from add** |
Webhook filter rules (`/oapi/tweet_filter/*`):
| Capability | Method | Path & body |
|---|---|---|
| Add rule | POST | `/oapi/tweet_filter/add_rule` body `{tag, value, interval_seconds?=60}` (range 0.05–86400) |
| List rules | GET | `/oapi/tweet_filter/get_rules` |
| Update rule | POST | `/oapi/tweet_filter/update_rule` body `{rule_id, tag, value, interval_seconds?, is_effect?}` |
| Delete rule | DELETE | `/oapi/tweet_filter/delete_rule` body `{rule_id}` (**body JSON, not query**) |
Writes — require `login_cookies` + `proxy` in body. `login_cookies` is **base64(json(cookie_dict))** from `/twitter/user_login_v2`.
| Capability | Method | Path | Key body fields (beyond login_cookies+proxy) |
|---|---|---|---|
| Log in → cookies | POST | `/twitter/user_login_v2` | `{user_name, email, password, totp_secret?, proxy}` |
| Create tweet | POST | `/twitter/create_tweet_v2` | `tweet_text` (req); optional `reply_to_tweet_id`, `quote_tweet_id`, `community_id`, `media_ids[]`, `schedule_for` |
| Delete | POST | `/twitter/delete_tweet_v2` | `tweet_id` |
| Like / Unlike | POST | `/twitter/{like,unlike}_tweet_v2` | `tweet_id` |
| Retweet | POST | `/twitter/retweet_tweet_v2` | `tweet_id` |
| Bookmark / Unbookmark | POST | `/twitter/{bookmark,unbookmark}_tweet_v2` | `tweet_id` |
| List bookmarks | POST | `/twitter/bookmarks_v2` | optional `count` (def 20, **not `pageSize`**), `cursor` |
| Follow / Unfollow | POST | `/twitter/{follow,unfollow}_user_v2` | `user_id` |
| Send DM | POST | `/twitter/send_dm_to_user` | `user_id`, `text`, optional `media_id` (singular), `reply_to_message_id` |
| Report | POST | `/twitter/report_v2` | (`tweet_id` OR `user_id`), `reason` (enum, see endpoints.md) |
| Upload media | POST | `/twitter/upload_media_v2` | **multipart**: `file`, optional `media_category`, `is_long_video` |
| Update profile | PATCH | `/twitter/update_profile_v2` | JSON: at least one of `name`, **`description`** (not `bio`!), `location`, `url` |
| Update avatar/banner | PATCH | `/twitter/update_{avatar,banner}_v2` | **multipart**: `file` |
| Create community | POST | `/twitter/create_community_v2` | `name`, `description` (both required) |
| Join / Leave community | POST | `/twitter/{join,leave}_community_v2` | `community_id` |
| Delete community | POST | `/twitter/delete_community_v2` | `community_id`, **`community_name`** (both required) |
| Add list member | POST | `/twitter/list/add_member_v2` | `list_id`, `user_id` |
For exact param lists, response shapes, and edge cases see [references/endpoints.md](references/endpoints.md).
## Recurring patterns
### Cursor pagination — use `has_next_page`
```python
def iter_followers(user_name):
cursor = ""
while True:
r = requests.get(f"{BASE}/twitter/user/followers",
headers=HEADERS,
params={"userName": user_name, "cursor": cursor, "pageSize": 200},
timeout=30).json()
yield from r.get("followers", [])
if not r.get("has_next_page"):
break
cursor = r.get("next_cursor") or ""
```
### Error handling
```python
# FastAPI-style 4xx/5xx
{ "detail": "tweet_id is required" }
{ "detail": [{"type":"missing","loc":["body","file"]}] } # 422 on multipart
# Semantic 200 errors
{ "status": "error", "msg": "No active monitoring subscription" }
```
- `401` → bad `x-api-key` or expired `login_cookies`
- `402` → top up balance at dashboard
- `429` → rate-limited; exponential backoff
- `400/422` → read `detail` — it names the missing/wrong field
- `500` → transient; retry with jitter. If repeated, the param name is wrong
### Cost awareness
Before a loop that could paginate for a long time (followers of a mega-account, years of advanced search), **estimate cost up front** and confirm with the user if it could exceed a few dollars. A 100M-follower crawl is $15k+ at $0.15/1k.
## Anti-patterns
- Do **not** assume a universal param-casing rule — check endpoints.md
- Do **not** use `login_cookie` (singular) — it's `login_cookies` plural
- Do **not** forget `proxy` in write bodies — every write requires it
- Do **not** send `text` for `create_tweet_v2` — use `tweet_text`
- Do **not** use `bio` in `update_profile_v2` — use `description`
- Do **not** use `in_reply_to_tweet_id` for `create_tweet_v2` — it's `reply_to_tweet_id`
- Do **not** send JSON to `upload_media_v2` / `update_avatar_v2` / `update_banner_v2` — they're **multipart/form-data**
- Do **not** use `pageSize` for `bookmarks_v2` — it's `count`
- Do **not** send `user_id` to monitoring add — it's `x_user_name`; remove uses `id_for_user`
- Do **not** log or commit API keys, `login_cookies`, proxies, or `totp_secret`
- Do **not** use `developer.twitter.com` / official X OAuth — this API replaces them
- Do **not** poll `/user/last_tweets` for "real-time" — use `/oapi/tweet_filter/add_rule` + webhook
Creator's repository · kaitoinfra/twitterapi-io