Read and post to X/Twitter via API. Check mentions, post tweets, search. Use app bearer tokens for read-only fetches and OAuth 1.0a user context for account actions.
78
73%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Optimize this skill with Tessl
npx tessl skill review --optimize ./skills/x-api/SKILL.mdBasic X (Twitter) API access for the @joelclaw account until a proper CLI is built (ADR-0119).
Recommended for read-only access: app bearer token
There is no stored x_bot_bearer_token secret right now. Derive the app bearer token from the X app consumer key + secret, then use that bearer token for read-only endpoints like tweet lookup.
python3 <<'PY'
import base64, json, subprocess
from urllib import request, parse
def lease(name: str) -> str:
raw = subprocess.check_output(['secrets', 'lease', name], text=True)
data = json.loads(raw)
return data.get('secret') or data.get('result') or raw.strip()
ck = lease('x_consumer_key')
cs = lease('x_consumer_secret')
cred = base64.b64encode(f'{ck}:{cs}'.encode()).decode()
req = request.Request(
'https://api.twitter.com/oauth2/token',
data=parse.urlencode({'grant_type': 'client_credentials'}).encode(),
headers={
'Authorization': f'Basic {cred}',
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
},
method='POST',
)
with request.urlopen(req) as r:
bearer = json.loads(r.read().decode())['access_token']
req2 = request.Request(
'https://api.twitter.com/2/tweets/TWEET_ID',
headers={'Authorization': f'Bearer {bearer}'},
)
with request.urlopen(req2) as r:
print(r.read().decode())
PY
secrets revoke --allUse this for:
Alternative: OAuth 1.0a User Context (for posting and account actions)
Tokens do not expire — no refresh dance needed.
x_consumer_key — API Key (OAuth 1.0a consumer key)x_consumer_secret — API Key Secret (OAuth 1.0a consumer secret)x_access_token — Access Token (OAuth 1.0a, format: numeric-alphanumeric)x_access_token_secret — Access Token Secret (OAuth 1.0a)OAuth 1.0a requires cryptographic signing. Use requests-oauthlib (Python) or equivalent:
export CK=$(secrets lease x_consumer_key)
export CS=$(secrets lease x_consumer_secret)
export AT=$(secrets lease x_access_token)
export ATS=$(secrets lease x_access_token_secret)
uv run --with requests-oauthlib python3 << 'PYEOF'
import os
from requests_oauthlib import OAuth1Session
client = OAuth1Session(
os.environ['CK'],
client_secret=os.environ['CS'],
resource_owner_key=os.environ['AT'],
resource_owner_secret=os.environ['ATS'],
)
r = client.get("https://api.twitter.com/2/users/me")
print(r.json())
PYEOFsecrets revoke --allRead-only lookups can use the app bearer-token flow above.
Posting, replying, following, deleting, and account-scoped actions require OAuth 1.0a signing. Use the Python pattern from Authentication section above, then:
# Check mentions
r = client.get("https://api.twitter.com/2/users/2022779096049311744/mentions",
params={"max_results": 10, "tweet.fields": "created_at,author_id,text",
"expansions": "author_id", "user.fields": "username,name"})
# Get my timeline
r = client.get("https://api.twitter.com/2/users/2022779096049311744/tweets",
params={"max_results": 10, "tweet.fields": "created_at,public_metrics"})
# Post a tweet
r = client.post("https://api.twitter.com/2/tweets", json={"text": "your tweet"})
# Reply to a tweet
r = client.post("https://api.twitter.com/2/tweets",
json={"text": "@user reply", "reply": {"in_reply_to_tweet_id": "TWEET_ID"}})
# Search recent tweets
r = client.get("https://api.twitter.com/2/tweets/search/recent",
params={"query": "joelclaw", "max_results": 10, "tweet.fields": "created_at,author_id,text"})
# Get user by username
r = client.get("https://api.twitter.com/2/users/by/username/USERNAME",
params={"user.fields": "description,public_metrics"})
# Follow a user
my_id = "2022779096049311744"
r = client.post(f"https://api.twitter.com/2/users/{my_id}/following",
json={"target_user_id": "TARGET_USER_ID"})
# Delete a tweet
r = client.delete("https://api.twitter.com/2/tweets/TWEET_ID")X Articles are NOT accessible via the v2 tweets API — the tweet body is just a t.co link and the article endpoint returns 500. Use agent-browser to read them:
agent-browser open "https://x.com/USERNAME/status/TWEET_ID"
agent-browser snapshot
agent-browser closeThe snapshot returns the full article text in the DOM. Use this for any tweet where article.title is present in the API response or the expanded_url points to x.com/i/article/....
For capturing X posts/articles as discoveries, use joelclaw discover URL -c "context" — the discovery pipeline will handle enrichment. If the pipeline can't extract content (auth-gated), fall back to agent-browser + manual vault note.
825972c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.