Use the Xquik Python SDK for Twitter search, timelines, profiles & followers. Manage media, webhooks & X automation through documented Xquik REST routes.
Python SDK Guide | API Map | REST API | Webhooks | MCP Guide
Stainless generates this SDK.
| Task | REST Route | Usage |
|---|---|---|
| Search tweets without the X API | GET /x/tweets/search |
Use keyword or advanced operator queries. |
| Read an X profile timeline | GET /x/users/{id}/tweets |
Paginate bounded results. |
| Scrape Twitter followers | GET /x/users/{id}/followers |
Use an extraction for complete datasets. |
| Scrape following accounts | GET /x/users/{id}/following |
Use an extraction for complete datasets. |
| Read a home timeline | GET /x/timeline |
Approve this private read. |
| Export large X datasets | POST /extractions |
Poll status, then download results. |
| Run giveaway draws | POST /draws |
Pick winners from post replies. |
| Download or upload media | /x/media/* |
Use typed file helpers. |
| Monitor an account | POST /monitors |
Deliver events through HMAC webhooks. |
| Post or reply | POST /x/tweets |
Confirm the account and payload. |
Use the typed REST SDK in application code. Add https://xquik.com/mcp to MCP clients.
Follow the MCP guide for current authentication support.
- Package: PyPI
x_twitter_scraper - Source: Xquik-dev/x-twitter-scraper-python
- License: Apache-2.0
- Citation metadata: CITATION.cff
- Security policy: SECURITY.md
pip install x_twitter_scraperSee api.md for the complete API.
import os
from x_twitter_scraper import XTwitterScraper
client = XTwitterScraper(
api_key=os.environ.get("X_TWITTER_SCRAPER_API_KEY"), # Optional; the client reads this variable.
)
response = client.x.tweets.search(
q="from:elonmusk",
limit=10,
)Pass api_key directly or load X_TWITTER_SCRAPER_API_KEY with
python-dotenv. Keep credentials out of source control.
Import AsyncXTwitterScraper and await each API call:
import os
import asyncio
from x_twitter_scraper import AsyncXTwitterScraper
client = AsyncXTwitterScraper(
api_key=os.environ.get("X_TWITTER_SCRAPER_API_KEY"), # Optional; the client reads this variable.
)
async def main() -> None:
response = await client.x.tweets.search(
q="from:elonmusk",
limit=10,
)
asyncio.run(main())Both clients expose the same resources and methods.
The async client uses httpx. Install aiohttp for an alternative backend:
pip install x_twitter_scraper[aiohttp]Select it with http_client=DefaultAioHttpClient():
import os
import asyncio
from x_twitter_scraper import DefaultAioHttpClient
from x_twitter_scraper import AsyncXTwitterScraper
async def main() -> None:
async with AsyncXTwitterScraper(
api_key=os.environ.get("X_TWITTER_SCRAPER_API_KEY"), # Optional; the client reads this variable.
http_client=DefaultAioHttpClient(),
) as client:
response = await client.x.tweets.search(
q="from:elonmusk",
limit=10,
)
asyncio.run(main())Nested request parameters use TypedDicts. Responses use Pydantic models with these helpers:
- Serialize to JSON with
model.to_json(). - Convert to a dictionary with
model.to_dict().
Set python.analysis.typeCheckingMode to basic in VS Code to catch type errors.
Pass uploads as bytes, a PathLike, or (filename, contents, media_type).
from pathlib import Path
from x_twitter_scraper import XTwitterScraper
client = XTwitterScraper()
client.x.media.upload(
account="@elonmusk",
file=Path("/path/to/file"),
)The async client uses the same interface and reads PathLike content asynchronously.
Connection failures raise an x_twitter_scraper.APIConnectionError subclass.
Non-2xx responses raise an APIStatusError subclass with status_code and response.
Every SDK error inherits from x_twitter_scraper.APIError.
import x_twitter_scraper
from x_twitter_scraper import XTwitterScraper
client = XTwitterScraper()
try:
client.x.tweets.search(
q="from:elonmusk",
limit=10,
)
except x_twitter_scraper.APIConnectionError as e:
print("Could not reach the server. Check the connection.")
print(e.__cause__) # Underlying httpx exception.
except x_twitter_scraper.RateLimitError as e:
print("Rate limited. Retry later.")
except x_twitter_scraper.APIStatusError as e:
print("Server returned a non-2xx status.")
print(e.status_code)
print(e.response)The SDK uses these error classes:
| Status Code | Error Type |
|---|---|
| 400 | BadRequestError |
| 401 | AuthenticationError |
| 403 | PermissionDeniedError |
| 404 | NotFoundError |
| 422 | UnprocessableEntityError |
| 429 | RateLimitError |
| >=500 | InternalServerError |
| N/A | APIConnectionError |
The SDK retries connection errors and HTTP 408, 409, 429, and 5xx responses. It uses exponential backoff and attempts 2 retries by default.
Set max_retries to change or disable retries:
from x_twitter_scraper import XTwitterScraper
# Set the client default:
client = XTwitterScraper(
max_retries=0,
)
# Override one request:
client.with_options(max_retries=5).x.tweets.search(
q="from:elonmusk",
limit=10,
)Requests time out after 1 minute.
Set a float or httpx.Timeout through timeout:
from x_twitter_scraper import XTwitterScraper
# Set the client default:
client = XTwitterScraper(
# 20 seconds; default: 1 minute.
timeout=20.0,
)
# Set granular limits:
client = XTwitterScraper(
timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
)
# Override per-request:
client.with_options(timeout=5.0).x.tweets.search(
q="from:elonmusk",
limit=10,
)Timeouts raise APITimeoutError.
Timed-out requests follow the default retry policy.
The SDK uses Python's logging module.
Set X_TWITTER_SCRAPER_LOG to info to enable logs.
$ export X_TWITTER_SCRAPER_LOG=infoUse debug for request and response details.
Both missing and explicit null response fields map to None.
Check .model_fields_set to distinguish them:
if response.my_field is None:
if "my_field" not in response.model_fields_set:
print('The response omitted "my_field".')
else:
print('The response set "my_field" to null.')Prefix a method with .with_raw_response. to access the raw response:
from x_twitter_scraper import XTwitterScraper
client = XTwitterScraper()
response = client.x.tweets.with_raw_response.search(
q="from:elonmusk",
limit=10,
)
print(response.headers.get("X-My-Header"))
tweet = response.parse() # Parse the regular x.tweets.search() result.
print(tweet.has_next_page)Sync methods return APIResponse.
Async methods return AsyncAPIResponse with awaitable content readers.
The raw-response interface reads the complete body immediately.
Use .with_streaming_response and a context manager to read it on demand.
Call .read(), .text(), .json(), an iterator, or .parse().
The async client provides async versions of these methods.
with client.x.tweets.with_streaming_response.search(
q="from:elonmusk",
limit=10,
) as response:
print(response.headers.get("X-My-Header"))
for line in response.iter_lines():
print(line)The context manager always closes the response.
The SDK types every documented endpoint, parameter, and response property. Use its lower-level methods for undocumented API features.
Use client.get, client.post, or another HTTP method for undocumented endpoints.
Client options, including retries, apply to these requests.
import httpx
response = client.post(
"/foo",
cast_to=httpx.Response,
body={"my_param": True},
)
print(response.headers.get("x-foo"))Pass extra values through extra_query, extra_body, or extra_headers.
Read an extra field through response.unknown_prop.
Use response.model_extra to get every extra field as a dictionary.
Replace the httpx client to configure:
- Support for proxies
- Custom transports
- Additional advanced functionality
import httpx
from x_twitter_scraper import XTwitterScraper, DefaultHttpxClient
client = XTwitterScraper(
# Or use the `X_TWITTER_SCRAPER_BASE_URL` env var
base_url="http://my.test.server.example.com:8083",
http_client=DefaultHttpxClient(
proxy="http://my.test.proxy.example.com",
transport=httpx.HTTPTransport(local_address="0.0.0.0"),
),
)Use with_options() to replace it for one request:
client.with_options(http_client=DefaultHttpxClient(...))Garbage collection closes the client's HTTP connections.
Call .close() or use a context manager to close them earlier.
from x_twitter_scraper import XTwitterScraper
with XTwitterScraper() as client:
# Make requests here.
...
# The HTTP client is closed.This package follows SemVer with these exceptions:
- Static type changes that preserve runtime behavior.
- Changes to undocumented internals that remain technically public.
- Changes unlikely to affect normal use.
Open an issue with questions, bugs, or suggestions.
If new features are missing, Python may still load an older package. Check the runtime version:
import x_twitter_scraper
print(x_twitter_scraper.__version__)Python 3.10 or higher.
See the contributing documentation.
Xquik is an independent third-party service. Not affiliated with X Corp. "Twitter" and "X" are trademarks of X Corp.