tweetapi 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tweetapi-1.1.0/.claude/settings.local.json +3 -0
- tweetapi-1.1.0/.gitignore +12 -0
- tweetapi-1.1.0/PKG-INFO +345 -0
- tweetapi-1.1.0/README.md +315 -0
- tweetapi-1.1.0/examples/create_post.py +37 -0
- tweetapi-1.1.0/examples/get_user.py +31 -0
- tweetapi-1.1.0/examples/search_tweets.py +20 -0
- tweetapi-1.1.0/pyproject.toml +63 -0
- tweetapi-1.1.0/tests/__init__.py +0 -0
- tweetapi-1.1.0/tests/test_client.py +247 -0
- tweetapi-1.1.0/tests/test_pagination.py +101 -0
- tweetapi-1.1.0/tests/test_retry.py +148 -0
- tweetapi-1.1.0/tests/test_timeout.py +56 -0
- tweetapi-1.1.0/tweetapi/__init__.py +40 -0
- tweetapi-1.1.0/tweetapi/client.py +232 -0
- tweetapi-1.1.0/tweetapi/errors.py +122 -0
- tweetapi-1.1.0/tweetapi/pagination.py +64 -0
- tweetapi-1.1.0/tweetapi/py.typed +0 -0
- tweetapi-1.1.0/tweetapi/resources/__init__.py +0 -0
- tweetapi-1.1.0/tweetapi/resources/auth.py +19 -0
- tweetapi-1.1.0/tweetapi/resources/community.py +75 -0
- tweetapi-1.1.0/tweetapi/resources/explore.py +22 -0
- tweetapi-1.1.0/tweetapi/resources/interaction.py +91 -0
- tweetapi-1.1.0/tweetapi/resources/list_.py +32 -0
- tweetapi-1.1.0/tweetapi/resources/post.py +53 -0
- tweetapi-1.1.0/tweetapi/resources/space.py +20 -0
- tweetapi-1.1.0/tweetapi/resources/tweet.py +37 -0
- tweetapi-1.1.0/tweetapi/resources/unencrypted_dm.py +64 -0
- tweetapi-1.1.0/tweetapi/resources/user.py +84 -0
- tweetapi-1.1.0/tweetapi/resources/xchat.py +45 -0
- tweetapi-1.1.0/tweetapi/types.py +563 -0
tweetapi-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tweetapi
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Official Python SDK for TweetAPI — Twitter/X Data API for developers and researchers
|
|
5
|
+
Project-URL: Homepage, https://tweetapi.com?utm_source=pypi&utm_medium=readme&utm_campaign=python-sdk
|
|
6
|
+
Project-URL: Documentation, https://tweetapi.com/docs?utm_source=pypi&utm_medium=readme&utm_campaign=python-sdk
|
|
7
|
+
Project-URL: Repository, https://github.com/tweetapi/python
|
|
8
|
+
Project-URL: Issues, https://github.com/tweetapi/python/issues
|
|
9
|
+
Author-email: TweetAPI <support@tweetapi.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Keywords: social media api,tweet api,tweetapi,tweets,twitter,twitter api,twitter client,twitter data,twitter library,twitter sdk,twitter wrapper,x api
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: requests>=2.28.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: responses>=0.23.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# TweetAPI Python SDK
|
|
32
|
+
|
|
33
|
+
Official Python SDK for [TweetAPI](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk) — the Twitter/X Data API for developers and researchers.
|
|
34
|
+
|
|
35
|
+
Access tweets, user profiles, followers, analytics, and full interaction capabilities. 70+ endpoints with built-in error handling and type hints.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install tweetapi
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from tweetapi import TweetAPI
|
|
47
|
+
|
|
48
|
+
client = TweetAPI(api_key="YOUR_API_KEY")
|
|
49
|
+
|
|
50
|
+
# Get a user profile
|
|
51
|
+
user = client.user.get_by_username(username="elonmusk")
|
|
52
|
+
print(user["data"]["followerCount"]) # 180000000
|
|
53
|
+
|
|
54
|
+
# Search tweets
|
|
55
|
+
results = client.explore.search(query="bitcoin", type="Latest")
|
|
56
|
+
|
|
57
|
+
# Get followers with pagination
|
|
58
|
+
followers = client.user.get_followers(user_id="123456")
|
|
59
|
+
next_page = client.user.get_followers(
|
|
60
|
+
user_id="123456",
|
|
61
|
+
cursor=followers["pagination"]["nextCursor"],
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
> **Get your free API key** — [100 requests, no credit card required](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
66
|
+
|
|
67
|
+
## Features
|
|
68
|
+
|
|
69
|
+
- **70+ endpoints** covering users, tweets, posts, interactions, DMs, communities, spaces, and search
|
|
70
|
+
- **Full type hints** with TypedDict response types for IDE autocomplete
|
|
71
|
+
- **Automatic retry with backoff** on rate limits (429) and server errors (5xx)
|
|
72
|
+
- **Auto-pagination helpers** — iterate all pages with a simple `for` loop
|
|
73
|
+
- **Solid error handling** with typed exceptions (`RateLimitError`, `NotFoundError`, etc.)
|
|
74
|
+
- **Rate limit awareness** — `retry_after` respected automatically, state exposed via `client.rate_limit_info`
|
|
75
|
+
- **Split timeouts** — separate connect and read timeouts
|
|
76
|
+
- **Single dependency** — `requests` only
|
|
77
|
+
- **Python 3.9+** compatible
|
|
78
|
+
|
|
79
|
+
## API Reference
|
|
80
|
+
|
|
81
|
+
### User
|
|
82
|
+
|
|
83
|
+
| Method | Description |
|
|
84
|
+
|--------|-------------|
|
|
85
|
+
| `client.user.get_by_username(username=...)` | Get user profile by username |
|
|
86
|
+
| `client.user.get_by_usernames(usernames=...)` | Get multiple users (comma-separated) |
|
|
87
|
+
| `client.user.get_by_user_id(user_id=...)` | Get user profile by ID |
|
|
88
|
+
| `client.user.get_by_user_ids(user_ids=...)` | Get multiple users by IDs |
|
|
89
|
+
| `client.user.get_tweets(user_id=...)` | Get a user's tweets |
|
|
90
|
+
| `client.user.get_tweets_and_replies(user_id=...)` | Get tweets and replies |
|
|
91
|
+
| `client.user.get_following(user_id=...)` | Get who a user follows |
|
|
92
|
+
| `client.user.get_followers(user_id=...)` | Get a user's followers |
|
|
93
|
+
| `client.user.get_verified_followers(user_id=...)` | Get verified followers |
|
|
94
|
+
| `client.user.get_subscriptions(user_id=...)` | Get subscriptions |
|
|
95
|
+
| `client.user.get_following_v1(user_id=...)` | Get following (v1, supports count) |
|
|
96
|
+
| `client.user.get_followers_v1(user_id=...)` | Get followers (v1, supports count) |
|
|
97
|
+
| `client.user.get_following_ids(user_id=...)` | Get following user IDs |
|
|
98
|
+
| `client.user.get_followers_ids(user_id=...)` | Get follower user IDs |
|
|
99
|
+
| `client.user.check_follow(subject_id=..., target_id=...)` | Check follow relationship |
|
|
100
|
+
| `client.user.about_account(username=...)` | Get account transparency info |
|
|
101
|
+
|
|
102
|
+
### Tweet
|
|
103
|
+
|
|
104
|
+
| Method | Description |
|
|
105
|
+
|--------|-------------|
|
|
106
|
+
| `client.tweet.get_details_and_conversation(tweet_id=...)` | Get tweet details and replies |
|
|
107
|
+
| `client.tweet.get_details_by_ids(ids=...)` | Get multiple tweets (max 200) |
|
|
108
|
+
| `client.tweet.get_retweets(tweet_id=...)` | Get who retweeted |
|
|
109
|
+
| `client.tweet.get_quotes(tweet_id=...)` | Get quote tweets |
|
|
110
|
+
| `client.tweet.translate(tweet_id=..., dst_lang=...)` | Translate a tweet |
|
|
111
|
+
|
|
112
|
+
### Post
|
|
113
|
+
|
|
114
|
+
| Method | Description |
|
|
115
|
+
|--------|-------------|
|
|
116
|
+
| `client.post.create_post(auth_token=..., text=..., proxy=...)` | Create a tweet |
|
|
117
|
+
| `client.post.create_post_quote(auth_token=..., text=..., attachment_url=..., proxy=...)` | Quote tweet |
|
|
118
|
+
| `client.post.create_post_with_media(auth_token=..., text=..., media=..., proxy=...)` | Tweet with media |
|
|
119
|
+
| `client.post.reply_post(auth_token=..., text=..., tweet_id=..., proxy=...)` | Reply to a tweet |
|
|
120
|
+
| `client.post.reply_post_with_media(...)` | Reply with media |
|
|
121
|
+
| `client.post.delete_post(auth_token=..., tweet_id=...)` | Delete a tweet |
|
|
122
|
+
|
|
123
|
+
### Interaction
|
|
124
|
+
|
|
125
|
+
| Method | Description |
|
|
126
|
+
|--------|-------------|
|
|
127
|
+
| `client.interaction.favorite_post(auth_token=..., tweet_id=...)` | Like a tweet |
|
|
128
|
+
| `client.interaction.unfavorite_post(auth_token=..., tweet_id=...)` | Unlike a tweet |
|
|
129
|
+
| `client.interaction.retweet(auth_token=..., tweet_id=...)` | Retweet |
|
|
130
|
+
| `client.interaction.delete_retweet(auth_token=..., tweet_id=...)` | Remove retweet |
|
|
131
|
+
| `client.interaction.bookmark(auth_token=..., tweet_id=...)` | Bookmark a tweet |
|
|
132
|
+
| `client.interaction.delete_bookmark(auth_token=..., tweet_id=...)` | Remove bookmark |
|
|
133
|
+
| `client.interaction.follow(auth_token=..., user_id=...)` | Follow a user |
|
|
134
|
+
| `client.interaction.unfollow(auth_token=..., user_id=...)` | Unfollow a user |
|
|
135
|
+
| `client.interaction.add_member_to_list(auth_token=..., list_id=..., user_id=...)` | Add to list |
|
|
136
|
+
| `client.interaction.remove_member_from_list(auth_token=..., list_id=..., user_id=...)` | Remove from list |
|
|
137
|
+
| `client.interaction.get_notifications(auth_token=...)` | Get notifications |
|
|
138
|
+
| `client.interaction.get_user_analytics(auth_token=...)` | Get analytics |
|
|
139
|
+
|
|
140
|
+
### List
|
|
141
|
+
|
|
142
|
+
| Method | Description |
|
|
143
|
+
|--------|-------------|
|
|
144
|
+
| `client.list.get_details(list_id=...)` | Get list details |
|
|
145
|
+
| `client.list.get_tweets(list_id=...)` | Get tweets in a list |
|
|
146
|
+
| `client.list.get_members(list_id=...)` | Get list members |
|
|
147
|
+
| `client.list.get_followers(list_id=...)` | Get list followers |
|
|
148
|
+
|
|
149
|
+
### Community
|
|
150
|
+
|
|
151
|
+
| Method | Description |
|
|
152
|
+
|--------|-------------|
|
|
153
|
+
| `client.community.get_details(community_id=...)` | Get community details |
|
|
154
|
+
| `client.community.get_tweets(community_id=..., sort_by=...)` | Get community tweets |
|
|
155
|
+
| `client.community.get_members(community_id=...)` | Get members |
|
|
156
|
+
| `client.community.search(query=...)` | Search communities |
|
|
157
|
+
| `client.community.create_post(auth_token=..., text=..., community_id=..., proxy=...)` | Post in community |
|
|
158
|
+
| `client.community.create_post_with_media(...)` | Post with media |
|
|
159
|
+
| `client.community.reply_post(...)` | Reply to community post |
|
|
160
|
+
| `client.community.reply_post_with_media(...)` | Reply with media |
|
|
161
|
+
| `client.community.join(auth_token=..., community_id=...)` | Join |
|
|
162
|
+
| `client.community.leave(auth_token=..., community_id=...)` | Leave |
|
|
163
|
+
|
|
164
|
+
### Space
|
|
165
|
+
|
|
166
|
+
| Method | Description |
|
|
167
|
+
|--------|-------------|
|
|
168
|
+
| `client.space.get_by_id(space_id=...)` | Get Space details |
|
|
169
|
+
| `client.space.get_stream_url(media_key=...)` | Get HLS stream URL |
|
|
170
|
+
|
|
171
|
+
### Explore
|
|
172
|
+
|
|
173
|
+
| Method | Description |
|
|
174
|
+
|--------|-------------|
|
|
175
|
+
| `client.explore.search(query=..., type=...)` | Search tweets/users/photos/videos |
|
|
176
|
+
|
|
177
|
+
### Auth
|
|
178
|
+
|
|
179
|
+
| Method | Description |
|
|
180
|
+
|--------|-------------|
|
|
181
|
+
| `client.auth.login(username=..., password=..., proxy=...)` | Log in, get auth tokens |
|
|
182
|
+
|
|
183
|
+
### X Chat (Encrypted DMs)
|
|
184
|
+
|
|
185
|
+
| Method | Description |
|
|
186
|
+
|--------|-------------|
|
|
187
|
+
| `client.xchat.setup(auth_token=..., user_id=..., pin=...)` | Initialize encrypted DMs |
|
|
188
|
+
| `client.xchat.get_conversations(auth_token=...)` | List conversations |
|
|
189
|
+
| `client.xchat.send(auth_token=..., recipient_id=..., message=...)` | Send message |
|
|
190
|
+
| `client.xchat.get_history(auth_token=..., conversation_id=...)` | Get history |
|
|
191
|
+
| `client.xchat.can_dm(auth_token=..., user_ids=...)` | Check DM availability |
|
|
192
|
+
|
|
193
|
+
### Unencrypted DMs
|
|
194
|
+
|
|
195
|
+
| Method | Description |
|
|
196
|
+
|--------|-------------|
|
|
197
|
+
| `client.dm.send_dm(auth_token=..., conversation_id=..., text=..., proxy=...)` | Send DM |
|
|
198
|
+
| `client.dm.get_dm_permissions(auth_token=..., recipient_ids=...)` | Check permissions |
|
|
199
|
+
| `client.dm.get_inbox_initial_state(auth_token=...)` | Get inbox state |
|
|
200
|
+
| `client.dm.get_inbox_trusted(auth_token=..., cursor=...)` | Trusted inbox |
|
|
201
|
+
| `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` | Message requests |
|
|
202
|
+
| `client.dm.get_conversation(auth_token=..., conversation_id=...)` | Get messages |
|
|
203
|
+
| `client.dm.get_dm_user_updates(auth_token=..., cursor=...)` | DM user updates |
|
|
204
|
+
| `client.dm.accept_conversation(auth_token=..., conversation_id=...)` | Accept request |
|
|
205
|
+
|
|
206
|
+
## Auto-Pagination
|
|
207
|
+
|
|
208
|
+
Use the `paginate()` and `paginate_pages()` helpers to iterate through all pages automatically:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from tweetapi import TweetAPI, paginate, paginate_pages
|
|
212
|
+
|
|
213
|
+
client = TweetAPI(api_key="YOUR_API_KEY")
|
|
214
|
+
|
|
215
|
+
# Iterate individual items across all pages
|
|
216
|
+
for user in paginate(
|
|
217
|
+
lambda cursor: client.user.get_followers(user_id="123456", cursor=cursor),
|
|
218
|
+
):
|
|
219
|
+
print(user["username"])
|
|
220
|
+
|
|
221
|
+
# Iterate full pages (access page-level data)
|
|
222
|
+
for page in paginate_pages(
|
|
223
|
+
lambda cursor: client.explore.search(query="bitcoin", type="Latest", cursor=cursor),
|
|
224
|
+
max_pages=5, # optional: limit number of pages
|
|
225
|
+
):
|
|
226
|
+
print(f"Got {len(page['data'])} results")
|
|
227
|
+
print(f"Next cursor: {page['pagination']['nextCursor']}")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Works with any paginated endpoint — followers, tweets, search results, list members, community posts, etc.
|
|
231
|
+
|
|
232
|
+
## Automatic Retry with Backoff
|
|
233
|
+
|
|
234
|
+
The SDK automatically retries on transient errors with exponential backoff:
|
|
235
|
+
|
|
236
|
+
- **429 (Rate Limit)** — waits the `retry_after` duration from the API, then retries
|
|
237
|
+
- **5xx (Server Error)** — retries with exponential backoff + jitter
|
|
238
|
+
- **Network errors** — retries on timeouts and connection failures
|
|
239
|
+
- **4xx (Client Error)** — never retried (400, 401, 403, 404 fail immediately)
|
|
240
|
+
|
|
241
|
+
Default: 3 retries, 2x backoff, 1s initial delay, 30s max delay.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
# Customize retry behavior
|
|
245
|
+
client = TweetAPI(
|
|
246
|
+
api_key="YOUR_API_KEY",
|
|
247
|
+
max_retries=5, # default: 3
|
|
248
|
+
initial_retry_delay=2.0, # default: 1.0 (seconds)
|
|
249
|
+
backoff_multiplier=3.0, # default: 2.0
|
|
250
|
+
max_retry_delay=60.0, # default: 30.0 (seconds)
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Disable retries entirely
|
|
254
|
+
client = TweetAPI(api_key="YOUR_API_KEY", max_retries=0)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Rate Limit Awareness
|
|
258
|
+
|
|
259
|
+
After a 429 response, the SDK exposes the last known rate limit state:
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
print(client.rate_limit_info)
|
|
263
|
+
# {"retry_after": 30, "timestamp": 1712345678.0} — or None if no 429 encountered
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Error Handling
|
|
267
|
+
|
|
268
|
+
The SDK raises typed exceptions you can catch and handle. With automatic retries enabled (default), you'll only see these after all retry attempts are exhausted:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from tweetapi import (
|
|
272
|
+
TweetAPI,
|
|
273
|
+
TweetAPIError,
|
|
274
|
+
AuthenticationError,
|
|
275
|
+
RateLimitError,
|
|
276
|
+
NotFoundError,
|
|
277
|
+
ValidationError,
|
|
278
|
+
ServerError,
|
|
279
|
+
NetworkError,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
client = TweetAPI(api_key="YOUR_API_KEY")
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
user = client.user.get_by_username(username="elonmusk")
|
|
286
|
+
except RateLimitError as e:
|
|
287
|
+
print(f"Rate limited. Retry in {e.retry_after}s")
|
|
288
|
+
except NotFoundError:
|
|
289
|
+
print("User not found")
|
|
290
|
+
except AuthenticationError:
|
|
291
|
+
print("Invalid API key")
|
|
292
|
+
except ValidationError as e:
|
|
293
|
+
print(f"Bad request: {e.message}")
|
|
294
|
+
except ServerError:
|
|
295
|
+
print("API is having issues, try again later")
|
|
296
|
+
except NetworkError:
|
|
297
|
+
print("Network error — check your connection")
|
|
298
|
+
except TweetAPIError as e:
|
|
299
|
+
print(f"Error [{e.code}]: {e.message}")
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Every error includes:
|
|
303
|
+
- `code` — API error code (e.g., `"ACCOUNT_SUSPENDED"`, `"RATE_LIMIT"`)
|
|
304
|
+
- `status_code` — HTTP status code
|
|
305
|
+
- `message` — Human-readable error message
|
|
306
|
+
- `details` — Additional context (field, reason, retry_after, etc.)
|
|
307
|
+
|
|
308
|
+
## Configuration
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
client = TweetAPI(
|
|
312
|
+
api_key="YOUR_API_KEY", # Required
|
|
313
|
+
base_url="https://...", # Optional (default: https://api.tweetapi.com)
|
|
314
|
+
timeout=30, # Optional — single value for both connect + read
|
|
315
|
+
connect_timeout=10.0, # Optional (default: 10s)
|
|
316
|
+
read_timeout=30.0, # Optional (default: 30s)
|
|
317
|
+
max_retries=3, # Optional (default: 3, set 0 to disable)
|
|
318
|
+
backoff_multiplier=2.0, # Optional (default: 2.0)
|
|
319
|
+
initial_retry_delay=1.0, # Optional (default: 1.0s)
|
|
320
|
+
max_retry_delay=30.0, # Optional (default: 30.0s)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# You can also pass a tuple for timeout
|
|
324
|
+
client = TweetAPI(api_key="YOUR_API_KEY", timeout=(5, 30)) # (connect, read)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Requirements
|
|
328
|
+
|
|
329
|
+
- Python 3.9+
|
|
330
|
+
- `requests` library
|
|
331
|
+
|
|
332
|
+
## Links
|
|
333
|
+
|
|
334
|
+
- [Full Documentation](https://tweetapi.com/docs?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
335
|
+
- [Get API Key (Free)](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
336
|
+
- [Dashboard](https://tweetapi.com/dashboard?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
337
|
+
- [Node.js SDK](https://github.com/tweetapi/node)
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
MIT
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
*TweetAPI is a third-party service and is not affiliated with X Corp.*
|
tweetapi-1.1.0/README.md
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# TweetAPI Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for [TweetAPI](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk) — the Twitter/X Data API for developers and researchers.
|
|
4
|
+
|
|
5
|
+
Access tweets, user profiles, followers, analytics, and full interaction capabilities. 70+ endpoints with built-in error handling and type hints.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install tweetapi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from tweetapi import TweetAPI
|
|
17
|
+
|
|
18
|
+
client = TweetAPI(api_key="YOUR_API_KEY")
|
|
19
|
+
|
|
20
|
+
# Get a user profile
|
|
21
|
+
user = client.user.get_by_username(username="elonmusk")
|
|
22
|
+
print(user["data"]["followerCount"]) # 180000000
|
|
23
|
+
|
|
24
|
+
# Search tweets
|
|
25
|
+
results = client.explore.search(query="bitcoin", type="Latest")
|
|
26
|
+
|
|
27
|
+
# Get followers with pagination
|
|
28
|
+
followers = client.user.get_followers(user_id="123456")
|
|
29
|
+
next_page = client.user.get_followers(
|
|
30
|
+
user_id="123456",
|
|
31
|
+
cursor=followers["pagination"]["nextCursor"],
|
|
32
|
+
)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> **Get your free API key** — [100 requests, no credit card required](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **70+ endpoints** covering users, tweets, posts, interactions, DMs, communities, spaces, and search
|
|
40
|
+
- **Full type hints** with TypedDict response types for IDE autocomplete
|
|
41
|
+
- **Automatic retry with backoff** on rate limits (429) and server errors (5xx)
|
|
42
|
+
- **Auto-pagination helpers** — iterate all pages with a simple `for` loop
|
|
43
|
+
- **Solid error handling** with typed exceptions (`RateLimitError`, `NotFoundError`, etc.)
|
|
44
|
+
- **Rate limit awareness** — `retry_after` respected automatically, state exposed via `client.rate_limit_info`
|
|
45
|
+
- **Split timeouts** — separate connect and read timeouts
|
|
46
|
+
- **Single dependency** — `requests` only
|
|
47
|
+
- **Python 3.9+** compatible
|
|
48
|
+
|
|
49
|
+
## API Reference
|
|
50
|
+
|
|
51
|
+
### User
|
|
52
|
+
|
|
53
|
+
| Method | Description |
|
|
54
|
+
|--------|-------------|
|
|
55
|
+
| `client.user.get_by_username(username=...)` | Get user profile by username |
|
|
56
|
+
| `client.user.get_by_usernames(usernames=...)` | Get multiple users (comma-separated) |
|
|
57
|
+
| `client.user.get_by_user_id(user_id=...)` | Get user profile by ID |
|
|
58
|
+
| `client.user.get_by_user_ids(user_ids=...)` | Get multiple users by IDs |
|
|
59
|
+
| `client.user.get_tweets(user_id=...)` | Get a user's tweets |
|
|
60
|
+
| `client.user.get_tweets_and_replies(user_id=...)` | Get tweets and replies |
|
|
61
|
+
| `client.user.get_following(user_id=...)` | Get who a user follows |
|
|
62
|
+
| `client.user.get_followers(user_id=...)` | Get a user's followers |
|
|
63
|
+
| `client.user.get_verified_followers(user_id=...)` | Get verified followers |
|
|
64
|
+
| `client.user.get_subscriptions(user_id=...)` | Get subscriptions |
|
|
65
|
+
| `client.user.get_following_v1(user_id=...)` | Get following (v1, supports count) |
|
|
66
|
+
| `client.user.get_followers_v1(user_id=...)` | Get followers (v1, supports count) |
|
|
67
|
+
| `client.user.get_following_ids(user_id=...)` | Get following user IDs |
|
|
68
|
+
| `client.user.get_followers_ids(user_id=...)` | Get follower user IDs |
|
|
69
|
+
| `client.user.check_follow(subject_id=..., target_id=...)` | Check follow relationship |
|
|
70
|
+
| `client.user.about_account(username=...)` | Get account transparency info |
|
|
71
|
+
|
|
72
|
+
### Tweet
|
|
73
|
+
|
|
74
|
+
| Method | Description |
|
|
75
|
+
|--------|-------------|
|
|
76
|
+
| `client.tweet.get_details_and_conversation(tweet_id=...)` | Get tweet details and replies |
|
|
77
|
+
| `client.tweet.get_details_by_ids(ids=...)` | Get multiple tweets (max 200) |
|
|
78
|
+
| `client.tweet.get_retweets(tweet_id=...)` | Get who retweeted |
|
|
79
|
+
| `client.tweet.get_quotes(tweet_id=...)` | Get quote tweets |
|
|
80
|
+
| `client.tweet.translate(tweet_id=..., dst_lang=...)` | Translate a tweet |
|
|
81
|
+
|
|
82
|
+
### Post
|
|
83
|
+
|
|
84
|
+
| Method | Description |
|
|
85
|
+
|--------|-------------|
|
|
86
|
+
| `client.post.create_post(auth_token=..., text=..., proxy=...)` | Create a tweet |
|
|
87
|
+
| `client.post.create_post_quote(auth_token=..., text=..., attachment_url=..., proxy=...)` | Quote tweet |
|
|
88
|
+
| `client.post.create_post_with_media(auth_token=..., text=..., media=..., proxy=...)` | Tweet with media |
|
|
89
|
+
| `client.post.reply_post(auth_token=..., text=..., tweet_id=..., proxy=...)` | Reply to a tweet |
|
|
90
|
+
| `client.post.reply_post_with_media(...)` | Reply with media |
|
|
91
|
+
| `client.post.delete_post(auth_token=..., tweet_id=...)` | Delete a tweet |
|
|
92
|
+
|
|
93
|
+
### Interaction
|
|
94
|
+
|
|
95
|
+
| Method | Description |
|
|
96
|
+
|--------|-------------|
|
|
97
|
+
| `client.interaction.favorite_post(auth_token=..., tweet_id=...)` | Like a tweet |
|
|
98
|
+
| `client.interaction.unfavorite_post(auth_token=..., tweet_id=...)` | Unlike a tweet |
|
|
99
|
+
| `client.interaction.retweet(auth_token=..., tweet_id=...)` | Retweet |
|
|
100
|
+
| `client.interaction.delete_retweet(auth_token=..., tweet_id=...)` | Remove retweet |
|
|
101
|
+
| `client.interaction.bookmark(auth_token=..., tweet_id=...)` | Bookmark a tweet |
|
|
102
|
+
| `client.interaction.delete_bookmark(auth_token=..., tweet_id=...)` | Remove bookmark |
|
|
103
|
+
| `client.interaction.follow(auth_token=..., user_id=...)` | Follow a user |
|
|
104
|
+
| `client.interaction.unfollow(auth_token=..., user_id=...)` | Unfollow a user |
|
|
105
|
+
| `client.interaction.add_member_to_list(auth_token=..., list_id=..., user_id=...)` | Add to list |
|
|
106
|
+
| `client.interaction.remove_member_from_list(auth_token=..., list_id=..., user_id=...)` | Remove from list |
|
|
107
|
+
| `client.interaction.get_notifications(auth_token=...)` | Get notifications |
|
|
108
|
+
| `client.interaction.get_user_analytics(auth_token=...)` | Get analytics |
|
|
109
|
+
|
|
110
|
+
### List
|
|
111
|
+
|
|
112
|
+
| Method | Description |
|
|
113
|
+
|--------|-------------|
|
|
114
|
+
| `client.list.get_details(list_id=...)` | Get list details |
|
|
115
|
+
| `client.list.get_tweets(list_id=...)` | Get tweets in a list |
|
|
116
|
+
| `client.list.get_members(list_id=...)` | Get list members |
|
|
117
|
+
| `client.list.get_followers(list_id=...)` | Get list followers |
|
|
118
|
+
|
|
119
|
+
### Community
|
|
120
|
+
|
|
121
|
+
| Method | Description |
|
|
122
|
+
|--------|-------------|
|
|
123
|
+
| `client.community.get_details(community_id=...)` | Get community details |
|
|
124
|
+
| `client.community.get_tweets(community_id=..., sort_by=...)` | Get community tweets |
|
|
125
|
+
| `client.community.get_members(community_id=...)` | Get members |
|
|
126
|
+
| `client.community.search(query=...)` | Search communities |
|
|
127
|
+
| `client.community.create_post(auth_token=..., text=..., community_id=..., proxy=...)` | Post in community |
|
|
128
|
+
| `client.community.create_post_with_media(...)` | Post with media |
|
|
129
|
+
| `client.community.reply_post(...)` | Reply to community post |
|
|
130
|
+
| `client.community.reply_post_with_media(...)` | Reply with media |
|
|
131
|
+
| `client.community.join(auth_token=..., community_id=...)` | Join |
|
|
132
|
+
| `client.community.leave(auth_token=..., community_id=...)` | Leave |
|
|
133
|
+
|
|
134
|
+
### Space
|
|
135
|
+
|
|
136
|
+
| Method | Description |
|
|
137
|
+
|--------|-------------|
|
|
138
|
+
| `client.space.get_by_id(space_id=...)` | Get Space details |
|
|
139
|
+
| `client.space.get_stream_url(media_key=...)` | Get HLS stream URL |
|
|
140
|
+
|
|
141
|
+
### Explore
|
|
142
|
+
|
|
143
|
+
| Method | Description |
|
|
144
|
+
|--------|-------------|
|
|
145
|
+
| `client.explore.search(query=..., type=...)` | Search tweets/users/photos/videos |
|
|
146
|
+
|
|
147
|
+
### Auth
|
|
148
|
+
|
|
149
|
+
| Method | Description |
|
|
150
|
+
|--------|-------------|
|
|
151
|
+
| `client.auth.login(username=..., password=..., proxy=...)` | Log in, get auth tokens |
|
|
152
|
+
|
|
153
|
+
### X Chat (Encrypted DMs)
|
|
154
|
+
|
|
155
|
+
| Method | Description |
|
|
156
|
+
|--------|-------------|
|
|
157
|
+
| `client.xchat.setup(auth_token=..., user_id=..., pin=...)` | Initialize encrypted DMs |
|
|
158
|
+
| `client.xchat.get_conversations(auth_token=...)` | List conversations |
|
|
159
|
+
| `client.xchat.send(auth_token=..., recipient_id=..., message=...)` | Send message |
|
|
160
|
+
| `client.xchat.get_history(auth_token=..., conversation_id=...)` | Get history |
|
|
161
|
+
| `client.xchat.can_dm(auth_token=..., user_ids=...)` | Check DM availability |
|
|
162
|
+
|
|
163
|
+
### Unencrypted DMs
|
|
164
|
+
|
|
165
|
+
| Method | Description |
|
|
166
|
+
|--------|-------------|
|
|
167
|
+
| `client.dm.send_dm(auth_token=..., conversation_id=..., text=..., proxy=...)` | Send DM |
|
|
168
|
+
| `client.dm.get_dm_permissions(auth_token=..., recipient_ids=...)` | Check permissions |
|
|
169
|
+
| `client.dm.get_inbox_initial_state(auth_token=...)` | Get inbox state |
|
|
170
|
+
| `client.dm.get_inbox_trusted(auth_token=..., cursor=...)` | Trusted inbox |
|
|
171
|
+
| `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` | Message requests |
|
|
172
|
+
| `client.dm.get_conversation(auth_token=..., conversation_id=...)` | Get messages |
|
|
173
|
+
| `client.dm.get_dm_user_updates(auth_token=..., cursor=...)` | DM user updates |
|
|
174
|
+
| `client.dm.accept_conversation(auth_token=..., conversation_id=...)` | Accept request |
|
|
175
|
+
|
|
176
|
+
## Auto-Pagination
|
|
177
|
+
|
|
178
|
+
Use the `paginate()` and `paginate_pages()` helpers to iterate through all pages automatically:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from tweetapi import TweetAPI, paginate, paginate_pages
|
|
182
|
+
|
|
183
|
+
client = TweetAPI(api_key="YOUR_API_KEY")
|
|
184
|
+
|
|
185
|
+
# Iterate individual items across all pages
|
|
186
|
+
for user in paginate(
|
|
187
|
+
lambda cursor: client.user.get_followers(user_id="123456", cursor=cursor),
|
|
188
|
+
):
|
|
189
|
+
print(user["username"])
|
|
190
|
+
|
|
191
|
+
# Iterate full pages (access page-level data)
|
|
192
|
+
for page in paginate_pages(
|
|
193
|
+
lambda cursor: client.explore.search(query="bitcoin", type="Latest", cursor=cursor),
|
|
194
|
+
max_pages=5, # optional: limit number of pages
|
|
195
|
+
):
|
|
196
|
+
print(f"Got {len(page['data'])} results")
|
|
197
|
+
print(f"Next cursor: {page['pagination']['nextCursor']}")
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Works with any paginated endpoint — followers, tweets, search results, list members, community posts, etc.
|
|
201
|
+
|
|
202
|
+
## Automatic Retry with Backoff
|
|
203
|
+
|
|
204
|
+
The SDK automatically retries on transient errors with exponential backoff:
|
|
205
|
+
|
|
206
|
+
- **429 (Rate Limit)** — waits the `retry_after` duration from the API, then retries
|
|
207
|
+
- **5xx (Server Error)** — retries with exponential backoff + jitter
|
|
208
|
+
- **Network errors** — retries on timeouts and connection failures
|
|
209
|
+
- **4xx (Client Error)** — never retried (400, 401, 403, 404 fail immediately)
|
|
210
|
+
|
|
211
|
+
Default: 3 retries, 2x backoff, 1s initial delay, 30s max delay.
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
# Customize retry behavior
|
|
215
|
+
client = TweetAPI(
|
|
216
|
+
api_key="YOUR_API_KEY",
|
|
217
|
+
max_retries=5, # default: 3
|
|
218
|
+
initial_retry_delay=2.0, # default: 1.0 (seconds)
|
|
219
|
+
backoff_multiplier=3.0, # default: 2.0
|
|
220
|
+
max_retry_delay=60.0, # default: 30.0 (seconds)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Disable retries entirely
|
|
224
|
+
client = TweetAPI(api_key="YOUR_API_KEY", max_retries=0)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Rate Limit Awareness
|
|
228
|
+
|
|
229
|
+
After a 429 response, the SDK exposes the last known rate limit state:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
print(client.rate_limit_info)
|
|
233
|
+
# {"retry_after": 30, "timestamp": 1712345678.0} — or None if no 429 encountered
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Error Handling
|
|
237
|
+
|
|
238
|
+
The SDK raises typed exceptions you can catch and handle. With automatic retries enabled (default), you'll only see these after all retry attempts are exhausted:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from tweetapi import (
|
|
242
|
+
TweetAPI,
|
|
243
|
+
TweetAPIError,
|
|
244
|
+
AuthenticationError,
|
|
245
|
+
RateLimitError,
|
|
246
|
+
NotFoundError,
|
|
247
|
+
ValidationError,
|
|
248
|
+
ServerError,
|
|
249
|
+
NetworkError,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
client = TweetAPI(api_key="YOUR_API_KEY")
|
|
253
|
+
|
|
254
|
+
try:
|
|
255
|
+
user = client.user.get_by_username(username="elonmusk")
|
|
256
|
+
except RateLimitError as e:
|
|
257
|
+
print(f"Rate limited. Retry in {e.retry_after}s")
|
|
258
|
+
except NotFoundError:
|
|
259
|
+
print("User not found")
|
|
260
|
+
except AuthenticationError:
|
|
261
|
+
print("Invalid API key")
|
|
262
|
+
except ValidationError as e:
|
|
263
|
+
print(f"Bad request: {e.message}")
|
|
264
|
+
except ServerError:
|
|
265
|
+
print("API is having issues, try again later")
|
|
266
|
+
except NetworkError:
|
|
267
|
+
print("Network error — check your connection")
|
|
268
|
+
except TweetAPIError as e:
|
|
269
|
+
print(f"Error [{e.code}]: {e.message}")
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Every error includes:
|
|
273
|
+
- `code` — API error code (e.g., `"ACCOUNT_SUSPENDED"`, `"RATE_LIMIT"`)
|
|
274
|
+
- `status_code` — HTTP status code
|
|
275
|
+
- `message` — Human-readable error message
|
|
276
|
+
- `details` — Additional context (field, reason, retry_after, etc.)
|
|
277
|
+
|
|
278
|
+
## Configuration
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
client = TweetAPI(
|
|
282
|
+
api_key="YOUR_API_KEY", # Required
|
|
283
|
+
base_url="https://...", # Optional (default: https://api.tweetapi.com)
|
|
284
|
+
timeout=30, # Optional — single value for both connect + read
|
|
285
|
+
connect_timeout=10.0, # Optional (default: 10s)
|
|
286
|
+
read_timeout=30.0, # Optional (default: 30s)
|
|
287
|
+
max_retries=3, # Optional (default: 3, set 0 to disable)
|
|
288
|
+
backoff_multiplier=2.0, # Optional (default: 2.0)
|
|
289
|
+
initial_retry_delay=1.0, # Optional (default: 1.0s)
|
|
290
|
+
max_retry_delay=30.0, # Optional (default: 30.0s)
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# You can also pass a tuple for timeout
|
|
294
|
+
client = TweetAPI(api_key="YOUR_API_KEY", timeout=(5, 30)) # (connect, read)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Requirements
|
|
298
|
+
|
|
299
|
+
- Python 3.9+
|
|
300
|
+
- `requests` library
|
|
301
|
+
|
|
302
|
+
## Links
|
|
303
|
+
|
|
304
|
+
- [Full Documentation](https://tweetapi.com/docs?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
305
|
+
- [Get API Key (Free)](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
306
|
+
- [Dashboard](https://tweetapi.com/dashboard?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
307
|
+
- [Node.js SDK](https://github.com/tweetapi/node)
|
|
308
|
+
|
|
309
|
+
## License
|
|
310
|
+
|
|
311
|
+
MIT
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
*TweetAPI is a third-party service and is not affiliated with X Corp.*
|