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.
- apitwitter-1.0.0/PKG-INFO +187 -0
- apitwitter-1.0.0/README.md +163 -0
- apitwitter-1.0.0/apitwitter/__init__.py +6 -0
- apitwitter-1.0.0/apitwitter/client.py +517 -0
- apitwitter-1.0.0/apitwitter/exceptions.py +34 -0
- apitwitter-1.0.0/apitwitter.egg-info/PKG-INFO +187 -0
- apitwitter-1.0.0/apitwitter.egg-info/SOURCES.txt +10 -0
- apitwitter-1.0.0/apitwitter.egg-info/dependency_links.txt +1 -0
- apitwitter-1.0.0/apitwitter.egg-info/requires.txt +1 -0
- apitwitter-1.0.0/apitwitter.egg-info/top_level.txt +1 -0
- apitwitter-1.0.0/pyproject.toml +34 -0
- apitwitter-1.0.0/setup.cfg +4 -0
|
@@ -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,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
|
+
|
|
@@ -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*"]
|