x-api
affaan-m/ecc
Post tweets, read timelines, search, and track analytics on X/Twitter via OAuth.
What is x-api?
Programmatic X/Twitter API integration for posting tweets and threads, reading timelines and user data, searching content, and tracking engagement metrics. Use when you need to interact with X programmatically—posting, reading, or analyzing tweets and accounts.
- Post individual tweets and threaded content programmatically
- Read user timelines, mentions, and account data
- Search X for tweets, trends, and conversations by query
- Upload media (images) and post with attachments
- Track engagement metrics and public analytics
- Handle OAuth 1.0a (write operations) and OAuth 2.0 Bearer (read/search)
How to install x-api
npx skills add null --skill x-api- X Developer account with API access and approved credentials
- OAuth 1.0a credentials (consumer key/secret, access token/secret) for posting
- OAuth 2.0 Bearer token for read-only operations
- Environment variables configured: X_CONSUMER_KEY, X_CONSUMER_SECRET, X_ACCESS_TOKEN, X_ACCESS_TOKEN_SECRET
How to use x-api
- 1.Set up environment variables with your X API credentials (consumer key/secret and access tokens)
- 2.Choose authentication method: OAuth 1.0a for posting/writes, OAuth 2.0 Bearer for read-only operations
- 3.For posting: use oauth.post() to the /tweets endpoint with text payload
- 4.For reading timelines: use requests.get() to /users/{user_id}/tweets with desired fields
- 5.For searching: query the /tweets/search/recent endpoint with your search terms and max_results
- 6.Monitor rate limit headers (x-rate-limit-remaining, x-rate-limit-reset) and implement backoff logic
- 7.For media posts: upload to upload.twitter.com/1.1/media/upload.json first, then reference media_id in tweet payload
Use cases
- Building a bot that posts updates or curated content to X automatically
- Searching X for mentions of a brand or topic and analyzing sentiment
- Pulling a user's recent tweets to extract voice samples for content modeling
- Posting long-form threads programmatically after content generation
- Monitoring timeline and engagement metrics for account analytics
- Social media managers automating posting workflows
- Content creators building X bots or integrations
- Developers building brand voice or content generation pipelines
- Analytics teams tracking X engagement and metrics
- Automation engineers connecting X to larger workflows
x-api FAQ
OAuth 1.0a (user context) is required for posting tweets, managing accounts, and write operations. OAuth 2.0 Bearer (app-only) is simpler and sufficient for read-heavy operations like search and timeline reading.
Post tweets sequentially, using the reply_to_tweet_id field to chain them together. Each tweet's ID becomes the in_reply_to_tweet_id for the next tweet in the thread.
Read the x-rate-limit-reset header to find when the limit resets, wait that duration, then retry. Implement automatic backoff instead of hardcoding static rate limits, as X changes them frequently.
Yes. Upload media first to upload.twitter.com/1.1/media/upload.json, capture the media_id_string, then include it in the media.media_ids array when posting the tweet.
Never. Always use environment variables or .env files, add .env to .gitignore, and rotate tokens immediately if exposed.
Full instructions (SKILL.md)
Source of truth, from affaan-m/ecc.
name: x-api description: X/Twitter API integration for posting tweets, threads, reading timelines, search, and analytics. Covers OAuth auth patterns, rate limits, and platform-native content posting. Use when the user wants to interact with X programmatically. metadata: origin: ECC
X API
Drift-prone skill. X API endpoints, access tiers, quotas, and write permissions change frequently. Verify current developer docs and account access before quoting rate limits or implementing a posting/search flow.
Programmatic interaction with X (Twitter) for posting, reading, searching, and analytics.
When to Activate
- User wants to post tweets or threads programmatically
- Reading timeline, mentions, or user data from X
- Searching X for content, trends, or conversations
- Building X integrations or bots
- Analytics and engagement tracking
- User says "post to X", "tweet", "X API", or "Twitter API"
Authentication
OAuth 2.0 Bearer Token (App-Only)
Best for: read-heavy operations, search, public data.
# Environment setup
export X_BEARER_TOKEN="your-bearer-token"
import os
import requests
bearer = os.environ["X_BEARER_TOKEN"]
headers = {"Authorization": f"Bearer {bearer}"}
# Search recent tweets
resp = requests.get(
"https://api.x.com/2/tweets/search/recent",
headers=headers,
params={"query": "claude code", "max_results": 10}
)
tweets = resp.json()
OAuth 1.0a (User Context)
Required for: posting tweets, managing account, DMs, and any write flow.
# Environment setup — source before use
export X_CONSUMER_KEY="your-consumer-key"
export X_CONSUMER_SECRET="your-consumer-secret"
export X_ACCESS_TOKEN="your-access-token"
export X_ACCESS_TOKEN_SECRET="your-access-token-secret"
Legacy aliases such as X_API_KEY, X_API_SECRET, and X_ACCESS_SECRET may exist in older setups. Prefer the X_CONSUMER_* and X_ACCESS_TOKEN_SECRET names when documenting or wiring new flows.
import os
from requests_oauthlib import OAuth1Session
oauth = OAuth1Session(
os.environ["X_CONSUMER_KEY"],
client_secret=os.environ["X_CONSUMER_SECRET"],
resource_owner_key=os.environ["X_ACCESS_TOKEN"],
resource_owner_secret=os.environ["X_ACCESS_TOKEN_SECRET"],
)
Core Operations
Post a Tweet
resp = oauth.post(
"https://api.x.com/2/tweets",
json={"text": "Hello from Claude Code"}
)
resp.raise_for_status()
tweet_id = resp.json()["data"]["id"]
Post a Thread
def post_thread(oauth, tweets: list[str]) -> list[str]:
ids = []
reply_to = None
for text in tweets:
payload = {"text": text}
if reply_to:
payload["reply"] = {"in_reply_to_tweet_id": reply_to}
resp = oauth.post("https://api.x.com/2/tweets", json=payload)
tweet_id = resp.json()["data"]["id"]
ids.append(tweet_id)
reply_to = tweet_id
return ids
Read User Timeline
resp = requests.get(
f"https://api.x.com/2/users/{user_id}/tweets",
headers=headers,
params={
"max_results": 10,
"tweet.fields": "created_at,public_metrics",
}
)
Search Tweets
resp = requests.get(
"https://api.x.com/2/tweets/search/recent",
headers=headers,
params={
"query": "from:affaanmustafa -is:retweet",
"max_results": 10,
"tweet.fields": "public_metrics,created_at",
}
)
Pull Recent Original Posts for Voice Modeling
resp = requests.get(
"https://api.x.com/2/tweets/search/recent",
headers=headers,
params={
"query": "from:affaanmustafa -is:retweet -is:reply",
"max_results": 25,
"tweet.fields": "created_at,public_metrics",
}
)
voice_samples = resp.json()
Get User by Username
resp = requests.get(
"https://api.x.com/2/users/by/username/affaanmustafa",
headers=headers,
params={"user.fields": "public_metrics,description,created_at"}
)
Upload Media and Post
# Media upload uses v1.1 endpoint
# Step 1: Upload media
media_resp = oauth.post(
"https://upload.twitter.com/1.1/media/upload.json",
files={"media": open("image.png", "rb")}
)
media_id = media_resp.json()["media_id_string"]
# Step 2: Post with media
resp = oauth.post(
"https://api.x.com/2/tweets",
json={"text": "Check this out", "media": {"media_ids": [media_id]}}
)
Rate Limits
X API rate limits vary by endpoint, auth method, and account tier, and they change over time. Always:
- Check the current X developer docs before hardcoding assumptions
- Read
x-rate-limit-remainingandx-rate-limit-resetheaders at runtime - Back off automatically instead of relying on static tables in code
import time
remaining = int(resp.headers.get("x-rate-limit-remaining", 0))
if remaining < 5:
reset = int(resp.headers.get("x-rate-limit-reset", 0))
wait = max(0, reset - int(time.time()))
print(f"Rate limit approaching. Resets in {wait}s")
Error Handling
resp = oauth.post("https://api.x.com/2/tweets", json={"text": content})
if resp.status_code == 201:
return resp.json()["data"]["id"]
elif resp.status_code == 429:
reset = int(resp.headers["x-rate-limit-reset"])
raise Exception(f"Rate limited. Resets at {reset}")
elif resp.status_code == 403:
raise Exception(f"Forbidden: {resp.json().get('detail', 'check permissions')}")
else:
raise Exception(f"X API error {resp.status_code}: {resp.text}")
Security
- Never hardcode tokens. Use environment variables or
.envfiles. - Never commit
.envfiles. Add to.gitignore. - Rotate tokens if exposed. Regenerate at developer.x.com.
- Use read-only tokens when write access is not needed.
- Store OAuth secrets securely — not in source code or logs.
Integration with Content Engine
Use brand-voice plus content-engine to generate platform-native content, then post via X API:
- Pull recent original posts when voice matching matters
- Build or reuse a
VOICE PROFILE - Generate content with
content-enginein X-native format - Validate length and thread structure
- Return the draft for approval unless the user explicitly asked to post now
- Post via X API only after approval
- Track engagement via public_metrics
Related Skills
brand-voice— Build a reusable voice profile from real X and site/source materialcontent-engine— Generate platform-native content for Xcrosspost— Distribute content across X, LinkedIn, and other platformsconnections-optimizer— Reorganize the X graph before drafting network-driven outreach
Related skills
More from affaan-m/ecc and the wider catalog.
accessibility
Design and audit inclusive digital products using WCAG 2.2 Level AA standards across Web, iOS, and Android.
agent-architecture-audit
Full-stack diagnostic for agent and LLM applications—audits 12-layer stack for wrapper regression, memory pollution, and tool discipline failures.
agent-eval
Systematically benchmark coding agents on your tasks with pass rate, cost, time, and consistency metrics.
agent-introspection-debugging
Structured self-debugging workflow for AI agents to diagnose and recover from failures systematically.

agent-harness-construction
Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates.

autonomous-agent-harness
Transform Claude Code into a persistent autonomous agent with memory, scheduling, and computer use—no external frameworks needed.