Skip to main content

TweetAPI Python SDK

tweetapi is the Python SDK for TweetAPI. It provides methods for users, tweets, posts, profiles, interactions, lists, communities, Spaces, search, authentication, and direct messages.

Install

pip install tweetapi

Quick start

from tweetapi import TweetAPI

client = TweetAPI(api_key="YOUR_API_KEY")

# Get a user profile
user = client.user.get_by_username(username="elonmusk")
print(user["data"]["followerCount"])

# Search tweets
results = client.explore.search(query="bitcoin", type="Latest")

# Get followers with pagination
followers = client.user.get_followers(user_id="123456")
next_page = client.user.get_followers(
    user_id="123456",
    cursor=followers["pagination"]["nextCursor"],
)

Create an API key with 100 included requests. No credit card is required.

SDK behavior

  • Public methods have type annotations, and response models use TypedDict.
  • Reads retry rate limits, server errors, timeouts, and connection failures by default. Writes retry only when the API confirms the request never reached X, so an action is never sent twice. Username changes and accepting or denying follow requests disable automatic retries.
  • Errors that a retry cannot fix fail at once: a failing proxy or a used-up or expired plan.
  • paginate() yields items from cursor-based responses; paginate_pages() yields full response pages.
  • HTTP errors map to exception classes such as RateLimitError and NotFoundError.
  • Timeouts can use one value or separate connect and read values.
  • The package supports Python 3.9+ and depends on requests.

API reference

User

Method Description
client.user.get_by_username(username=...) Get user profile by username
client.user.get_by_usernames(usernames=...) Get multiple users (comma-separated)
client.user.get_by_user_id(user_id=...) Get user profile by ID
client.user.get_by_user_ids(user_ids=...) Get multiple users by IDs
client.user.get_tweets(user_id=...) Get a user's tweets
client.user.get_tweets_and_replies(user_id=...) Get tweets and replies
client.user.get_following(user_id=...) Get who a user follows
client.user.get_followers(user_id=...) Get a user's followers
client.user.get_verified_followers(user_id=...) Get verified followers
client.user.get_subscriptions(user_id=...) Get subscriptions
client.user.get_following_v1(user_id=...) Get following (v1, supports count)
client.user.get_followers_v1(user_id=...) Get followers (v1, supports count)
client.user.get_following_ids(user_id=...) Get following user IDs
client.user.get_followers_ids(user_id=...) Get follower user IDs
client.user.check_follow(subject_id=..., target_id=...) Check follow relationship
client.user.about_account(username=...) Get account transparency info

Tweet

Method Description
client.tweet.get_details_and_conversation(tweet_id=...) Get tweet details and replies
client.tweet.get_details_by_ids(ids=...) Get multiple tweets (max 200)
client.tweet.get_retweets(tweet_id=...) Get users who retweeted
client.tweet.get_quotes(tweet_id=...) Get quote tweets
client.tweet.translate(tweet_id=..., dst_lang=...) Translate a tweet

Post

Method Description
client.post.create_post(auth_token=..., text=..., proxy=..., reply_option=...) Create a tweet
client.post.create_post_quote(auth_token=..., text=..., attachment_url=..., proxy=..., reply_option=...) Quote tweet
client.post.create_post_with_media(auth_token=..., text=..., media=..., proxy=..., reply_option=...) Tweet with media
client.post.reply_post(auth_token=..., text=..., tweet_id=..., proxy=...) Reply to a tweet
client.post.reply_post_with_media(...) Reply with media
client.post.delete_post(auth_token=..., tweet_id=...) Delete a tweet

Profile

Method Description
client.profile.update(auth_token=..., name=..., bio=..., location=..., website=..., proxy=...) Update profile fields
client.profile.avatar(auth_token=..., media=..., proxy=...) Update profile avatar
client.profile.banner(auth_token=..., media=..., proxy=...) Update profile banner
client.profile.remove_banner(auth_token=..., proxy=...) Remove the profile banner
client.profile.set_privacy(auth_token=..., is_private=..., proxy=...) Make the account public or private
client.profile.update_username(auth_token=..., password=..., username=..., proxy=...) Update the account username

Interaction

Method Description
client.interaction.favorite_post(auth_token=..., tweet_id=...) Like a tweet
client.interaction.unfavorite_post(auth_token=..., tweet_id=...) Unlike a tweet
client.interaction.retweet(auth_token=..., tweet_id=...) Retweet
client.interaction.delete_retweet(auth_token=..., tweet_id=...) Remove retweet
client.interaction.bookmark(auth_token=..., tweet_id=...) Bookmark a tweet
client.interaction.delete_bookmark(auth_token=..., tweet_id=...) Remove bookmark
client.interaction.follow(auth_token=..., user_id=...) Follow a user
client.interaction.unfollow(auth_token=..., user_id=...) Unfollow a user
client.interaction.get_follow_requests(auth_token=..., proxy=..., cursor=..., count=...) List pending follow requester IDs
client.interaction.accept_follow_request(auth_token=..., user_id=..., proxy=...) Accept one pending follow request
client.interaction.deny_follow_request(auth_token=..., user_id=..., proxy=...) Deny one pending follow request
client.interaction.add_member_to_list(auth_token=..., list_id=..., user_id=...) Add to list
client.interaction.remove_member_from_list(auth_token=..., list_id=..., user_id=...) Remove from list
client.interaction.get_notifications(auth_token=...) Get notifications
client.interaction.get_user_analytics(auth_token=...) Get analytics

Review follow requests

The supplied auth_token determines whose pending requests you can review. Listing returns data as a list of digit strings and pagination with nextCursor and prevCursor. IDs retain their full precision; the response includes no profiles or total count. An empty list is valid, and terminal cursors are None.

requests_page = client.interaction.get_follow_requests(auth_token="AUTH_TOKEN")
print(requests_page["data"])

next_cursor = requests_page["pagination"]["nextCursor"]
if next_cursor is not None:
    next_page = client.interaction.get_follow_requests(
        auth_token="AUTH_TOKEN", cursor=next_cursor, count=100,
    )

Omit cursor and count (or pass None) to use the server defaults of "-1" and 100. When supplied, count must be an integer from 1 to 100. Each method accepts an optional proxy in host:port@user:pass format.

After reviewing the IDs, select one request and choose an action:

selected_user_id = "9007199254740993123"  # A requester ID you reviewed
decision = "accept"  # Choose "accept" or "deny"

if decision == "accept":
    result = client.interaction.accept_follow_request(
        auth_token="AUTH_TOKEN", user_id=selected_user_id,
    )
elif decision == "deny":
    result = client.interaction.deny_follow_request(
        auth_token="AUTH_TOKEN", user_id=selected_user_id,
    )

Pass user_id as a digit-only string, not a username. Successful actions return data.id, data.action ("accept_follow_request" or "deny_follow_request"), data.timestamp, data.success, and data.metadata.user_id.

These actions are not guaranteed to be idempotent and are never retried automatically. A timeout leaves the outcome uncertain: check the pending requests and account state before deciding whether to retry manually. Listing keeps the client's normal retry behavior and works with paginate() and paginate_pages().

List

Method Description
client.list.get_details(list_id=...) Get list details
client.list.get_tweets(list_id=...) Get tweets in a list
client.list.get_members(list_id=...) Get list members
client.list.get_followers(list_id=...) Get list followers
client.list.create(auth_token=..., name=..., description=..., is_private=...) Create a list
client.list.add_member(auth_token=..., list_id=..., user_id=..., proxy=...) Add a member
client.list.remove_member(auth_token=..., list_id=..., user_id=..., proxy=...) Remove a member

Community

Method Description
client.community.get_details(community_id=...) Get community details
client.community.get_tweets(community_id=..., sort_by=...) Get community tweets
client.community.get_members(community_id=...) Get members
client.community.search(query=...) Search communities
client.community.create_post(auth_token=..., text=..., community_id=..., proxy=...) Post in community
client.community.create_post_with_media(...) Post with media
client.community.create_quote(auth_token=..., text=..., attachment_url=..., community_id=..., proxy=...) Quote post in community
client.community.create_quote_with_media(...) Quote post with media
client.community.reply_post(...) Reply to community post
client.community.reply_post_with_media(...) Reply with media
client.community.join(auth_token=..., community_id=...) Join a community
client.community.leave(auth_token=..., community_id=...) Leave a community

Space

Method Description
client.space.get_by_id(space_id=...) Get Space details
client.space.get_stream_url(media_key=...) Get HLS stream URL

Explore

Method Description
client.explore.search(query=..., type=...) Search tweets, users, photos, or videos

Auth

Method Description
client.auth.login(username=..., password=..., proxy=..., country=...) Log in and return auth tokens

country is the ISO 3166-1 alpha-2 code for the proxy's public egress IP (for example, "US"). It must match the IP used for the complete login attempt. Pass two_factor_secret when the account uses TOTP-based 2FA.

X Chat (encrypted DMs)

Method Description
client.xchat.setup(auth_token=..., user_id=..., pin=...) Initialize encrypted DMs
client.xchat.get_conversations(auth_token=...) List conversations
client.xchat.send(auth_token=..., recipient_id=..., message=...) Send message
client.xchat.get_history(auth_token=..., conversation_id=...) Get conversation history
client.xchat.can_dm(auth_token=..., user_ids=...) Check DM availability

Unencrypted DMs

Method Description
client.dm.send_dm(auth_token=..., conversation_id=..., text=..., proxy=...) Send DM
client.dm.get_dm_permissions(auth_token=..., recipient_ids=...) Check permissions
client.dm.get_inbox_initial_state(auth_token=...) Get inbox state
client.dm.get_inbox_trusted(auth_token=..., cursor=...) Get the trusted inbox
client.dm.get_inbox_untrusted(auth_token=..., cursor=...) Get message requests
client.dm.get_conversation(auth_token=..., conversation_id=...) Get messages
client.dm.get_dm_user_updates(auth_token=..., cursor=...) Get DM user updates
client.dm.accept_conversation(auth_token=..., conversation_id=...) Accept a conversation request

Posting and profile media

Tweet media accepts an existing TweetAPI media ID, a URL, or inline base64 data:

client.post.create_post_with_media(
    auth_token="AUTH_TOKEN",
    text="Launch update",
    media=[{"media_id": "1971008286821380096"}],
    proxy="host:port:user:pass",
)

Use reply_option to set reply controls when creating standard, quote, or media posts:

client.post.create_post(
    auth_token="AUTH_TOKEN",
    text="Followers can reply",
    proxy="host:port:user:pass",
    reply_option={"mode": "followers"},
)

Profile avatar and banner uploads accept a URL or inline data plus type:

client.profile.update(auth_token="AUTH_TOKEN", name="New Name", bio="Builder")
client.profile.avatar(
    auth_token="AUTH_TOKEN",
    media={"url": "https://example.com/avatar.jpg"},
)
client.profile.banner(
    auth_token="AUTH_TOKEN",
    media={"data": "BASE64_IMAGE_DATA", "type": "image/png"},
)
client.profile.remove_banner(auth_token="AUTH_TOKEN")
client.profile.set_privacy(auth_token="AUTH_TOKEN", is_private=True)
client.profile.update_username(
    auth_token="AUTH_TOKEN",
    password="ACCOUNT_PASSWORD",
    username="NEW_USERNAME",
)

Use client.list for list mutations. The older helpers under client.interaction remain available:

created = client.list.create(
    auth_token="AUTH_TOKEN",
    name="Research",
    description="Accounts to watch",
    is_private=True,
)
client.list.add_member(auth_token="AUTH_TOKEN", list_id=created["data"]["id"], user_id="123")
client.list.remove_member(auth_token="AUTH_TOKEN", list_id=created["data"]["id"], user_id="123")

Community quote posts support the same URL and media attachment shapes:

client.community.create_quote(
    auth_token="AUTH_TOKEN",
    text="Useful context",
    attachment_url="https://x.com/user/status/123",
    community_id="987",
    proxy="host:port:user:pass",
)
client.community.create_quote_with_media(
    auth_token="AUTH_TOKEN",
    text="Useful context",
    attachment_url="https://x.com/user/status/123",
    community_id="987",
    media=[{"media_id": "1971008286821380096"}],
    proxy="host:port:user:pass",
)

Pagination

paginate() yields individual items. paginate_pages() yields each full response page:

from tweetapi import TweetAPI, paginate, paginate_pages

client = TweetAPI(api_key="YOUR_API_KEY")

# Iterate individual items across all pages
for user in paginate(
    lambda cursor: client.user.get_followers(user_id="123456", cursor=cursor),
):
    print(user["username"])

# Iterate full pages (access page-level data)
for page in paginate_pages(
    lambda cursor: client.explore.search(query="bitcoin", type="Latest", cursor=cursor),
    max_pages=5,  # optional: limit number of pages
):
    print(f"Got {len(page['data'])} results")
    print(f"Next cursor: {page['pagination']['nextCursor']}")

Each helper accepts a callable that takes a cursor and returns a response with data and pagination.nextCursor. Use max_pages to limit the number of fetched pages.

Retries

The client retries reads after these transient failures. Reads are every GET request, plus tweet.translate() and the X Chat get_conversations(), get_history(), and can_dm() calls.

  • For a 429 response, it waits for RateLimitError.retry_after seconds, up to max_retry_delay. That is the API's Retry-After, or 60 seconds if it sends none.
  • For a 5xx response, it waits for Retry-After when the API sends one.
  • Otherwise, and for a timeout or connection failure, it uses exponential backoff with up to 25% jitter.

By default, the client makes up to 3 retries. Its base delay starts at 1 second, doubles after each attempt, and is capped at 30 seconds. Other 4xx responses are not retried.

Writes, such as posts, replies, likes, follows, messages, and logins, retry only after a 429 or 503 response that includes Retry-After. The API sends that header only before a request reaches X, so the retry cannot repeat an action. Any other failure of a write, including a timeout, may have happened after X accepted the action. The client raises the error instead, so you can check the result before trying again.

These errors are never retried, because repeating the request cannot succeed:

code Status Cause
PROXY_ERROR 502 Your proxy refused the connection or could not be reached
PROXY_TIMEOUT 504 Your proxy timed out
QUOTA_EXHAUSTED 429 Your plan allowance or prepaid balance is used up
SUBSCRIPTION_INACTIVE 429 Your plan expired or is inactive

Username changes and accepting or denying follow requests make one attempt regardless of max_retries.

# Customize retry behavior
client = TweetAPI(
    api_key="YOUR_API_KEY",
    max_retries=5,             # default: 3
    initial_retry_delay=2.0,   # default: 1.0 (seconds)
    backoff_multiplier=3.0,    # default: 2.0
    max_retry_delay=60.0,      # default: 30.0 (seconds)
)

# Disable retries entirely
client = TweetAPI(api_key="YOUR_API_KEY", max_retries=0)

Rate-limit state

After a 429 response, client.rate_limit_info records the retry delay and the time the response was received:

print(client.rate_limit_info)

Error handling

The client raises retryable errors after it exhausts the configured retries. It raises other errors from the first response:

from tweetapi import (
    TweetAPI,
    TweetAPIError,
    AuthenticationError,
    RateLimitError,
    NotFoundError,
    ValidationError,
    ServerError,
    NetworkError,
)

client = TweetAPI(api_key="YOUR_API_KEY")

try:
    user = client.user.get_by_username(username="elonmusk")
except RateLimitError as e:
    print(f"Rate limited. Retry in {e.retry_after}s")
except NotFoundError:
    print("User not found")
except AuthenticationError:
    print("Invalid API key")
except ValidationError as e:
    print(f"Bad request: {e.message}")
except ServerError:
    print("API is having issues, try again later")
except NetworkError:
    print("Network error — check your connection")
except TweetAPIError as e:
    print(f"Error [{e.code}]: {e.message}")

Every error includes these attributes:

  • code: API error code, such as "ACCOUNT_LOCKED" or "RATE_LIMITED", or "UNKNOWN_ERROR" when the response has no specific code
  • status_code: HTTP status code
  • message: human-readable error message
  • details: response context such as a field, reason, or retry delay

Error codes

ErrorCode provides the codes as constants. Network failures use "CONNECTION_ERROR".

from tweetapi import ErrorCode, TweetAPIError

try:
    client.post.create_post(auth_token=auth_token, text="Hello", proxy=proxy)
except TweetAPIError as e:
    if e.code == ErrorCode.ACCOUNT_LOCKED:
        print("Unlock the account on X, then try again")
code Status Meaning
RATE_LIMITED 429 Per-minute limit reached. retry_after gives the seconds until it resets. During login, X can also return it without Retry-After.
QUOTA_EXHAUSTED 429 Plan allowance or prepaid balance used up
SUBSCRIPTION_INACTIVE 429 Plan expired or inactive
ACCOUNT_SUSPENDED 403 The account behind auth_token is suspended
ACCOUNT_LOCKED 403 The account behind auth_token is locked
PROXY_ERROR 502 Your proxy refused the connection or could not be reached
PROXY_TIMEOUT 504 Your proxy timed out
INVALID_CREDENTIALS 401 Login: wrong username or password
TWO_FACTOR_REQUIRED 401 Login: the account needs two_factor_secret
INVALID_TWO_FACTOR_CODE 401 Login: the two-factor code was rejected
EMAIL_VERIFICATION_REQUIRED 401 Login: the account must verify its email first
LOGIN_RUNTIME_UNAVAILABLE 503 Login: X could not be reached; try again later

Configuration

client = TweetAPI(
    api_key="YOUR_API_KEY",           # Required
    base_url="https://...",           # Optional (default: https://api.tweetapi.com)
    timeout=30,                       # Optional; one value for connect and read
    connect_timeout=10.0,             # Optional (default: 10s)
    read_timeout=30.0,                # Optional (default: 30s)
    max_retries=3,                    # Optional (default: 3, set 0 to disable)
    backoff_multiplier=2.0,           # Optional (default: 2.0)
    initial_retry_delay=1.0,          # Optional (default: 1.0s)
    max_retry_delay=30.0,             # Optional (default: 30.0s)
)

# You can also pass a tuple for timeout
client = TweetAPI(api_key="YOUR_API_KEY", timeout=(5, 30))  # (connect, read)

Requirements

  • Python 3.9+
  • requests library

License

MIT

TweetAPI is a third-party service and is not affiliated with X Corp.

Metadata

Release files for tweetapi 2.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tweetapi 2.5.0
File Size Uploaded
tweetapi-2.5.0.tar.gz 19.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tweetapi 2.5.0
File Interpreter ABI Platform
tweetapi-2.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 46.9 kB

Release files / tweetapi-2.5.0.tar.gz

Download URL tweetapi-2.5.0.tar.gz
Size 19.9 kB
Tags Source
SHA-256 checksum
How to use checksums
6321acfac663bcce248ae8c12cddc7bd60ff9bd9efeeb23f72ba4de6ce82c8b2
BLAKE2b-256 checksum
How to use checksums
144f772746bb3d71feda1389c358689268f7f4ad8ece6d555d68f34f4297c6a1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / tweetapi-2.5.0-py3-none-any.whl

Download URL tweetapi-2.5.0-py3-none-any.whl
Size 27.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
55acae6539b88610de30b7d5d37669025aab6893221915151bf564d22b9128f8
BLAKE2b-256 checksum
How to use checksums
3b7bc03e3fda7797fc0e5840cec71bec8b89b34c36f41e615ffc62a3bd15b1fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release history Release notifications | RSS feed

This release

2.5.0 This release

2 release files

2.4.0

2 release files

2.3.0

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page