twitter

Public
0

Repository: resciencelab/opc-skills

Log in or sign up to clone this skill.

R
resciencelab
Imported Feb 28, 2026

Low Risk

No security issues found

INFO

Skill manifest does not include a 'license' field. Specifying a license helps users understand usage terms.

Remediation Add 'license' field to SKILL.md frontmatter (e.g., MIT, Apache-2.0)

Scanned in 0.003s

Description

Search and retrieve content from Twitter/X. Get user info, tweets, replies, followers, communities, spaces, and trends via twitterapi.io. Use when user mentions Twitter, X, or tweets.

Skill Files

Download .zip
SKILL.md
# Twitter/X Skill

Get user profiles, tweets, replies, followers/following, communities, spaces, and trends from Twitter/X via twitterapi.io.

## Prerequisites

Set API key in `~/.zshrc`:
```bash
export TWITTERAPI_API_KEY="your_api_key"
```

**Quick Check**:
```bash
cd <skill_directory>
python3 scripts/get_user_info.py elonmusk
```

## Commands

All commands run from the skill directory.

### User Endpoints
```bash
python3 scripts/get_user_info.py USERNAME
python3 scripts/get_user_about.py USERNAME
python3 scripts/batch_get_users.py USER_ID1,USER_ID2
python3 scripts/get_user_tweets.py USERNAME --limit 20
python3 scripts/get_user_mentions.py USERNAME --limit 20
python3 scripts/get_followers.py USERNAME --limit 100
python3 scripts/get_following.py USERNAME --limit 100
python3 scripts/get_verified_followers.py USERNAME --limit 20
python3 scripts/check_relationship.py USER1 USER2
python3 scripts/search_users.py "query" --limit 20
```

### Tweet Endpoints
```bash
python3 scripts/get_tweet.py TWEET_ID [TWEET_ID2...]
python3 scripts/search_tweets.py "query" --type Latest --limit 20
python3 scripts/get_tweet_replies.py TWEET_ID --limit 20
python3 scripts/get_tweet_quotes.py TWEET_ID --limit 20
python3 scripts/get_tweet_retweeters.py TWEET_ID --limit 50
python3 scripts/get_tweet_thread.py TWEET_ID
python3 scripts/get_article.py TWEET_ID
```

### List Endpoints
```bash
python3 scripts/get_list_followers.py LIST_ID --limit 20
python3 scripts/get_list_members.py LIST_ID --limit 20
```

### Community Endpoints
```bash
python3 scripts/get_community.py COMMUNITY_ID
python3 scripts/get_community_members.py COMMUNITY_ID --limit 20
python3 scripts/get_community_moderators.py COMMUNITY_ID
python3 scripts/get_community_tweets.py COMMUNITY_ID --limit 20
python3 scripts/search_community_tweets.py "query" --limit 20
```

### Other Endpoints
```bash
python3 scripts/get_space.py SPACE_ID
python3 scripts/get_trends.py --woeid 1  # Worldwide
```

## Search Query Syntax

```bash
# Basic search
python3 scripts/search_tweets.py "AI agent"

# From specific user
python3 scripts/search_tweets.py "from:elonmusk"

# Date range
python3 scripts/search_tweets.py "AI since:2024-01-01 until:2024-12-31"

# Exclude retweets
python3 scripts/search_tweets.py "AI -filter:retweets"

# With media
python3 scripts/search_tweets.py "AI filter:media"

# Minimum engagement
python3 scripts/search_tweets.py "AI min_faves:1000"
```

## API: twitterapi.io
- Base URL: https://api.twitterapi.io/twitter
- Auth: X-API-Key header
- Pricing: ~$0.15-0.18/1k requests
- Docs: https://docs.twitterapi.io/
.claude-plugin/plugin.json Reference
{
  "name": "twitter",
  "version": "1.0.0",
  "description": "Search and retrieve content from Twitter/X. Get user info, tweets, replies, followers, communities, spaces, and trends via twitterapi.io.",
  "author": {
    "name": "ReScienceLab"
  },
  "homepage": "https://github.com/ReScienceLab/opc-skills/tree/main/skills/twitter",
  "repository": "https://github.com/ReScienceLab/opc-skills",
  "license": "MIT",
  "keywords": [
    "twitter",
    "X",
    "tweet"
  ],
  "skills": [
    "./SKILL.md"
  ],
  "commands": [
    "./scripts/"
  ]
}
scripts/batch_get_users.py Script
#!/usr/bin/env python3
"""
Batch get user info by user IDs
Usage: python3 scripts/batch_get_users.py USER_ID1,USER_ID2,USER_ID3
"""
import argparse
from twitter_api import api_get, print_users_list


def main():
    parser = argparse.ArgumentParser(description="Batch get Twitter users by IDs")
    parser.add_argument("user_ids", help="Comma-separated user IDs")
    args = parser.parse_args()

    data = api_get("user/batch_info_by_ids", {"userIds": args.user_ids})
    users = data.get("users") or data.get("data") or []
    
    print(f"total: {len(users)}")
    print_users_list(users)


if __name__ == "__main__":
    main()
scripts/check_relationship.py Script
#!/usr/bin/env python3
"""
Check follow relationship between two users
Usage: python3 scripts/check_relationship.py USER1 USER2
"""
import argparse
from twitter_api import api_get


def main():
    parser = argparse.ArgumentParser(description="Check follow relationship")
    parser.add_argument("source", help="Source username")
    parser.add_argument("target", help="Target username")
    args = parser.parse_args()

    params = {"source_user_name": args.source, "target_user_name": args.target}
    data = api_get("user/check_follow_relationship", params)
    result = data.get("data") or data

    print(f"source: @{args.source}")
    print(f"target: @{args.target}")
    print(f"source_follows_target: {result.get('following', False)}")
    print(f"target_follows_source: {result.get('followed_by', False)}")


if __name__ == "__main__":
    main()
scripts/credential.py Script
#!/usr/bin/env python3
"""
Twitter API credential management.
Reads from environment: TWITTERAPI_API_KEY
"""
import os


def get_twitter_api_key() -> str | None:
    """Get TwitterAPI.io API key"""
    return os.environ.get("TWITTERAPI_API_KEY")


def has_twitter_key() -> bool:
    """Check if API key is available"""
    return get_twitter_api_key() is not None
scripts/get_article.py Script
#!/usr/bin/env python3
"""
Get long-form article from tweet
Usage: python3 scripts/get_article.py TWEET_ID
"""
import argparse
import json
from twitter_api import api_get


def main():
    parser = argparse.ArgumentParser(description="Get Twitter article")
    parser.add_argument("tweet_id", help="Tweet ID with article")
    parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
    args = parser.parse_args()

    data = api_get("article", {"tweet_id": args.tweet_id})
    article = data.get("data") or data

    if args.json:
        print(json.dumps(article, indent=2))
        return

    print(f"tweet_id: {args.tweet_id}")
    if article.get("title"):
        print(f"title: {article['title']}")
    if article.get("content"):
        print(f"---")
        print(article["content"][:2000])
        if len(article.get("content", "")) > 2000:
            print(f"... (truncated, {len(article['content'])} chars total)")


if __name__ == "__main__":
    main()
scripts/get_community.py Script
#!/usr/bin/env python3
"""
Get community info by ID
Usage: python3 scripts/get_community.py COMMUNITY_ID
"""
import argparse
import json
from twitter_api import api_get, format_count


def main():
    parser = argparse.ArgumentParser(description="Get community info")
    parser.add_argument("community_id", help="Community ID")
    parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
    args = parser.parse_args()

    data = api_get("community", {"communityId": args.community_id})
    community = data.get("data") or data

    if args.json:
        print(json.dumps(community, indent=2))
        return

    print(f"id: {community.get('id', '')}")
    print(f"name: {community.get('name', '')}")
    print(f"description: {community.get('description', '')[:200]}")
    print(f"members: {format_count(community.get('member_count'))}")
    print(f"created: {community.get('created_at', '')}")


if __name__ == "__main__":
    main()
scripts/get_community_members.py Script
#!/usr/bin/env python3
"""
Get community members
Usage: python3 scripts/get_community_members.py COMMUNITY_ID --limit 20
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get community members")
    parser.add_argument("community_id", help="Community ID")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max members")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"communityId": args.community_id, "cursor": args.cursor}
    data = api_get("community/members", params)
    users = (data.get("members") or data.get("users") or [])[:args.limit]

    print(f"community_id: {args.community_id}")
    print_users_list(users, "members")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_community_moderators.py Script
#!/usr/bin/env python3
"""
Get community moderators
Usage: python3 scripts/get_community_moderators.py COMMUNITY_ID
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get community moderators")
    parser.add_argument("community_id", help="Community ID")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"communityId": args.community_id, "cursor": args.cursor}
    data = api_get("community/moderators", params)
    users = data.get("moderators") or data.get("users") or []

    print(f"community_id: {args.community_id}")
    print_users_list(users, "moderators")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_community_tweets.py Script
#!/usr/bin/env python3
"""
Get community tweets
Usage: python3 scripts/get_community_tweets.py COMMUNITY_ID --limit 20
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get community tweets")
    parser.add_argument("community_id", help="Community ID")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max tweets")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"communityId": args.community_id, "cursor": args.cursor}
    data = api_get("community/tweets", params)
    tweets = (data.get("tweets") or [])[:args.limit]

    print(f"community_id: {args.community_id}")
    print_tweets_list(tweets)
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_followers.py Script
#!/usr/bin/env python3
"""
Get user's followers
Usage: python3 scripts/get_followers.py USERNAME --limit 100
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get user's followers")
    parser.add_argument("username", help="Twitter username (without @)")
    parser.add_argument("--limit", "-l", type=int, default=100, help="Max followers (max 200/page)")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {
        "userName": args.username,
        "cursor": args.cursor,
        "pageSize": min(args.limit, 200),
    }
    data = api_get("user/followers", params)
    users = (data.get("followers") or data.get("users") or [])[:args.limit]

    print(f"username: @{args.username}")
    print_users_list(users, "followers")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_following.py Script
#!/usr/bin/env python3
"""
Get user's following
Usage: python3 scripts/get_following.py USERNAME --limit 100
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get user's following")
    parser.add_argument("username", help="Twitter username (without @)")
    parser.add_argument("--limit", "-l", type=int, default=100, help="Max following (max 200/page)")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {
        "userName": args.username,
        "cursor": args.cursor,
        "pageSize": min(args.limit, 200),
    }
    data = api_get("user/followings", params)
    users = (data.get("followings") or data.get("users") or [])[:args.limit]

    print(f"username: @{args.username}")
    print_users_list(users, "following")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_list_followers.py Script
#!/usr/bin/env python3
"""
Get list followers
Usage: python3 scripts/get_list_followers.py LIST_ID --limit 20
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get list followers")
    parser.add_argument("list_id", help="List ID")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max followers")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"list_id": args.list_id, "cursor": args.cursor}
    data = api_get("list/followers", params)
    users = (data.get("followers") or data.get("users") or [])[:args.limit]

    print(f"list_id: {args.list_id}")
    print_users_list(users, "followers")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_list_members.py Script
#!/usr/bin/env python3
"""
Get list members
Usage: python3 scripts/get_list_members.py LIST_ID --limit 20
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get list members")
    parser.add_argument("list_id", help="List ID")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max members")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"list_id": args.list_id, "cursor": args.cursor}
    data = api_get("list/members", params)
    users = (data.get("members") or data.get("users") or [])[:args.limit]

    print(f"list_id: {args.list_id}")
    print_users_list(users, "members")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_space.py Script
#!/usr/bin/env python3
"""
Get Twitter Space details
Usage: python3 scripts/get_space.py SPACE_ID
"""
import argparse
import json
from twitter_api import api_get, format_count


def main():
    parser = argparse.ArgumentParser(description="Get Twitter Space details")
    parser.add_argument("space_id", help="Space ID")
    parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
    args = parser.parse_args()

    data = api_get("space", {"spaceId": args.space_id})
    space = data.get("data") or data

    if args.json:
        print(json.dumps(space, indent=2))
        return

    print(f"id: {space.get('id', '')}")
    print(f"title: {space.get('title', '')}")
    print(f"state: {space.get('state', '')}")
    print(f"host: @{space.get('creator', {}).get('userName', '')}")
    print(f"participants: {format_count(space.get('participant_count'))}")
    print(f"created: {space.get('created_at', '')}")
    print(f"started: {space.get('started_at', '')}")


if __name__ == "__main__":
    main()
scripts/get_trends.py Script
#!/usr/bin/env python3
"""
Get Twitter trends by location
Usage: python3 scripts/get_trends.py --woeid 1
"""
import argparse
import json
from twitter_api import api_get, format_count


# Common WOEIDs
WOEIDS = {
    "worldwide": 1,
    "usa": 23424977,
    "uk": 23424975,
    "japan": 23424856,
    "india": 23424848,
    "brazil": 23424768,
    "canada": 23424775,
    "australia": 23424748,
    "germany": 23424829,
    "france": 23424819,
}


def main():
    parser = argparse.ArgumentParser(description="Get Twitter trends")
    parser.add_argument("--woeid", "-w", type=int, default=1, 
                        help="WOEID (1=Worldwide, 23424977=USA)")
    parser.add_argument("--location", "-l", choices=list(WOEIDS.keys()),
                        help="Location name (alternative to woeid)")
    parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
    args = parser.parse_args()

    woeid = WOEIDS.get(args.location, args.woeid) if args.location else args.woeid
    data = api_get("trends", {"woeid": woeid})
    trends = data.get("trends") or data.get("data") or []

    if args.json:
        print(json.dumps(trends, indent=2))
        return

    print(f"woeid: {woeid}")
    print(f"trends[{len(trends)}]{{name,posts}}:")
    for t in trends[:30]:
        trend = t.get("trend", t)
        name = trend.get("name", "")
        posts = trend.get("meta_description", "-")
        print(f"  {name},{posts}")


if __name__ == "__main__":
    main()
scripts/get_tweet.py Script
#!/usr/bin/env python3
"""
Get tweets by IDs
Usage: python3 scripts/get_tweet.py TWEET_ID [TWEET_ID2...]
"""
import argparse
from twitter_api import api_get, clean_tweet, print_tweet


def main():
    parser = argparse.ArgumentParser(description="Get tweets by IDs")
    parser.add_argument("tweet_ids", nargs="+", help="Tweet ID(s)")
    args = parser.parse_args()

    ids = ",".join(args.tweet_ids)
    data = api_get("tweets", {"tweet_ids": ids})
    tweets = data.get("tweets") or []

    for i, tweet in enumerate(tweets):
        if i > 0:
            print("---")
        print_tweet(clean_tweet(tweet))


if __name__ == "__main__":
    main()
scripts/get_tweet_quotes.py Script
#!/usr/bin/env python3
"""
Get quote tweets
Usage: python3 scripts/get_tweet_quotes.py TWEET_ID --limit 20
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get quote tweets")
    parser.add_argument("tweet_id", help="Tweet ID")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max quotes")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"tweetId": args.tweet_id, "cursor": args.cursor}
    data = api_get("tweet/quotes", params)
    tweets = (data.get("tweets") or data.get("quotes") or [])[:args.limit]

    print(f"tweet_id: {args.tweet_id}")
    print_tweets_list(tweets, "quotes")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_tweet_replies.py Script
#!/usr/bin/env python3
"""
Get tweet replies
Usage: python3 scripts/get_tweet_replies.py TWEET_ID --limit 20
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get tweet replies")
    parser.add_argument("tweet_id", help="Tweet ID")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max replies")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"tweetId": args.tweet_id, "cursor": args.cursor}
    data = api_get("tweet/replies", params)
    tweets = (data.get("tweets") or data.get("replies") or [])[:args.limit]

    print(f"tweet_id: {args.tweet_id}")
    print_tweets_list(tweets, "replies")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_tweet_retweeters.py Script
#!/usr/bin/env python3
"""
Get users who retweeted a tweet
Usage: python3 scripts/get_tweet_retweeters.py TWEET_ID --limit 50
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get tweet retweeters")
    parser.add_argument("tweet_id", help="Tweet ID")
    parser.add_argument("--limit", "-l", type=int, default=50, help="Max retweeters")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"tweetId": args.tweet_id, "cursor": args.cursor}
    data = api_get("tweet/retweeters", params)
    users = (data.get("users") or data.get("retweeters") or [])[:args.limit]

    print(f"tweet_id: {args.tweet_id}")
    print_users_list(users, "retweeters")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_tweet_thread.py Script
#!/usr/bin/env python3
"""
Get tweet thread context
Usage: python3 scripts/get_tweet_thread.py TWEET_ID
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get tweet thread context")
    parser.add_argument("tweet_id", help="Tweet ID")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"tweetId": args.tweet_id, "cursor": args.cursor}
    data = api_get("tweet/thread_context", params)
    tweets = data.get("replies") or data.get("tweets") or []

    print(f"tweet_id: {args.tweet_id}")
    print_tweets_list(tweets, "thread")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_user_about.py Script
#!/usr/bin/env python3
"""
Get user profile about (extended info)
Usage: python3 scripts/get_user_about.py USERNAME
"""
import argparse
import json
from twitter_api import api_get


def main():
    parser = argparse.ArgumentParser(description="Get Twitter user about/profile")
    parser.add_argument("username", help="Twitter username (without @)")
    parser.add_argument("--json", "-j", action="store_true", help="Output as JSON")
    args = parser.parse_args()

    data = api_get("user_about", {"userName": args.username})
    user = data.get("data") or data

    if args.json:
        print(json.dumps(user, indent=2))
        return

    print(f"id: {user.get('id', '')}")
    print(f"username: @{user.get('userName', '')}")
    print(f"name: {user.get('name', '')}")
    print(f"verified: {user.get('isBlueVerified', False)}")
    print(f"protected: {user.get('protected', False)}")
    print(f"created: {user.get('createdAt', '')}")
    
    about = user.get("about_profile", {})
    if about:
        if about.get("account_based_in"):
            print(f"based_in: {about['account_based_in']}")
        changes = about.get("username_changes", {})
        if changes.get("count"):
            print(f"username_changes: {changes['count']}")


if __name__ == "__main__":
    main()
scripts/get_user_info.py Script
#!/usr/bin/env python3
"""
Get user info by username
Usage: python3 scripts/get_user_info.py USERNAME
"""
import argparse
from twitter_api import api_get, clean_user, print_user


def main():
    parser = argparse.ArgumentParser(description="Get Twitter user info")
    parser.add_argument("username", help="Twitter username (without @)")
    args = parser.parse_args()

    data = api_get("user/info", {"userName": args.username})
    user = data.get("data") or data
    print_user(clean_user(user))


if __name__ == "__main__":
    main()
scripts/get_user_mentions.py Script
#!/usr/bin/env python3
"""
Get user's mentions
Usage: python3 scripts/get_user_mentions.py USERNAME --limit 20
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get user's mentions")
    parser.add_argument("username", help="Twitter username (without @)")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max mentions")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"userName": args.username, "cursor": args.cursor}
    data = api_get("user/mentions", params)
    tweets = (data.get("tweets") or [])[:args.limit]

    print(f"username: @{args.username}")
    print_tweets_list(tweets, "mentions")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_user_tweets.py Script
#!/usr/bin/env python3
"""
Get user's recent tweets
Usage: python3 scripts/get_user_tweets.py USERNAME --limit 20
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get user's tweets")
    parser.add_argument("username", help="Twitter username (without @)")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max tweets")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    parser.add_argument("--include-replies", action="store_true", help="Include replies")
    args = parser.parse_args()

    params = {
        "userName": args.username,
        "cursor": args.cursor,
        "includeReplies": "true" if args.include_replies else "false",
    }
    data = api_get("user/last_tweets", params)
    tweets = (data.get("tweets") or [])[:args.limit]

    print(f"username: @{args.username}")
    print_tweets_list(tweets)
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/get_verified_followers.py Script
#!/usr/bin/env python3
"""
Get user's verified followers only
Usage: python3 scripts/get_verified_followers.py USERNAME --limit 20
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Get user's verified followers")
    parser.add_argument("username", help="Twitter username (without @)")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max results")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    # First get user_id from username
    user_data = api_get("user/info", {"userName": args.username})
    user_id = (user_data.get("data") or user_data).get("id")
    
    params = {"user_id": user_id, "cursor": args.cursor}
    data = api_get("user/verifiedFollowers", params)
    users = (data.get("followers") or data.get("users") or [])[:args.limit]

    print(f"username: @{args.username}")
    print_users_list(users, "verified_followers")
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/search_community_tweets.py Script
#!/usr/bin/env python3
"""
Search tweets from all communities
Usage: python3 scripts/search_community_tweets.py "query" --limit 20
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Search community tweets")
    parser.add_argument("query", help="Search query")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max tweets")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"query": args.query, "queryType": "Latest", "cursor": args.cursor}
    data = api_get("community/get_tweets_from_all_community", params)
    tweets = (data.get("tweets") or [])[:args.limit]

    print(f"query: {args.query}")
    print_tweets_list(tweets)
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/search_tweets.py Script
#!/usr/bin/env python3
"""
Advanced tweet search
Usage: python3 scripts/search_tweets.py "query" --type Latest --limit 20
"""
import argparse
from twitter_api import api_get, print_tweets_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Search tweets")
    parser.add_argument("query", help="Search query")
    parser.add_argument("--type", "-t", choices=["Latest", "Top"], default="Latest",
                        help="Query type (default: Latest)")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max results")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {
        "query": args.query,
        "queryType": args.type,
        "cursor": args.cursor,
    }
    data = api_get("tweet/advanced_search", params)
    tweets = (data.get("tweets") or [])[:args.limit]

    print(f"query: {args.query}")
    print(f"type: {args.type}")
    print_tweets_list(tweets)
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/search_users.py Script
#!/usr/bin/env python3
"""
Search users by keyword
Usage: python3 scripts/search_users.py "AI researcher" --limit 20
"""
import argparse
from twitter_api import api_get, print_users_list, print_pagination


def main():
    parser = argparse.ArgumentParser(description="Search Twitter users")
    parser.add_argument("query", help="Search query")
    parser.add_argument("--limit", "-l", type=int, default=20, help="Max results")
    parser.add_argument("--cursor", "-c", help="Pagination cursor")
    args = parser.parse_args()

    params = {"query": args.query, "cursor": args.cursor}
    data = api_get("user/search", params)
    users = (data.get("users") or [])[:args.limit]

    print(f"query: {args.query}")
    print_users_list(users)
    print_pagination(data)


if __name__ == "__main__":
    main()
scripts/twitter_api.py Script
#!/usr/bin/env python3
"""
Twitter API wrapper using twitterapi.io
"""
import urllib.request
import urllib.parse
import json
import sys
from credential import get_twitter_api_key

API_BASE = "https://api.twitterapi.io/twitter"


def api_get(endpoint: str, params: dict = None) -> dict:
    """Make GET request to twitterapi.io"""
    api_key = get_twitter_api_key()
    if not api_key:
        print("error: TWITTERAPI_API_KEY not set", file=sys.stderr)
        sys.exit(1)
    
    url = f"{API_BASE}/{endpoint}"
    if params:
        filtered = {k: v for k, v in params.items() if v is not None}
        if filtered:
            url += "?" + urllib.parse.urlencode(filtered)
    
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0", "X-API-Key": api_key})
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode())
    except urllib.error.HTTPError as e:
        error_body = e.read().decode()
        print(f"error: HTTP {e.code} - {error_body}", file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(f"error: {e}", file=sys.stderr)
        sys.exit(1)


def format_count(n) -> str:
    """Format numbers (1234567 -> 1.2M)"""
    if n is None:
        return "0"
    n = int(n)
    if n >= 1_000_000_000:
        return f"{n/1_000_000_000:.1f}B"
    if n >= 1_000_000:
        return f"{n/1_000_000:.1f}M"
    if n >= 1_000:
        return f"{n/1_000:.1f}K"
    return str(n)


def clean_user(u: dict) -> dict:
    """Clean user object"""
    if not u:
        return None
    return {
        "id": u.get("id"),
        "username": u.get("userName"),
        "name": u.get("name"),
        "verified": u.get("isBlueVerified"),
        "followers": u.get("followers"),
        "following": u.get("following"),
        "tweets": u.get("statusesCount"),
        "description": (u.get("description") or "")[:150],
        "location": u.get("location"),
        "created": u.get("createdAt"),
    }


def clean_tweet(t: dict) -> dict:
    """Clean tweet object"""
    if not t:
        return None
    author = t.get("author", {})
    return {
        "id": t.get("id"),
        "text": t.get("text"),
        "author": author.get("userName") if author else None,
        "author_name": author.get("name") if author else None,
        "created": t.get("createdAt"),
        "retweets": t.get("retweetCount"),
        "likes": t.get("likeCount"),
        "replies": t.get("replyCount"),
        "quotes": t.get("quoteCount"),
        "views": t.get("viewCount"),
        "lang": t.get("lang"),
        "isReply": t.get("isReply"),
    }


def print_user(u: dict):
    """Print user in TOON format"""
    if not u:
        return
    print(f"id: {u.get('id', '')}")
    print(f"username: @{u.get('username', '')}")
    print(f"name: {u.get('name', '')}")
    print(f"verified: {u.get('verified', False)}")
    print(f"followers: {format_count(u.get('followers'))}")
    print(f"following: {format_count(u.get('following'))}")
    print(f"tweets: {format_count(u.get('tweets'))}")
    if u.get('location'):
        print(f"location: {u['location']}")
    if u.get('description'):
        print(f"bio: {u['description']}")
    if u.get('created'):
        print(f"created: {u['created']}")


def print_tweet(t: dict):
    """Print tweet in TOON format"""
    if not t:
        return
    print(f"id: {t.get('id', '')}")
    print(f"author: @{t.get('author', '')} ({t.get('author_name', '')})")
    text = (t.get('text') or '')[:280]
    print(f"text: {text}")
    print(f"stats: {format_count(t.get('retweets'))} RT | {format_count(t.get('likes'))} likes | {format_count(t.get('replies'))} replies | {format_count(t.get('views'))} views")
    print(f"created: {t.get('created', '')}")


def print_users_list(users: list, label: str = "users"):
    """Print list of users"""
    cleaned = [clean_user(u) for u in users if u]
    print(f"{label}[{len(cleaned)}]{{username,name,followers,verified}}:")
    for u in cleaned:
        print(f"  @{u['username']},{u['name']},{format_count(u['followers'])},{u['verified']}")


def print_tweets_list(tweets: list, label: str = "tweets"):
    """Print list of tweets"""
    cleaned = [clean_tweet(t) for t in tweets if t]
    print(f"{label}[{len(cleaned)}]{{id,author,text,likes}}:")
    for t in cleaned:
        text = (t['text'] or '')[:60].replace('\n', ' ')
        print(f"  {t['id']},@{t['author']},{text},{format_count(t['likes'])}")


def print_pagination(data: dict):
    """Print pagination info"""
    has_next = data.get("has_next_page", False)
    cursor = data.get("next_cursor", "")
    if has_next and cursor:
        print(f"---")
        print(f"has_next_page: {has_next}")
        print(f"next_cursor: {cursor}")

Version History

v1.0.0 Imported from GitHub
1 week ago