apitwitter 1.0.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.
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: apitwitter
3
+ Version: 1.0.0
4
+ Summary: Python SDK for the ApiTwitter REST API — Twitter/X data access
5
+ Author-email: ApiTwitter <support@apitwitter.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://apitwitter.com
8
+ Project-URL: Documentation, https://docs.apitwitter.com
9
+ Project-URL: Repository, https://github.com/apitwitter/python-sdk
10
+ Keywords: twitter,api,sdk,x,tweets,social-media
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: httpx>=0.24.0
24
+
25
+ # ApiTwitter Python SDK
26
+
27
+ Official Python SDK for the [ApiTwitter](https://apitwitter.com) REST API — access Twitter/X data without the official developer portal.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install apitwitter
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```python
38
+ from apitwitter import ApiTwitter
39
+
40
+ client = ApiTwitter("your-api-key")
41
+
42
+ # Get user profile (uses server pool — no cookies needed)
43
+ user = client.get_user("elonmusk")
44
+ print(user["name"], user["followers"])
45
+
46
+ # Search tweets
47
+ results = client.search("python programming", count=10)
48
+ for tweet in results["tweets"]:
49
+ print(tweet["text"])
50
+
51
+ # Get user tweets
52
+ tweets = client.get_user_tweets("elonmusk")
53
+ for tweet in tweets["tweets"]:
54
+ print(tweet["text"])
55
+ ```
56
+
57
+ ## Read Operations (Server Pool)
58
+
59
+ These endpoints use the server-side pool — no cookies or proxy needed:
60
+
61
+ ```python
62
+ client = ApiTwitter("your-api-key")
63
+
64
+ # Users
65
+ user = client.get_user("username")
66
+ user = client.get_user_by_id("12345")
67
+ users = client.get_users_batch(["id1", "id2", "id3"])
68
+ followers = client.get_followers("username", count=100)
69
+ following = client.get_following("username", count=100)
70
+
71
+ # Tweets
72
+ tweets = client.get_user_tweets("username")
73
+ tweets = client.get_tweets(["tweet_id_1", "tweet_id_2"])
74
+
75
+ # Search
76
+ results = client.search("query", product="Top", count=20)
77
+ # product options: "Top", "Latest", "People", "Photos", "Videos"
78
+ ```
79
+
80
+ ## Write Operations (Own Credentials)
81
+
82
+ Write endpoints require your own Twitter cookies and proxy:
83
+
84
+ ```python
85
+ COOKIE = "ct0=...;auth_token=..."
86
+ PROXY = "http://user:pass@host:port"
87
+
88
+ # Post a tweet
89
+ client.create_tweet("Hello from ApiTwitter SDK!", COOKIE, PROXY)
90
+
91
+ # Like / unlike
92
+ client.like("tweet_id", COOKIE, PROXY)
93
+ client.unlike("tweet_id", COOKIE, PROXY)
94
+
95
+ # Retweet
96
+ client.retweet("tweet_id", COOKIE, PROXY)
97
+
98
+ # Follow / unfollow
99
+ client.follow("user_id", COOKIE, PROXY)
100
+ client.unfollow("user_id", COOKIE, PROXY)
101
+
102
+ # Send DM
103
+ client.send_dm("user_id", "Hello!", COOKIE, PROXY)
104
+
105
+ # Bookmarks
106
+ client.add_bookmark("tweet_id", COOKIE, PROXY)
107
+ bookmarks = client.get_bookmarks(COOKIE, PROXY)
108
+
109
+ # Timeline
110
+ timeline = client.get_timeline_for_you(COOKIE, PROXY, count=20)
111
+ latest = client.get_timeline_latest(COOKIE, PROXY, count=20)
112
+ ```
113
+
114
+ ## Pagination
115
+
116
+ ```python
117
+ # First page
118
+ result = client.get_followers("username", count=100)
119
+ followers = result["followers"]
120
+
121
+ # Next page
122
+ if result.get("next_cursor"):
123
+ result = client.get_followers("username", count=100, cursor=result["next_cursor"])
124
+ followers.extend(result["followers"])
125
+ ```
126
+
127
+ ## Error Handling
128
+
129
+ ```python
130
+ from apitwitter import ApiTwitter
131
+ from apitwitter.exceptions import (
132
+ AuthenticationError,
133
+ InsufficientCreditsError,
134
+ RateLimitError,
135
+ NotFoundError,
136
+ )
137
+
138
+ client = ApiTwitter("your-api-key")
139
+
140
+ try:
141
+ user = client.get_user("username")
142
+ except AuthenticationError:
143
+ print("Invalid API key")
144
+ except InsufficientCreditsError:
145
+ print("Top up your balance")
146
+ except RateLimitError as e:
147
+ print(f"Rate limited, retry after {e.retry_after}s")
148
+ except NotFoundError:
149
+ print("User not found")
150
+ ```
151
+
152
+ ## Lists, Communities, Topics
153
+
154
+ ```python
155
+ COOKIE = "ct0=...;auth_token=..."
156
+ PROXY = "http://user:pass@host:port"
157
+
158
+ # Lists
159
+ client.create_list("My List", COOKIE, PROXY, description="A list")
160
+ client.get_list_tweets("list_id", COOKIE, PROXY)
161
+ client.add_list_member("list_id", "user_id", COOKIE, PROXY)
162
+
163
+ # Communities
164
+ communities = client.explore_communities(COOKIE, PROXY)
165
+ client.join_community("community_id", COOKIE, PROXY)
166
+
167
+ # Topics
168
+ client.follow_topic("topic_id", COOKIE, PROXY)
169
+ ```
170
+
171
+ ## Configuration
172
+
173
+ ```python
174
+ client = ApiTwitter(
175
+ api_key="your-api-key",
176
+ base_url="https://api.apitwitter.com", # default
177
+ timeout=30.0, # request timeout in seconds
178
+ )
179
+ ```
180
+
181
+ ## Links
182
+
183
+ - [Documentation](https://docs.apitwitter.com)
184
+ - [API Reference](https://docs.apitwitter.com/api-reference)
185
+ - [Website](https://apitwitter.com)
186
+ - [Get API Key](https://apitwitter.com/dashboard)
187
+ - [Telegram Support](https://t.me/ApiTwitter)
@@ -0,0 +1,163 @@
1
+ # ApiTwitter Python SDK
2
+
3
+ Official Python SDK for the [ApiTwitter](https://apitwitter.com) REST API — access Twitter/X data without the official developer portal.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install apitwitter
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from apitwitter import ApiTwitter
15
+
16
+ client = ApiTwitter("your-api-key")
17
+
18
+ # Get user profile (uses server pool — no cookies needed)
19
+ user = client.get_user("elonmusk")
20
+ print(user["name"], user["followers"])
21
+
22
+ # Search tweets
23
+ results = client.search("python programming", count=10)
24
+ for tweet in results["tweets"]:
25
+ print(tweet["text"])
26
+
27
+ # Get user tweets
28
+ tweets = client.get_user_tweets("elonmusk")
29
+ for tweet in tweets["tweets"]:
30
+ print(tweet["text"])
31
+ ```
32
+
33
+ ## Read Operations (Server Pool)
34
+
35
+ These endpoints use the server-side pool — no cookies or proxy needed:
36
+
37
+ ```python
38
+ client = ApiTwitter("your-api-key")
39
+
40
+ # Users
41
+ user = client.get_user("username")
42
+ user = client.get_user_by_id("12345")
43
+ users = client.get_users_batch(["id1", "id2", "id3"])
44
+ followers = client.get_followers("username", count=100)
45
+ following = client.get_following("username", count=100)
46
+
47
+ # Tweets
48
+ tweets = client.get_user_tweets("username")
49
+ tweets = client.get_tweets(["tweet_id_1", "tweet_id_2"])
50
+
51
+ # Search
52
+ results = client.search("query", product="Top", count=20)
53
+ # product options: "Top", "Latest", "People", "Photos", "Videos"
54
+ ```
55
+
56
+ ## Write Operations (Own Credentials)
57
+
58
+ Write endpoints require your own Twitter cookies and proxy:
59
+
60
+ ```python
61
+ COOKIE = "ct0=...;auth_token=..."
62
+ PROXY = "http://user:pass@host:port"
63
+
64
+ # Post a tweet
65
+ client.create_tweet("Hello from ApiTwitter SDK!", COOKIE, PROXY)
66
+
67
+ # Like / unlike
68
+ client.like("tweet_id", COOKIE, PROXY)
69
+ client.unlike("tweet_id", COOKIE, PROXY)
70
+
71
+ # Retweet
72
+ client.retweet("tweet_id", COOKIE, PROXY)
73
+
74
+ # Follow / unfollow
75
+ client.follow("user_id", COOKIE, PROXY)
76
+ client.unfollow("user_id", COOKIE, PROXY)
77
+
78
+ # Send DM
79
+ client.send_dm("user_id", "Hello!", COOKIE, PROXY)
80
+
81
+ # Bookmarks
82
+ client.add_bookmark("tweet_id", COOKIE, PROXY)
83
+ bookmarks = client.get_bookmarks(COOKIE, PROXY)
84
+
85
+ # Timeline
86
+ timeline = client.get_timeline_for_you(COOKIE, PROXY, count=20)
87
+ latest = client.get_timeline_latest(COOKIE, PROXY, count=20)
88
+ ```
89
+
90
+ ## Pagination
91
+
92
+ ```python
93
+ # First page
94
+ result = client.get_followers("username", count=100)
95
+ followers = result["followers"]
96
+
97
+ # Next page
98
+ if result.get("next_cursor"):
99
+ result = client.get_followers("username", count=100, cursor=result["next_cursor"])
100
+ followers.extend(result["followers"])
101
+ ```
102
+
103
+ ## Error Handling
104
+
105
+ ```python
106
+ from apitwitter import ApiTwitter
107
+ from apitwitter.exceptions import (
108
+ AuthenticationError,
109
+ InsufficientCreditsError,
110
+ RateLimitError,
111
+ NotFoundError,
112
+ )
113
+
114
+ client = ApiTwitter("your-api-key")
115
+
116
+ try:
117
+ user = client.get_user("username")
118
+ except AuthenticationError:
119
+ print("Invalid API key")
120
+ except InsufficientCreditsError:
121
+ print("Top up your balance")
122
+ except RateLimitError as e:
123
+ print(f"Rate limited, retry after {e.retry_after}s")
124
+ except NotFoundError:
125
+ print("User not found")
126
+ ```
127
+
128
+ ## Lists, Communities, Topics
129
+
130
+ ```python
131
+ COOKIE = "ct0=...;auth_token=..."
132
+ PROXY = "http://user:pass@host:port"
133
+
134
+ # Lists
135
+ client.create_list("My List", COOKIE, PROXY, description="A list")
136
+ client.get_list_tweets("list_id", COOKIE, PROXY)
137
+ client.add_list_member("list_id", "user_id", COOKIE, PROXY)
138
+
139
+ # Communities
140
+ communities = client.explore_communities(COOKIE, PROXY)
141
+ client.join_community("community_id", COOKIE, PROXY)
142
+
143
+ # Topics
144
+ client.follow_topic("topic_id", COOKIE, PROXY)
145
+ ```
146
+
147
+ ## Configuration
148
+
149
+ ```python
150
+ client = ApiTwitter(
151
+ api_key="your-api-key",
152
+ base_url="https://api.apitwitter.com", # default
153
+ timeout=30.0, # request timeout in seconds
154
+ )
155
+ ```
156
+
157
+ ## Links
158
+
159
+ - [Documentation](https://docs.apitwitter.com)
160
+ - [API Reference](https://docs.apitwitter.com/api-reference)
161
+ - [Website](https://apitwitter.com)
162
+ - [Get API Key](https://apitwitter.com/dashboard)
163
+ - [Telegram Support](https://t.me/ApiTwitter)
@@ -0,0 +1,6 @@
1
+ """ApiTwitter — Python SDK for the ApiTwitter REST API."""
2
+
3
+ from apitwitter.client import ApiTwitter
4
+
5
+ __all__ = ["ApiTwitter"]
6
+ __version__ = "1.0.0"
@@ -0,0 +1,517 @@
1
+ """ApiTwitter SDK main client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ from apitwitter.exceptions import (
10
+ ApiTwitterError,
11
+ AuthenticationError,
12
+ InsufficientCreditsError,
13
+ NotFoundError,
14
+ RateLimitError,
15
+ ValidationError,
16
+ )
17
+
18
+
19
+ class ApiTwitter:
20
+ """Client for the ApiTwitter REST API.
21
+
22
+ Usage::
23
+
24
+ from apitwitter import ApiTwitter
25
+
26
+ client = ApiTwitter("your-api-key")
27
+ user = client.get_user("elonmusk")
28
+ print(user)
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ api_key: str,
34
+ base_url: str = "https://api.apitwitter.com",
35
+ timeout: float = 30.0,
36
+ ):
37
+ self._api_key = api_key
38
+ self._base_url = base_url.rstrip("/")
39
+ self._client = httpx.Client(
40
+ base_url=self._base_url,
41
+ headers={"X-API-Key": api_key, "Content-Type": "application/json"},
42
+ timeout=timeout,
43
+ )
44
+
45
+ def close(self):
46
+ self._client.close()
47
+
48
+ def __enter__(self):
49
+ return self
50
+
51
+ def __exit__(self, *args):
52
+ self.close()
53
+
54
+ # -- internal helpers --
55
+
56
+ def _request(self, method: str, path: str, **kwargs) -> Any:
57
+ resp = self._client.request(method, path, **kwargs)
58
+ return self._handle_response(resp)
59
+
60
+ def _get(self, path: str, params: dict | None = None) -> Any:
61
+ return self._request("GET", path, params=params)
62
+
63
+ def _post(self, path: str, body: dict | None = None) -> Any:
64
+ return self._request("POST", path, json=body)
65
+
66
+ def _delete(self, path: str, body: dict | None = None) -> Any:
67
+ return self._request("DELETE", path, json=body)
68
+
69
+ def _patch(self, path: str, body: dict | None = None) -> Any:
70
+ return self._request("PATCH", path, json=body)
71
+
72
+ def _handle_response(self, resp: httpx.Response) -> Any:
73
+ try:
74
+ data = resp.json()
75
+ except Exception:
76
+ data = {"msg": resp.text}
77
+
78
+ if resp.status_code == 200:
79
+ return data.get("data", data)
80
+
81
+ msg = data.get("msg", data.get("message", str(data)))
82
+ status = resp.status_code
83
+
84
+ if status == 401:
85
+ raise AuthenticationError(msg, status_code=status, response=data)
86
+ if status == 402:
87
+ raise InsufficientCreditsError(msg, status_code=status, response=data)
88
+ if status == 404:
89
+ raise NotFoundError(msg, status_code=status, response=data)
90
+ if status == 429:
91
+ retry = float(data.get("retry_after", 0))
92
+ raise RateLimitError(msg, retry_after=retry, status_code=status, response=data)
93
+ if status == 400:
94
+ raise ValidationError(msg, status_code=status, response=data)
95
+ raise ApiTwitterError(msg, status_code=status, response=data)
96
+
97
+ # ── Session ───────────────────────────────────────────────
98
+
99
+ def verify_session(self, cookie: str, proxy: str, *, user_agent: str | None = None) -> str:
100
+ """Verify Twitter cookies. Returns the authenticated screen name."""
101
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
102
+ if user_agent:
103
+ body["user_agent"] = user_agent
104
+ return self._post("/twitter/session/verify", body)
105
+
106
+ # ── Users (GET — pool) ────────────────────────────────────
107
+
108
+ def get_user(self, username: str) -> dict:
109
+ """Get user profile by screen name (uses server pool)."""
110
+ return self._get(f"/twitter/user/{username}")
111
+
112
+ def get_user_by_id(self, user_id: str) -> dict:
113
+ """Get user profile by numeric ID (uses server pool)."""
114
+ return self._get(f"/twitter/user/id/{user_id}")
115
+
116
+ def get_users_batch(self, user_ids: list[str]) -> list[dict]:
117
+ """Batch get users by IDs (uses server pool)."""
118
+ return self._get("/twitter/users/batch", params={"userIds": ",".join(user_ids)})
119
+
120
+ def get_followers(self, username: str, count: int = 200, cursor: str | None = None) -> dict:
121
+ """Get followers of a user (uses server pool)."""
122
+ params: dict[str, Any] = {"count": count}
123
+ if cursor:
124
+ params["cursor"] = cursor
125
+ return self._get(f"/twitter/user/{username}/followers", params=params)
126
+
127
+ def get_following(self, username: str, count: int = 200, cursor: str | None = None) -> dict:
128
+ """Get who a user follows (uses server pool)."""
129
+ params: dict[str, Any] = {"count": count}
130
+ if cursor:
131
+ params["cursor"] = cursor
132
+ return self._get(f"/twitter/user/{username}/following", params=params)
133
+
134
+ def get_followers_you_know(self, username: str, count: int = 20, cursor: str | None = None) -> dict:
135
+ """Get mutual followers (uses server pool)."""
136
+ params: dict[str, Any] = {"count": count}
137
+ if cursor:
138
+ params["cursor"] = cursor
139
+ return self._get(f"/twitter/user/{username}/followers-you-know", params=params)
140
+
141
+ # ── Users (POST — own credentials) ────────────────────────
142
+
143
+ def get_user_post(self, username: str, cookie: str, proxy: str) -> dict:
144
+ """Get user profile using your own credentials."""
145
+ return self._post(f"/twitter/user/{username}", {"cookie": cookie, "proxy": proxy})
146
+
147
+ def get_user_likes(self, username: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
148
+ """Get a user's liked tweets."""
149
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
150
+ if cursor:
151
+ body["cursor"] = cursor
152
+ return self._post(f"/twitter/user/{username}/likes", body)
153
+
154
+ def get_user_media(self, username: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
155
+ """Get a user's media tweets."""
156
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
157
+ if cursor:
158
+ body["cursor"] = cursor
159
+ return self._post(f"/twitter/user/{username}/media", body)
160
+
161
+ def get_user_replies(self, username: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
162
+ """Get a user's tweets and replies."""
163
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
164
+ if cursor:
165
+ body["cursor"] = cursor
166
+ return self._post(f"/twitter/user/{username}/replies", body)
167
+
168
+ def get_blocked(self, cookie: str, proxy: str, count: int = 200, cursor: str | None = None) -> dict:
169
+ """Get blocked accounts."""
170
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
171
+ if cursor:
172
+ body["cursor"] = cursor
173
+ return self._post("/twitter/blocked", body)
174
+
175
+ def get_muted(self, cookie: str, proxy: str, count: int = 200, cursor: str | None = None) -> dict:
176
+ """Get muted accounts."""
177
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
178
+ if cursor:
179
+ body["cursor"] = cursor
180
+ return self._post("/twitter/muted", body)
181
+
182
+ def remove_follower(self, user_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
183
+ """Remove a follower."""
184
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
185
+ if user_agent:
186
+ body["user_agent"] = user_agent
187
+ return self._post(f"/twitter/user/{user_id}/remove-follower", body)
188
+
189
+ # ── Tweets (GET — pool) ───────────────────────────────────
190
+
191
+ def get_user_tweets(self, username: str, cursor: str | None = None) -> dict:
192
+ """Get a user's tweets (uses server pool)."""
193
+ params: dict[str, Any] = {}
194
+ if cursor:
195
+ params["cursor"] = cursor
196
+ return self._get(f"/twitter/user/{username}/tweets", params=params)
197
+
198
+ def get_tweets(self, tweet_ids: list[str]) -> list[dict]:
199
+ """Get tweets by IDs (uses server pool)."""
200
+ return self._get("/twitter/tweets/lookup", params={"tweet_ids": ",".join(tweet_ids)})
201
+
202
+ def search(self, query: str, product: str = "Top", count: int = 20, cursor: str | None = None) -> dict:
203
+ """Search tweets (uses server pool)."""
204
+ params: dict[str, Any] = {"query": query, "product": product, "count": count}
205
+ if cursor:
206
+ params["cursor"] = cursor
207
+ return self._get("/twitter/search", params=params)
208
+
209
+ # ── Tweets (POST — own credentials) ───────────────────────
210
+
211
+ def create_tweet(self, tweet_text: str, cookie: str, proxy: str, *, reply_to: str | None = None, user_agent: str | None = None) -> dict:
212
+ """Post a new tweet."""
213
+ body: dict[str, Any] = {"tweet_text": tweet_text, "cookie": cookie, "proxy": proxy}
214
+ if reply_to:
215
+ body["reply_to_tweet_id"] = reply_to
216
+ if user_agent:
217
+ body["user_agent"] = user_agent
218
+ return self._post("/twitter/tweets", body)
219
+
220
+ def delete_tweet(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
221
+ """Delete a tweet."""
222
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
223
+ if user_agent:
224
+ body["user_agent"] = user_agent
225
+ return self._delete(f"/twitter/tweets/{tweet_id}", body)
226
+
227
+ def retweet(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
228
+ """Retweet a tweet."""
229
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
230
+ if user_agent:
231
+ body["user_agent"] = user_agent
232
+ return self._post(f"/twitter/tweets/{tweet_id}/retweet", body)
233
+
234
+ def unretweet(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
235
+ """Remove a retweet."""
236
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
237
+ if user_agent:
238
+ body["user_agent"] = user_agent
239
+ return self._delete(f"/twitter/tweets/{tweet_id}/retweet", body)
240
+
241
+ def pin_tweet(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
242
+ """Pin a tweet to profile."""
243
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
244
+ if user_agent:
245
+ body["user_agent"] = user_agent
246
+ return self._post(f"/twitter/tweets/{tweet_id}/pin", body)
247
+
248
+ def unpin_tweet(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
249
+ """Unpin a tweet."""
250
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
251
+ if user_agent:
252
+ body["user_agent"] = user_agent
253
+ return self._delete(f"/twitter/tweets/{tweet_id}/pin", body)
254
+
255
+ # ── Engagement ────────────────────────────────────────────
256
+
257
+ def like(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
258
+ """Like a tweet."""
259
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
260
+ if user_agent:
261
+ body["user_agent"] = user_agent
262
+ return self._post(f"/twitter/tweets/{tweet_id}/like", body)
263
+
264
+ def unlike(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
265
+ """Unlike a tweet."""
266
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
267
+ if user_agent:
268
+ body["user_agent"] = user_agent
269
+ return self._post(f"/twitter/tweets/{tweet_id}/unlike", body)
270
+
271
+ def follow(self, user_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
272
+ """Follow a user."""
273
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
274
+ if user_agent:
275
+ body["user_agent"] = user_agent
276
+ return self._post(f"/twitter/user/{user_id}/follow", body)
277
+
278
+ def unfollow(self, user_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
279
+ """Unfollow a user."""
280
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
281
+ if user_agent:
282
+ body["user_agent"] = user_agent
283
+ return self._post(f"/twitter/user/{user_id}/unfollow", body)
284
+
285
+ # ── DM ────────────────────────────────────────────────────
286
+
287
+ def send_dm(self, user_id: str, text: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
288
+ """Send a direct message."""
289
+ body: dict[str, Any] = {"text": text, "cookie": cookie, "proxy": proxy}
290
+ if user_agent:
291
+ body["user_agent"] = user_agent
292
+ return self._post(f"/twitter/dm/{user_id}", body)
293
+
294
+ def dm_block(self, user_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
295
+ """Block a user in DMs."""
296
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
297
+ if user_agent:
298
+ body["user_agent"] = user_agent
299
+ return self._post(f"/twitter/dm/block/{user_id}", body)
300
+
301
+ def dm_unblock(self, user_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
302
+ """Unblock a user in DMs."""
303
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
304
+ if user_agent:
305
+ body["user_agent"] = user_agent
306
+ return self._delete(f"/twitter/dm/block/{user_id}", body)
307
+
308
+ # ── Bookmarks ─────────────────────────────────────────────
309
+
310
+ def add_bookmark(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
311
+ """Bookmark a tweet."""
312
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
313
+ if user_agent:
314
+ body["user_agent"] = user_agent
315
+ return self._post(f"/twitter/tweets/{tweet_id}/bookmark", body)
316
+
317
+ def remove_bookmark(self, tweet_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
318
+ """Remove a bookmark."""
319
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
320
+ if user_agent:
321
+ body["user_agent"] = user_agent
322
+ return self._delete(f"/twitter/tweets/{tweet_id}/bookmark", body)
323
+
324
+ def get_bookmarks(self, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
325
+ """Get bookmarked tweets."""
326
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
327
+ if cursor:
328
+ body["cursor"] = cursor
329
+ return self._post("/twitter/bookmarks", body)
330
+
331
+ # ── Timeline ──────────────────────────────────────────────
332
+
333
+ def get_timeline_for_you(self, cookie: str, proxy: str, count: int = 20, cursor: str | None = None, *, user_agent: str | None = None) -> dict:
334
+ """Get the For You home timeline."""
335
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
336
+ if cursor:
337
+ body["cursor"] = cursor
338
+ if user_agent:
339
+ body["user_agent"] = user_agent
340
+ return self._post("/twitter/timeline/for-you", body)
341
+
342
+ def get_timeline_latest(self, cookie: str, proxy: str, count: int = 20, cursor: str | None = None, *, user_agent: str | None = None) -> dict:
343
+ """Get the Latest (Following) home timeline."""
344
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
345
+ if cursor:
346
+ body["cursor"] = cursor
347
+ if user_agent:
348
+ body["user_agent"] = user_agent
349
+ return self._post("/twitter/timeline/latest", body)
350
+
351
+ # ── Search (POST) ─────────────────────────────────────────
352
+
353
+ def search_post(self, query: str, cookie: str, proxy: str, product: str = "Top", count: int = 20, cursor: str | None = None) -> dict:
354
+ """Search tweets with your own credentials."""
355
+ body: dict[str, Any] = {"query": query, "cookie": cookie, "proxy": proxy, "product": product, "count": count}
356
+ if cursor:
357
+ body["cursor"] = cursor
358
+ return self._post("/twitter/search", body)
359
+
360
+ # ── Lists ─────────────────────────────────────────────────
361
+
362
+ def create_list(self, name: str, cookie: str, proxy: str, *, description: str | None = None, is_private: bool = False, user_agent: str | None = None) -> dict:
363
+ """Create a new list."""
364
+ body: dict[str, Any] = {"name": name, "cookie": cookie, "proxy": proxy, "is_private": is_private}
365
+ if description:
366
+ body["description"] = description
367
+ if user_agent:
368
+ body["user_agent"] = user_agent
369
+ return self._post("/twitter/lists", body)
370
+
371
+ def delete_list(self, list_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
372
+ """Delete a list."""
373
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
374
+ if user_agent:
375
+ body["user_agent"] = user_agent
376
+ return self._delete(f"/twitter/lists/{list_id}", body)
377
+
378
+ def update_list(self, list_id: str, cookie: str, proxy: str, *, name: str | None = None, description: str | None = None, is_private: bool | None = None, user_agent: str | None = None) -> dict:
379
+ """Update a list."""
380
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
381
+ if name:
382
+ body["name"] = name
383
+ if description is not None:
384
+ body["description"] = description
385
+ if is_private is not None:
386
+ body["is_private"] = is_private
387
+ if user_agent:
388
+ body["user_agent"] = user_agent
389
+ return self._patch(f"/twitter/lists/{list_id}", body)
390
+
391
+ def get_list_info(self, list_id: str, cookie: str, proxy: str) -> dict:
392
+ """Get list details."""
393
+ return self._post(f"/twitter/lists/{list_id}/info", {"cookie": cookie, "proxy": proxy})
394
+
395
+ def get_list_tweets(self, list_id: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
396
+ """Get tweets from a list."""
397
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
398
+ if cursor:
399
+ body["cursor"] = cursor
400
+ return self._post(f"/twitter/lists/{list_id}/tweets", body)
401
+
402
+ def get_list_members(self, list_id: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
403
+ """Get list members."""
404
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
405
+ if cursor:
406
+ body["cursor"] = cursor
407
+ return self._post(f"/twitter/lists/{list_id}/members", body)
408
+
409
+ def add_list_member(self, list_id: str, user_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
410
+ """Add a member to a list."""
411
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
412
+ if user_agent:
413
+ body["user_agent"] = user_agent
414
+ return self._post(f"/twitter/lists/{list_id}/members/{user_id}/add", body)
415
+
416
+ def remove_list_member(self, list_id: str, user_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
417
+ """Remove a member from a list."""
418
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
419
+ if user_agent:
420
+ body["user_agent"] = user_agent
421
+ return self._post(f"/twitter/lists/{list_id}/members/{user_id}/remove", body)
422
+
423
+ def get_owned_lists(self, user_id: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
424
+ """Get lists owned by a user."""
425
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "user_id": user_id, "count": count}
426
+ if cursor:
427
+ body["cursor"] = cursor
428
+ return self._post("/twitter/lists/owned", body)
429
+
430
+ def get_list_memberships(self, user_id: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
431
+ """Get lists a user is a member of."""
432
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "user_id": user_id, "count": count}
433
+ if cursor:
434
+ body["cursor"] = cursor
435
+ return self._post("/twitter/lists/memberships", body)
436
+
437
+ def get_list_subscribers(self, list_id: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
438
+ """Get list subscribers."""
439
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
440
+ if cursor:
441
+ body["cursor"] = cursor
442
+ return self._post(f"/twitter/lists/{list_id}/subscribers", body)
443
+
444
+ def subscribe_to_list(self, list_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
445
+ """Subscribe to a list."""
446
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
447
+ if user_agent:
448
+ body["user_agent"] = user_agent
449
+ return self._post(f"/twitter/lists/{list_id}/subscribe", body)
450
+
451
+ def unsubscribe_from_list(self, list_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
452
+ """Unsubscribe from a list."""
453
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
454
+ if user_agent:
455
+ body["user_agent"] = user_agent
456
+ return self._delete(f"/twitter/lists/{list_id}/subscribe", body)
457
+
458
+ # ── Communities ───────────────────────────────────────────
459
+
460
+ def explore_communities(self, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
461
+ """Explore communities."""
462
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
463
+ if cursor:
464
+ body["cursor"] = cursor
465
+ return self._post("/twitter/communities/explore", body)
466
+
467
+ def get_community(self, community_id: str, cookie: str, proxy: str) -> dict:
468
+ """Get community info."""
469
+ return self._post(f"/twitter/communities/{community_id}", {"cookie": cookie, "proxy": proxy})
470
+
471
+ def join_community(self, community_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
472
+ """Join a community."""
473
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
474
+ if user_agent:
475
+ body["user_agent"] = user_agent
476
+ return self._post(f"/twitter/communities/{community_id}/join", body)
477
+
478
+ def leave_community(self, community_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
479
+ """Leave a community."""
480
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
481
+ if user_agent:
482
+ body["user_agent"] = user_agent
483
+ return self._delete(f"/twitter/communities/{community_id}/join", body)
484
+
485
+ def get_community_tweets(self, community_id: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
486
+ """Get community tweets."""
487
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
488
+ if cursor:
489
+ body["cursor"] = cursor
490
+ return self._post(f"/twitter/communities/{community_id}/tweets", body)
491
+
492
+ def get_community_media(self, community_id: str, cookie: str, proxy: str, count: int = 20, cursor: str | None = None) -> dict:
493
+ """Get community media."""
494
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy, "count": count}
495
+ if cursor:
496
+ body["cursor"] = cursor
497
+ return self._post(f"/twitter/communities/{community_id}/media", body)
498
+
499
+ # ── Topics ────────────────────────────────────────────────
500
+
501
+ def get_topic(self, topic_id: str, cookie: str, proxy: str) -> dict:
502
+ """Get topic info."""
503
+ return self._post(f"/twitter/topics/{topic_id}", {"cookie": cookie, "proxy": proxy})
504
+
505
+ def follow_topic(self, topic_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
506
+ """Follow a topic."""
507
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
508
+ if user_agent:
509
+ body["user_agent"] = user_agent
510
+ return self._post(f"/twitter/topics/{topic_id}/follow", body)
511
+
512
+ def unfollow_topic(self, topic_id: str, cookie: str, proxy: str, *, user_agent: str | None = None) -> dict:
513
+ """Unfollow a topic."""
514
+ body: dict[str, Any] = {"cookie": cookie, "proxy": proxy}
515
+ if user_agent:
516
+ body["user_agent"] = user_agent
517
+ return self._delete(f"/twitter/topics/{topic_id}/follow", body)
@@ -0,0 +1,34 @@
1
+ """ApiTwitter SDK exceptions."""
2
+
3
+
4
+ class ApiTwitterError(Exception):
5
+ """Base exception for all API errors."""
6
+
7
+ def __init__(self, message: str, status_code: int = 0, response: dict | None = None):
8
+ super().__init__(message)
9
+ self.status_code = status_code
10
+ self.response = response or {}
11
+
12
+
13
+ class AuthenticationError(ApiTwitterError):
14
+ """Raised when the API key is invalid or missing."""
15
+
16
+
17
+ class InsufficientCreditsError(ApiTwitterError):
18
+ """Raised when the user doesn't have enough credits."""
19
+
20
+
21
+ class RateLimitError(ApiTwitterError):
22
+ """Raised when the rate limit is exceeded."""
23
+
24
+ def __init__(self, message: str, retry_after: float = 0, **kwargs):
25
+ super().__init__(message, **kwargs)
26
+ self.retry_after = retry_after
27
+
28
+
29
+ class NotFoundError(ApiTwitterError):
30
+ """Raised when a resource is not found."""
31
+
32
+
33
+ class ValidationError(ApiTwitterError):
34
+ """Raised when request parameters are invalid."""
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: apitwitter
3
+ Version: 1.0.0
4
+ Summary: Python SDK for the ApiTwitter REST API — Twitter/X data access
5
+ Author-email: ApiTwitter <support@apitwitter.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://apitwitter.com
8
+ Project-URL: Documentation, https://docs.apitwitter.com
9
+ Project-URL: Repository, https://github.com/apitwitter/python-sdk
10
+ Keywords: twitter,api,sdk,x,tweets,social-media
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: httpx>=0.24.0
24
+
25
+ # ApiTwitter Python SDK
26
+
27
+ Official Python SDK for the [ApiTwitter](https://apitwitter.com) REST API — access Twitter/X data without the official developer portal.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install apitwitter
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```python
38
+ from apitwitter import ApiTwitter
39
+
40
+ client = ApiTwitter("your-api-key")
41
+
42
+ # Get user profile (uses server pool — no cookies needed)
43
+ user = client.get_user("elonmusk")
44
+ print(user["name"], user["followers"])
45
+
46
+ # Search tweets
47
+ results = client.search("python programming", count=10)
48
+ for tweet in results["tweets"]:
49
+ print(tweet["text"])
50
+
51
+ # Get user tweets
52
+ tweets = client.get_user_tweets("elonmusk")
53
+ for tweet in tweets["tweets"]:
54
+ print(tweet["text"])
55
+ ```
56
+
57
+ ## Read Operations (Server Pool)
58
+
59
+ These endpoints use the server-side pool — no cookies or proxy needed:
60
+
61
+ ```python
62
+ client = ApiTwitter("your-api-key")
63
+
64
+ # Users
65
+ user = client.get_user("username")
66
+ user = client.get_user_by_id("12345")
67
+ users = client.get_users_batch(["id1", "id2", "id3"])
68
+ followers = client.get_followers("username", count=100)
69
+ following = client.get_following("username", count=100)
70
+
71
+ # Tweets
72
+ tweets = client.get_user_tweets("username")
73
+ tweets = client.get_tweets(["tweet_id_1", "tweet_id_2"])
74
+
75
+ # Search
76
+ results = client.search("query", product="Top", count=20)
77
+ # product options: "Top", "Latest", "People", "Photos", "Videos"
78
+ ```
79
+
80
+ ## Write Operations (Own Credentials)
81
+
82
+ Write endpoints require your own Twitter cookies and proxy:
83
+
84
+ ```python
85
+ COOKIE = "ct0=...;auth_token=..."
86
+ PROXY = "http://user:pass@host:port"
87
+
88
+ # Post a tweet
89
+ client.create_tweet("Hello from ApiTwitter SDK!", COOKIE, PROXY)
90
+
91
+ # Like / unlike
92
+ client.like("tweet_id", COOKIE, PROXY)
93
+ client.unlike("tweet_id", COOKIE, PROXY)
94
+
95
+ # Retweet
96
+ client.retweet("tweet_id", COOKIE, PROXY)
97
+
98
+ # Follow / unfollow
99
+ client.follow("user_id", COOKIE, PROXY)
100
+ client.unfollow("user_id", COOKIE, PROXY)
101
+
102
+ # Send DM
103
+ client.send_dm("user_id", "Hello!", COOKIE, PROXY)
104
+
105
+ # Bookmarks
106
+ client.add_bookmark("tweet_id", COOKIE, PROXY)
107
+ bookmarks = client.get_bookmarks(COOKIE, PROXY)
108
+
109
+ # Timeline
110
+ timeline = client.get_timeline_for_you(COOKIE, PROXY, count=20)
111
+ latest = client.get_timeline_latest(COOKIE, PROXY, count=20)
112
+ ```
113
+
114
+ ## Pagination
115
+
116
+ ```python
117
+ # First page
118
+ result = client.get_followers("username", count=100)
119
+ followers = result["followers"]
120
+
121
+ # Next page
122
+ if result.get("next_cursor"):
123
+ result = client.get_followers("username", count=100, cursor=result["next_cursor"])
124
+ followers.extend(result["followers"])
125
+ ```
126
+
127
+ ## Error Handling
128
+
129
+ ```python
130
+ from apitwitter import ApiTwitter
131
+ from apitwitter.exceptions import (
132
+ AuthenticationError,
133
+ InsufficientCreditsError,
134
+ RateLimitError,
135
+ NotFoundError,
136
+ )
137
+
138
+ client = ApiTwitter("your-api-key")
139
+
140
+ try:
141
+ user = client.get_user("username")
142
+ except AuthenticationError:
143
+ print("Invalid API key")
144
+ except InsufficientCreditsError:
145
+ print("Top up your balance")
146
+ except RateLimitError as e:
147
+ print(f"Rate limited, retry after {e.retry_after}s")
148
+ except NotFoundError:
149
+ print("User not found")
150
+ ```
151
+
152
+ ## Lists, Communities, Topics
153
+
154
+ ```python
155
+ COOKIE = "ct0=...;auth_token=..."
156
+ PROXY = "http://user:pass@host:port"
157
+
158
+ # Lists
159
+ client.create_list("My List", COOKIE, PROXY, description="A list")
160
+ client.get_list_tweets("list_id", COOKIE, PROXY)
161
+ client.add_list_member("list_id", "user_id", COOKIE, PROXY)
162
+
163
+ # Communities
164
+ communities = client.explore_communities(COOKIE, PROXY)
165
+ client.join_community("community_id", COOKIE, PROXY)
166
+
167
+ # Topics
168
+ client.follow_topic("topic_id", COOKIE, PROXY)
169
+ ```
170
+
171
+ ## Configuration
172
+
173
+ ```python
174
+ client = ApiTwitter(
175
+ api_key="your-api-key",
176
+ base_url="https://api.apitwitter.com", # default
177
+ timeout=30.0, # request timeout in seconds
178
+ )
179
+ ```
180
+
181
+ ## Links
182
+
183
+ - [Documentation](https://docs.apitwitter.com)
184
+ - [API Reference](https://docs.apitwitter.com/api-reference)
185
+ - [Website](https://apitwitter.com)
186
+ - [Get API Key](https://apitwitter.com/dashboard)
187
+ - [Telegram Support](https://t.me/ApiTwitter)
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ apitwitter/__init__.py
4
+ apitwitter/client.py
5
+ apitwitter/exceptions.py
6
+ apitwitter.egg-info/PKG-INFO
7
+ apitwitter.egg-info/SOURCES.txt
8
+ apitwitter.egg-info/dependency_links.txt
9
+ apitwitter.egg-info/requires.txt
10
+ apitwitter.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ httpx>=0.24.0
@@ -0,0 +1 @@
1
+ apitwitter
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "apitwitter"
7
+ version = "1.0.0"
8
+ description = "Python SDK for the ApiTwitter REST API — Twitter/X data access"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.9"
12
+ authors = [{name = "ApiTwitter", email = "support@apitwitter.com"}]
13
+ keywords = ["twitter", "api", "sdk", "x", "tweets", "social-media"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Software Development :: Libraries",
25
+ ]
26
+ dependencies = ["httpx>=0.24.0"]
27
+
28
+ [project.urls]
29
+ Homepage = "https://apitwitter.com"
30
+ Documentation = "https://docs.apitwitter.com"
31
+ Repository = "https://github.com/apitwitter/python-sdk"
32
+
33
+ [tool.setuptools.packages.find]
34
+ include = ["apitwitter*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+