tweetapi 2.1.0__tar.gz → 2.2.1__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-2.1.0 → tweetapi-2.2.1}/PKG-INFO +54 -59
- {tweetapi-2.1.0 → tweetapi-2.2.1}/README.md +51 -56
- {tweetapi-2.1.0 → tweetapi-2.2.1}/pyproject.toml +2 -2
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/__init__.py +1 -1
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/profile.py +13 -1
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/types.py +8 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/.gitignore +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/client.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/errors.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/pagination.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/py.typed +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/__init__.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/auth.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/community.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/explore.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/interaction.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/list_.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/post.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/space.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/tweet.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/unencrypted_dm.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/user.py +0 -0
- {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/xchat.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: tweetapi
|
|
3
|
-
Version: 2.1
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.2.1
|
|
4
|
+
Summary: Python SDK for TweetAPI's Twitter/X data and account API
|
|
5
5
|
Project-URL: Homepage, https://tweetapi.com?utm_source=pypi&utm_medium=readme&utm_campaign=python-sdk
|
|
6
6
|
Project-URL: Documentation, https://tweetapi.com/docs?utm_source=pypi&utm_medium=readme&utm_campaign=python-sdk
|
|
7
7
|
Project-URL: Repository, https://github.com/tweetapi/python
|
|
@@ -30,9 +30,7 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
|
|
31
31
|
# TweetAPI Python SDK
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Access tweets, user profiles, followers, analytics, and full interaction capabilities. 70+ endpoints with built-in error handling and type hints.
|
|
33
|
+
`tweetapi` is the Python SDK for [TweetAPI](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk). It provides methods for users, tweets, posts, profiles, interactions, lists, communities, Spaces, search, authentication, and direct messages.
|
|
36
34
|
|
|
37
35
|
## Install
|
|
38
36
|
|
|
@@ -40,7 +38,7 @@ Access tweets, user profiles, followers, analytics, and full interaction capabil
|
|
|
40
38
|
pip install tweetapi
|
|
41
39
|
```
|
|
42
40
|
|
|
43
|
-
## Quick
|
|
41
|
+
## Quick start
|
|
44
42
|
|
|
45
43
|
```python
|
|
46
44
|
from tweetapi import TweetAPI
|
|
@@ -49,7 +47,7 @@ client = TweetAPI(api_key="YOUR_API_KEY")
|
|
|
49
47
|
|
|
50
48
|
# Get a user profile
|
|
51
49
|
user = client.user.get_by_username(username="elonmusk")
|
|
52
|
-
print(user["data"]["followerCount"])
|
|
50
|
+
print(user["data"]["followerCount"])
|
|
53
51
|
|
|
54
52
|
# Search tweets
|
|
55
53
|
results = client.explore.search(query="bitcoin", type="Latest")
|
|
@@ -62,21 +60,18 @@ next_page = client.user.get_followers(
|
|
|
62
60
|
)
|
|
63
61
|
```
|
|
64
62
|
|
|
65
|
-
|
|
63
|
+
[Create an API key](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk) with 100 included requests. No credit card is required.
|
|
66
64
|
|
|
67
|
-
##
|
|
65
|
+
## SDK behavior
|
|
68
66
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
- **Split timeouts** — separate connect and read timeouts
|
|
76
|
-
- **Single dependency** — `requests` only
|
|
77
|
-
- **Python 3.9+** compatible
|
|
67
|
+
- Public methods have type annotations, and response models use `TypedDict`.
|
|
68
|
+
- The client retries rate limits, server errors, timeouts, and connection failures.
|
|
69
|
+
- `paginate()` yields items from cursor-based responses; `paginate_pages()` yields full response pages.
|
|
70
|
+
- HTTP errors map to exception classes such as `RateLimitError` and `NotFoundError`.
|
|
71
|
+
- Timeouts can use one value or separate connect and read values.
|
|
72
|
+
- The package supports Python 3.9+ and depends on `requests`.
|
|
78
73
|
|
|
79
|
-
## API
|
|
74
|
+
## API reference
|
|
80
75
|
|
|
81
76
|
### User
|
|
82
77
|
|
|
@@ -105,7 +100,7 @@ next_page = client.user.get_followers(
|
|
|
105
100
|
|--------|-------------|
|
|
106
101
|
| `client.tweet.get_details_and_conversation(tweet_id=...)` | Get tweet details and replies |
|
|
107
102
|
| `client.tweet.get_details_by_ids(ids=...)` | Get multiple tweets (max 200) |
|
|
108
|
-
| `client.tweet.get_retweets(tweet_id=...)` | Get who retweeted |
|
|
103
|
+
| `client.tweet.get_retweets(tweet_id=...)` | Get users who retweeted |
|
|
109
104
|
| `client.tweet.get_quotes(tweet_id=...)` | Get quote tweets |
|
|
110
105
|
| `client.tweet.translate(tweet_id=..., dst_lang=...)` | Translate a tweet |
|
|
111
106
|
|
|
@@ -127,6 +122,8 @@ next_page = client.user.get_followers(
|
|
|
127
122
|
| `client.profile.update(auth_token=..., name=..., bio=..., location=..., website=..., proxy=...)` | Update profile fields |
|
|
128
123
|
| `client.profile.avatar(auth_token=..., media=..., proxy=...)` | Update profile avatar |
|
|
129
124
|
| `client.profile.banner(auth_token=..., media=..., proxy=...)` | Update profile banner |
|
|
125
|
+
| `client.profile.remove_banner(auth_token=..., proxy=...)` | Remove the profile banner |
|
|
126
|
+
| `client.profile.set_privacy(auth_token=..., is_private=..., proxy=...)` | Make the account public or private |
|
|
130
127
|
|
|
131
128
|
### Interaction
|
|
132
129
|
|
|
@@ -171,8 +168,8 @@ next_page = client.user.get_followers(
|
|
|
171
168
|
| `client.community.create_quote_with_media(...)` | Quote post with media |
|
|
172
169
|
| `client.community.reply_post(...)` | Reply to community post |
|
|
173
170
|
| `client.community.reply_post_with_media(...)` | Reply with media |
|
|
174
|
-
| `client.community.join(auth_token=..., community_id=...)` | Join |
|
|
175
|
-
| `client.community.leave(auth_token=..., community_id=...)` | Leave |
|
|
171
|
+
| `client.community.join(auth_token=..., community_id=...)` | Join a community |
|
|
172
|
+
| `client.community.leave(auth_token=..., community_id=...)` | Leave a community |
|
|
176
173
|
|
|
177
174
|
### Space
|
|
178
175
|
|
|
@@ -185,24 +182,24 @@ next_page = client.user.get_followers(
|
|
|
185
182
|
|
|
186
183
|
| Method | Description |
|
|
187
184
|
|--------|-------------|
|
|
188
|
-
| `client.explore.search(query=..., type=...)` | Search tweets
|
|
185
|
+
| `client.explore.search(query=..., type=...)` | Search tweets, users, photos, or videos |
|
|
189
186
|
|
|
190
187
|
### Auth
|
|
191
188
|
|
|
192
189
|
| Method | Description |
|
|
193
190
|
|--------|-------------|
|
|
194
|
-
| `client.auth.login(username=..., password=..., proxy=..., country=...)` | Log in
|
|
191
|
+
| `client.auth.login(username=..., password=..., proxy=..., country=...)` | Log in and return auth tokens |
|
|
195
192
|
|
|
196
193
|
`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.
|
|
197
194
|
|
|
198
|
-
### X Chat (
|
|
195
|
+
### X Chat (encrypted DMs)
|
|
199
196
|
|
|
200
197
|
| Method | Description |
|
|
201
198
|
|--------|-------------|
|
|
202
199
|
| `client.xchat.setup(auth_token=..., user_id=..., pin=...)` | Initialize encrypted DMs |
|
|
203
200
|
| `client.xchat.get_conversations(auth_token=...)` | List conversations |
|
|
204
201
|
| `client.xchat.send(auth_token=..., recipient_id=..., message=...)` | Send message |
|
|
205
|
-
| `client.xchat.get_history(auth_token=..., conversation_id=...)` | Get history |
|
|
202
|
+
| `client.xchat.get_history(auth_token=..., conversation_id=...)` | Get conversation history |
|
|
206
203
|
| `client.xchat.can_dm(auth_token=..., user_ids=...)` | Check DM availability |
|
|
207
204
|
|
|
208
205
|
### Unencrypted DMs
|
|
@@ -212,13 +209,13 @@ next_page = client.user.get_followers(
|
|
|
212
209
|
| `client.dm.send_dm(auth_token=..., conversation_id=..., text=..., proxy=...)` | Send DM |
|
|
213
210
|
| `client.dm.get_dm_permissions(auth_token=..., recipient_ids=...)` | Check permissions |
|
|
214
211
|
| `client.dm.get_inbox_initial_state(auth_token=...)` | Get inbox state |
|
|
215
|
-
| `client.dm.get_inbox_trusted(auth_token=..., cursor=...)` |
|
|
216
|
-
| `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` |
|
|
212
|
+
| `client.dm.get_inbox_trusted(auth_token=..., cursor=...)` | Get the trusted inbox |
|
|
213
|
+
| `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` | Get message requests |
|
|
217
214
|
| `client.dm.get_conversation(auth_token=..., conversation_id=...)` | Get messages |
|
|
218
|
-
| `client.dm.get_dm_user_updates(auth_token=..., cursor=...)` | DM user updates |
|
|
219
|
-
| `client.dm.accept_conversation(auth_token=..., conversation_id=...)` | Accept request |
|
|
215
|
+
| `client.dm.get_dm_user_updates(auth_token=..., cursor=...)` | Get DM user updates |
|
|
216
|
+
| `client.dm.accept_conversation(auth_token=..., conversation_id=...)` | Accept a conversation request |
|
|
220
217
|
|
|
221
|
-
## Posting and
|
|
218
|
+
## Posting and profile media
|
|
222
219
|
|
|
223
220
|
Tweet media accepts an existing TweetAPI media ID, a URL, or inline base64 data:
|
|
224
221
|
|
|
@@ -254,9 +251,11 @@ client.profile.banner(
|
|
|
254
251
|
auth_token="AUTH_TOKEN",
|
|
255
252
|
media={"data": "BASE64_IMAGE_DATA", "type": "image/png"},
|
|
256
253
|
)
|
|
254
|
+
client.profile.remove_banner(auth_token="AUTH_TOKEN")
|
|
255
|
+
client.profile.set_privacy(auth_token="AUTH_TOKEN", is_private=True)
|
|
257
256
|
```
|
|
258
257
|
|
|
259
|
-
|
|
258
|
+
Use `client.list` for list mutations. The older helpers under `client.interaction` remain available:
|
|
260
259
|
|
|
261
260
|
```python
|
|
262
261
|
created = client.list.create(
|
|
@@ -289,9 +288,9 @@ client.community.create_quote_with_media(
|
|
|
289
288
|
)
|
|
290
289
|
```
|
|
291
290
|
|
|
292
|
-
##
|
|
291
|
+
## Pagination
|
|
293
292
|
|
|
294
|
-
|
|
293
|
+
`paginate()` yields individual items. `paginate_pages()` yields each full response page:
|
|
295
294
|
|
|
296
295
|
```python
|
|
297
296
|
from tweetapi import TweetAPI, paginate, paginate_pages
|
|
@@ -313,18 +312,16 @@ for page in paginate_pages(
|
|
|
313
312
|
print(f"Next cursor: {page['pagination']['nextCursor']}")
|
|
314
313
|
```
|
|
315
314
|
|
|
316
|
-
|
|
315
|
+
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.
|
|
317
316
|
|
|
318
|
-
##
|
|
317
|
+
## Retries
|
|
319
318
|
|
|
320
|
-
The
|
|
319
|
+
The client retries these transient failures:
|
|
321
320
|
|
|
322
|
-
-
|
|
323
|
-
-
|
|
324
|
-
- **Network errors** — retries on timeouts and connection failures
|
|
325
|
-
- **4xx (Client Error)** — never retried (400, 401, 403, 404 fail immediately)
|
|
321
|
+
- For a 429 response, it waits for `RateLimitError.retry_after` seconds, up to `max_retry_delay`.
|
|
322
|
+
- For a 5xx response, timeout, or connection failure, it uses exponential backoff with up to 25% jitter.
|
|
326
323
|
|
|
327
|
-
|
|
324
|
+
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.
|
|
328
325
|
|
|
329
326
|
```python
|
|
330
327
|
# Customize retry behavior
|
|
@@ -340,18 +337,17 @@ client = TweetAPI(
|
|
|
340
337
|
client = TweetAPI(api_key="YOUR_API_KEY", max_retries=0)
|
|
341
338
|
```
|
|
342
339
|
|
|
343
|
-
### Rate
|
|
340
|
+
### Rate-limit state
|
|
344
341
|
|
|
345
|
-
After a 429 response, the
|
|
342
|
+
After a 429 response, `client.rate_limit_info` records the retry delay and the time the response was received:
|
|
346
343
|
|
|
347
344
|
```python
|
|
348
345
|
print(client.rate_limit_info)
|
|
349
|
-
# {"retry_after": 30, "timestamp": 1712345678.0} — or None if no 429 encountered
|
|
350
346
|
```
|
|
351
347
|
|
|
352
|
-
## Error
|
|
348
|
+
## Error handling
|
|
353
349
|
|
|
354
|
-
The
|
|
350
|
+
The client raises retryable errors after it exhausts the configured retries. It raises other errors from the first response:
|
|
355
351
|
|
|
356
352
|
```python
|
|
357
353
|
from tweetapi import (
|
|
@@ -385,11 +381,12 @@ except TweetAPIError as e:
|
|
|
385
381
|
print(f"Error [{e.code}]: {e.message}")
|
|
386
382
|
```
|
|
387
383
|
|
|
388
|
-
Every error includes:
|
|
389
|
-
|
|
390
|
-
- `
|
|
391
|
-
- `
|
|
392
|
-
- `
|
|
384
|
+
Every error includes these attributes:
|
|
385
|
+
|
|
386
|
+
- `code`: API error code, such as `"ACCOUNT_SUSPENDED"` or `"RATE_LIMIT"`
|
|
387
|
+
- `status_code`: HTTP status code
|
|
388
|
+
- `message`: human-readable error message
|
|
389
|
+
- `details`: response context such as a field, reason, or retry delay
|
|
393
390
|
|
|
394
391
|
## Configuration
|
|
395
392
|
|
|
@@ -397,7 +394,7 @@ Every error includes:
|
|
|
397
394
|
client = TweetAPI(
|
|
398
395
|
api_key="YOUR_API_KEY", # Required
|
|
399
396
|
base_url="https://...", # Optional (default: https://api.tweetapi.com)
|
|
400
|
-
timeout=30, # Optional
|
|
397
|
+
timeout=30, # Optional; one value for connect and read
|
|
401
398
|
connect_timeout=10.0, # Optional (default: 10s)
|
|
402
399
|
read_timeout=30.0, # Optional (default: 30s)
|
|
403
400
|
max_retries=3, # Optional (default: 3, set 0 to disable)
|
|
@@ -417,8 +414,8 @@ client = TweetAPI(api_key="YOUR_API_KEY", timeout=(5, 30)) # (connect, read)
|
|
|
417
414
|
|
|
418
415
|
## Links
|
|
419
416
|
|
|
420
|
-
- [
|
|
421
|
-
- [
|
|
417
|
+
- [Documentation](https://tweetapi.com/docs?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
418
|
+
- [Create an API key](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
422
419
|
- [Dashboard](https://tweetapi.com/dashboard?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
423
420
|
- [Node.js SDK](https://github.com/tweetapi/node)
|
|
424
421
|
|
|
@@ -426,6 +423,4 @@ client = TweetAPI(api_key="YOUR_API_KEY", timeout=(5, 30)) # (connect, read)
|
|
|
426
423
|
|
|
427
424
|
MIT
|
|
428
425
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
*TweetAPI is a third-party service and is not affiliated with X Corp.*
|
|
426
|
+
TweetAPI is a third-party service and is not affiliated with X Corp.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# TweetAPI Python SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Access tweets, user profiles, followers, analytics, and full interaction capabilities. 70+ endpoints with built-in error handling and type hints.
|
|
3
|
+
`tweetapi` is the Python SDK for [TweetAPI](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk). It provides methods for users, tweets, posts, profiles, interactions, lists, communities, Spaces, search, authentication, and direct messages.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
@@ -10,7 +8,7 @@ Access tweets, user profiles, followers, analytics, and full interaction capabil
|
|
|
10
8
|
pip install tweetapi
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
## Quick
|
|
11
|
+
## Quick start
|
|
14
12
|
|
|
15
13
|
```python
|
|
16
14
|
from tweetapi import TweetAPI
|
|
@@ -19,7 +17,7 @@ client = TweetAPI(api_key="YOUR_API_KEY")
|
|
|
19
17
|
|
|
20
18
|
# Get a user profile
|
|
21
19
|
user = client.user.get_by_username(username="elonmusk")
|
|
22
|
-
print(user["data"]["followerCount"])
|
|
20
|
+
print(user["data"]["followerCount"])
|
|
23
21
|
|
|
24
22
|
# Search tweets
|
|
25
23
|
results = client.explore.search(query="bitcoin", type="Latest")
|
|
@@ -32,21 +30,18 @@ next_page = client.user.get_followers(
|
|
|
32
30
|
)
|
|
33
31
|
```
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
[Create an API key](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk) with 100 included requests. No credit card is required.
|
|
36
34
|
|
|
37
|
-
##
|
|
35
|
+
## SDK behavior
|
|
38
36
|
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
- **Split timeouts** — separate connect and read timeouts
|
|
46
|
-
- **Single dependency** — `requests` only
|
|
47
|
-
- **Python 3.9+** compatible
|
|
37
|
+
- Public methods have type annotations, and response models use `TypedDict`.
|
|
38
|
+
- The client retries rate limits, server errors, timeouts, and connection failures.
|
|
39
|
+
- `paginate()` yields items from cursor-based responses; `paginate_pages()` yields full response pages.
|
|
40
|
+
- HTTP errors map to exception classes such as `RateLimitError` and `NotFoundError`.
|
|
41
|
+
- Timeouts can use one value or separate connect and read values.
|
|
42
|
+
- The package supports Python 3.9+ and depends on `requests`.
|
|
48
43
|
|
|
49
|
-
## API
|
|
44
|
+
## API reference
|
|
50
45
|
|
|
51
46
|
### User
|
|
52
47
|
|
|
@@ -75,7 +70,7 @@ next_page = client.user.get_followers(
|
|
|
75
70
|
|--------|-------------|
|
|
76
71
|
| `client.tweet.get_details_and_conversation(tweet_id=...)` | Get tweet details and replies |
|
|
77
72
|
| `client.tweet.get_details_by_ids(ids=...)` | Get multiple tweets (max 200) |
|
|
78
|
-
| `client.tweet.get_retweets(tweet_id=...)` | Get who retweeted |
|
|
73
|
+
| `client.tweet.get_retweets(tweet_id=...)` | Get users who retweeted |
|
|
79
74
|
| `client.tweet.get_quotes(tweet_id=...)` | Get quote tweets |
|
|
80
75
|
| `client.tweet.translate(tweet_id=..., dst_lang=...)` | Translate a tweet |
|
|
81
76
|
|
|
@@ -97,6 +92,8 @@ next_page = client.user.get_followers(
|
|
|
97
92
|
| `client.profile.update(auth_token=..., name=..., bio=..., location=..., website=..., proxy=...)` | Update profile fields |
|
|
98
93
|
| `client.profile.avatar(auth_token=..., media=..., proxy=...)` | Update profile avatar |
|
|
99
94
|
| `client.profile.banner(auth_token=..., media=..., proxy=...)` | Update profile banner |
|
|
95
|
+
| `client.profile.remove_banner(auth_token=..., proxy=...)` | Remove the profile banner |
|
|
96
|
+
| `client.profile.set_privacy(auth_token=..., is_private=..., proxy=...)` | Make the account public or private |
|
|
100
97
|
|
|
101
98
|
### Interaction
|
|
102
99
|
|
|
@@ -141,8 +138,8 @@ next_page = client.user.get_followers(
|
|
|
141
138
|
| `client.community.create_quote_with_media(...)` | Quote post with media |
|
|
142
139
|
| `client.community.reply_post(...)` | Reply to community post |
|
|
143
140
|
| `client.community.reply_post_with_media(...)` | Reply with media |
|
|
144
|
-
| `client.community.join(auth_token=..., community_id=...)` | Join |
|
|
145
|
-
| `client.community.leave(auth_token=..., community_id=...)` | Leave |
|
|
141
|
+
| `client.community.join(auth_token=..., community_id=...)` | Join a community |
|
|
142
|
+
| `client.community.leave(auth_token=..., community_id=...)` | Leave a community |
|
|
146
143
|
|
|
147
144
|
### Space
|
|
148
145
|
|
|
@@ -155,24 +152,24 @@ next_page = client.user.get_followers(
|
|
|
155
152
|
|
|
156
153
|
| Method | Description |
|
|
157
154
|
|--------|-------------|
|
|
158
|
-
| `client.explore.search(query=..., type=...)` | Search tweets
|
|
155
|
+
| `client.explore.search(query=..., type=...)` | Search tweets, users, photos, or videos |
|
|
159
156
|
|
|
160
157
|
### Auth
|
|
161
158
|
|
|
162
159
|
| Method | Description |
|
|
163
160
|
|--------|-------------|
|
|
164
|
-
| `client.auth.login(username=..., password=..., proxy=..., country=...)` | Log in
|
|
161
|
+
| `client.auth.login(username=..., password=..., proxy=..., country=...)` | Log in and return auth tokens |
|
|
165
162
|
|
|
166
163
|
`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.
|
|
167
164
|
|
|
168
|
-
### X Chat (
|
|
165
|
+
### X Chat (encrypted DMs)
|
|
169
166
|
|
|
170
167
|
| Method | Description |
|
|
171
168
|
|--------|-------------|
|
|
172
169
|
| `client.xchat.setup(auth_token=..., user_id=..., pin=...)` | Initialize encrypted DMs |
|
|
173
170
|
| `client.xchat.get_conversations(auth_token=...)` | List conversations |
|
|
174
171
|
| `client.xchat.send(auth_token=..., recipient_id=..., message=...)` | Send message |
|
|
175
|
-
| `client.xchat.get_history(auth_token=..., conversation_id=...)` | Get history |
|
|
172
|
+
| `client.xchat.get_history(auth_token=..., conversation_id=...)` | Get conversation history |
|
|
176
173
|
| `client.xchat.can_dm(auth_token=..., user_ids=...)` | Check DM availability |
|
|
177
174
|
|
|
178
175
|
### Unencrypted DMs
|
|
@@ -182,13 +179,13 @@ next_page = client.user.get_followers(
|
|
|
182
179
|
| `client.dm.send_dm(auth_token=..., conversation_id=..., text=..., proxy=...)` | Send DM |
|
|
183
180
|
| `client.dm.get_dm_permissions(auth_token=..., recipient_ids=...)` | Check permissions |
|
|
184
181
|
| `client.dm.get_inbox_initial_state(auth_token=...)` | Get inbox state |
|
|
185
|
-
| `client.dm.get_inbox_trusted(auth_token=..., cursor=...)` |
|
|
186
|
-
| `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` |
|
|
182
|
+
| `client.dm.get_inbox_trusted(auth_token=..., cursor=...)` | Get the trusted inbox |
|
|
183
|
+
| `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` | Get message requests |
|
|
187
184
|
| `client.dm.get_conversation(auth_token=..., conversation_id=...)` | Get messages |
|
|
188
|
-
| `client.dm.get_dm_user_updates(auth_token=..., cursor=...)` | DM user updates |
|
|
189
|
-
| `client.dm.accept_conversation(auth_token=..., conversation_id=...)` | Accept request |
|
|
185
|
+
| `client.dm.get_dm_user_updates(auth_token=..., cursor=...)` | Get DM user updates |
|
|
186
|
+
| `client.dm.accept_conversation(auth_token=..., conversation_id=...)` | Accept a conversation request |
|
|
190
187
|
|
|
191
|
-
## Posting and
|
|
188
|
+
## Posting and profile media
|
|
192
189
|
|
|
193
190
|
Tweet media accepts an existing TweetAPI media ID, a URL, or inline base64 data:
|
|
194
191
|
|
|
@@ -224,9 +221,11 @@ client.profile.banner(
|
|
|
224
221
|
auth_token="AUTH_TOKEN",
|
|
225
222
|
media={"data": "BASE64_IMAGE_DATA", "type": "image/png"},
|
|
226
223
|
)
|
|
224
|
+
client.profile.remove_banner(auth_token="AUTH_TOKEN")
|
|
225
|
+
client.profile.set_privacy(auth_token="AUTH_TOKEN", is_private=True)
|
|
227
226
|
```
|
|
228
227
|
|
|
229
|
-
|
|
228
|
+
Use `client.list` for list mutations. The older helpers under `client.interaction` remain available:
|
|
230
229
|
|
|
231
230
|
```python
|
|
232
231
|
created = client.list.create(
|
|
@@ -259,9 +258,9 @@ client.community.create_quote_with_media(
|
|
|
259
258
|
)
|
|
260
259
|
```
|
|
261
260
|
|
|
262
|
-
##
|
|
261
|
+
## Pagination
|
|
263
262
|
|
|
264
|
-
|
|
263
|
+
`paginate()` yields individual items. `paginate_pages()` yields each full response page:
|
|
265
264
|
|
|
266
265
|
```python
|
|
267
266
|
from tweetapi import TweetAPI, paginate, paginate_pages
|
|
@@ -283,18 +282,16 @@ for page in paginate_pages(
|
|
|
283
282
|
print(f"Next cursor: {page['pagination']['nextCursor']}")
|
|
284
283
|
```
|
|
285
284
|
|
|
286
|
-
|
|
285
|
+
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.
|
|
287
286
|
|
|
288
|
-
##
|
|
287
|
+
## Retries
|
|
289
288
|
|
|
290
|
-
The
|
|
289
|
+
The client retries these transient failures:
|
|
291
290
|
|
|
292
|
-
-
|
|
293
|
-
-
|
|
294
|
-
- **Network errors** — retries on timeouts and connection failures
|
|
295
|
-
- **4xx (Client Error)** — never retried (400, 401, 403, 404 fail immediately)
|
|
291
|
+
- For a 429 response, it waits for `RateLimitError.retry_after` seconds, up to `max_retry_delay`.
|
|
292
|
+
- For a 5xx response, timeout, or connection failure, it uses exponential backoff with up to 25% jitter.
|
|
296
293
|
|
|
297
|
-
|
|
294
|
+
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.
|
|
298
295
|
|
|
299
296
|
```python
|
|
300
297
|
# Customize retry behavior
|
|
@@ -310,18 +307,17 @@ client = TweetAPI(
|
|
|
310
307
|
client = TweetAPI(api_key="YOUR_API_KEY", max_retries=0)
|
|
311
308
|
```
|
|
312
309
|
|
|
313
|
-
### Rate
|
|
310
|
+
### Rate-limit state
|
|
314
311
|
|
|
315
|
-
After a 429 response, the
|
|
312
|
+
After a 429 response, `client.rate_limit_info` records the retry delay and the time the response was received:
|
|
316
313
|
|
|
317
314
|
```python
|
|
318
315
|
print(client.rate_limit_info)
|
|
319
|
-
# {"retry_after": 30, "timestamp": 1712345678.0} — or None if no 429 encountered
|
|
320
316
|
```
|
|
321
317
|
|
|
322
|
-
## Error
|
|
318
|
+
## Error handling
|
|
323
319
|
|
|
324
|
-
The
|
|
320
|
+
The client raises retryable errors after it exhausts the configured retries. It raises other errors from the first response:
|
|
325
321
|
|
|
326
322
|
```python
|
|
327
323
|
from tweetapi import (
|
|
@@ -355,11 +351,12 @@ except TweetAPIError as e:
|
|
|
355
351
|
print(f"Error [{e.code}]: {e.message}")
|
|
356
352
|
```
|
|
357
353
|
|
|
358
|
-
Every error includes:
|
|
359
|
-
|
|
360
|
-
- `
|
|
361
|
-
- `
|
|
362
|
-
- `
|
|
354
|
+
Every error includes these attributes:
|
|
355
|
+
|
|
356
|
+
- `code`: API error code, such as `"ACCOUNT_SUSPENDED"` or `"RATE_LIMIT"`
|
|
357
|
+
- `status_code`: HTTP status code
|
|
358
|
+
- `message`: human-readable error message
|
|
359
|
+
- `details`: response context such as a field, reason, or retry delay
|
|
363
360
|
|
|
364
361
|
## Configuration
|
|
365
362
|
|
|
@@ -367,7 +364,7 @@ Every error includes:
|
|
|
367
364
|
client = TweetAPI(
|
|
368
365
|
api_key="YOUR_API_KEY", # Required
|
|
369
366
|
base_url="https://...", # Optional (default: https://api.tweetapi.com)
|
|
370
|
-
timeout=30, # Optional
|
|
367
|
+
timeout=30, # Optional; one value for connect and read
|
|
371
368
|
connect_timeout=10.0, # Optional (default: 10s)
|
|
372
369
|
read_timeout=30.0, # Optional (default: 30s)
|
|
373
370
|
max_retries=3, # Optional (default: 3, set 0 to disable)
|
|
@@ -387,8 +384,8 @@ client = TweetAPI(api_key="YOUR_API_KEY", timeout=(5, 30)) # (connect, read)
|
|
|
387
384
|
|
|
388
385
|
## Links
|
|
389
386
|
|
|
390
|
-
- [
|
|
391
|
-
- [
|
|
387
|
+
- [Documentation](https://tweetapi.com/docs?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
388
|
+
- [Create an API key](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
392
389
|
- [Dashboard](https://tweetapi.com/dashboard?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
|
|
393
390
|
- [Node.js SDK](https://github.com/tweetapi/node)
|
|
394
391
|
|
|
@@ -396,6 +393,4 @@ client = TweetAPI(api_key="YOUR_API_KEY", timeout=(5, 30)) # (connect, read)
|
|
|
396
393
|
|
|
397
394
|
MIT
|
|
398
395
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
*TweetAPI is a third-party service and is not affiliated with X Corp.*
|
|
396
|
+
TweetAPI is a third-party service and is not affiliated with X Corp.
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tweetapi"
|
|
7
|
-
version = "2.1
|
|
8
|
-
description = "
|
|
7
|
+
version = "2.2.1"
|
|
8
|
+
description = "Python SDK for TweetAPI's Twitter/X data and account API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.9"
|
|
@@ -4,7 +4,7 @@ from typing import Optional, TYPE_CHECKING
|
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
6
|
from ..client import TweetAPI
|
|
7
|
-
from ..types import ProfileMediaInput, UserResponse
|
|
7
|
+
from ..types import ProfileMediaInput, ProfilePrivacyResponse, UserResponse
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ProfileResource:
|
|
@@ -29,3 +29,15 @@ class ProfileResource:
|
|
|
29
29
|
return self._client._post("/tw-v2/profile/banner", {
|
|
30
30
|
"authToken": auth_token, "media": media, "proxy": proxy,
|
|
31
31
|
})
|
|
32
|
+
|
|
33
|
+
def remove_banner(self, *, auth_token: str, proxy: Optional[str] = None) -> UserResponse:
|
|
34
|
+
"""Remove the authenticated user's banner."""
|
|
35
|
+
return self._client._post("/tw-v2/profile/remove-banner", {
|
|
36
|
+
"authToken": auth_token, "proxy": proxy,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
def set_privacy(self, *, auth_token: str, is_private: bool, proxy: Optional[str] = None) -> ProfilePrivacyResponse:
|
|
40
|
+
"""Change the authenticated account between public and private."""
|
|
41
|
+
return self._client._post("/tw-v2/profile/privacy", {
|
|
42
|
+
"authToken": auth_token, "isPrivate": is_private, "proxy": proxy,
|
|
43
|
+
})
|
|
@@ -560,6 +560,14 @@ class UserResponse(TypedDict):
|
|
|
560
560
|
data: User
|
|
561
561
|
|
|
562
562
|
|
|
563
|
+
class ProfilePrivacy(TypedDict):
|
|
564
|
+
isPrivate: bool
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
class ProfilePrivacyResponse(TypedDict):
|
|
568
|
+
data: ProfilePrivacy
|
|
569
|
+
|
|
570
|
+
|
|
563
571
|
class UsersResponse(TypedDict):
|
|
564
572
|
data: list[User]
|
|
565
573
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|