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.
Files changed (23) hide show
  1. {tweetapi-2.1.0 → tweetapi-2.2.1}/PKG-INFO +54 -59
  2. {tweetapi-2.1.0 → tweetapi-2.2.1}/README.md +51 -56
  3. {tweetapi-2.1.0 → tweetapi-2.2.1}/pyproject.toml +2 -2
  4. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/__init__.py +1 -1
  5. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/profile.py +13 -1
  6. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/types.py +8 -0
  7. {tweetapi-2.1.0 → tweetapi-2.2.1}/.gitignore +0 -0
  8. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/client.py +0 -0
  9. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/errors.py +0 -0
  10. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/pagination.py +0 -0
  11. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/py.typed +0 -0
  12. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/__init__.py +0 -0
  13. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/auth.py +0 -0
  14. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/community.py +0 -0
  15. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/explore.py +0 -0
  16. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/interaction.py +0 -0
  17. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/list_.py +0 -0
  18. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/post.py +0 -0
  19. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/space.py +0 -0
  20. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/tweet.py +0 -0
  21. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/unencrypted_dm.py +0 -0
  22. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/user.py +0 -0
  23. {tweetapi-2.1.0 → tweetapi-2.2.1}/tweetapi/resources/xchat.py +0 -0
@@ -1,7 +1,7 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: tweetapi
3
- Version: 2.1.0
4
- Summary: Official Python SDK for TweetAPI — Twitter/X Data API for developers and researchers
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
- 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.
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 Start
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"]) # 180000000
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
- > **Get your free API key** — [100 requests, no credit card required](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
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
- ## Features
65
+ ## SDK behavior
68
66
 
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
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 Reference
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/users/photos/videos |
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, get auth tokens |
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 (Encrypted DMs)
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=...)` | Trusted inbox |
216
- | `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` | Message requests |
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 Profile Media
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
- Canonical list mutations live under `client.list`; the legacy interaction list helpers remain available:
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
- ## Auto-Pagination
291
+ ## Pagination
293
292
 
294
- Use the `paginate()` and `paginate_pages()` helpers to iterate through all pages automatically:
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
- Works with any paginated endpoint — followers, tweets, search results, list members, community posts, etc.
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
- ## Automatic Retry with Backoff
317
+ ## Retries
319
318
 
320
- The SDK automatically retries on transient errors with exponential backoff:
319
+ The client retries these transient failures:
321
320
 
322
- - **429 (Rate Limit)** — waits the `retry_after` duration from the API, then retries
323
- - **5xx (Server Error)** — retries with exponential backoff + jitter
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
- Default: 3 retries, 2x backoff, 1s initial delay, 30s max delay.
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 Limit Awareness
340
+ ### Rate-limit state
344
341
 
345
- After a 429 response, the SDK exposes the last known rate limit state:
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 Handling
348
+ ## Error handling
353
349
 
354
- 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:
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
- - `code` — API error code (e.g., `"ACCOUNT_SUSPENDED"`, `"RATE_LIMIT"`)
390
- - `status_code` — HTTP status code
391
- - `message` — Human-readable error message
392
- - `details` — Additional context (field, reason, retry_after, etc.)
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 — single value for both connect + read
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
- - [Full Documentation](https://tweetapi.com/docs?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
421
- - [Get API Key (Free)](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
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
- 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.
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 Start
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"]) # 180000000
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
- > **Get your free API key** — [100 requests, no credit card required](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
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
- ## Features
35
+ ## SDK behavior
38
36
 
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
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 Reference
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/users/photos/videos |
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, get auth tokens |
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 (Encrypted DMs)
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=...)` | Trusted inbox |
186
- | `client.dm.get_inbox_untrusted(auth_token=..., cursor=...)` | Message requests |
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 Profile Media
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
- Canonical list mutations live under `client.list`; the legacy interaction list helpers remain available:
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
- ## Auto-Pagination
261
+ ## Pagination
263
262
 
264
- Use the `paginate()` and `paginate_pages()` helpers to iterate through all pages automatically:
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
- Works with any paginated endpoint — followers, tweets, search results, list members, community posts, etc.
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
- ## Automatic Retry with Backoff
287
+ ## Retries
289
288
 
290
- The SDK automatically retries on transient errors with exponential backoff:
289
+ The client retries these transient failures:
291
290
 
292
- - **429 (Rate Limit)** — waits the `retry_after` duration from the API, then retries
293
- - **5xx (Server Error)** — retries with exponential backoff + jitter
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
- Default: 3 retries, 2x backoff, 1s initial delay, 30s max delay.
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 Limit Awareness
310
+ ### Rate-limit state
314
311
 
315
- After a 429 response, the SDK exposes the last known rate limit state:
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 Handling
318
+ ## Error handling
323
319
 
324
- 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:
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
- - `code` — API error code (e.g., `"ACCOUNT_SUSPENDED"`, `"RATE_LIMIT"`)
360
- - `status_code` — HTTP status code
361
- - `message` — Human-readable error message
362
- - `details` — Additional context (field, reason, retry_after, etc.)
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 — single value for both connect + read
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
- - [Full Documentation](https://tweetapi.com/docs?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
391
- - [Get API Key (Free)](https://tweetapi.com?utm_source=github&utm_medium=readme&utm_campaign=python-sdk)
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.0"
8
- description = "Official Python SDK for TweetAPI — Twitter/X Data API for developers and researchers"
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"
@@ -37,4 +37,4 @@ __all__ = [
37
37
  "paginate_pages",
38
38
  ]
39
39
 
40
- __version__ = "2.1.0"
40
+ __version__ = "2.2.1"
@@ -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