Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ title: Xquik Python SDK for Twitter Search & X Automation
type: software
authors:
- name: Xquik
version: 0.11.0
date-released: 2026-08-12
version: 0.11.1
date-released: 2026-08-20
url: https://github.com/Xquik-dev/x-twitter-scraper-python
repository-code: https://github.com/Xquik-dev/x-twitter-scraper-python
license: Apache-2.0
Expand Down
24 changes: 9 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ It provides a Twitter API alternative through documented Xquik REST routes.

[Stainless](https://www.stainless.com/) generates this SDK.

## Choose the Python SDK

Choose this client for scripts, notebooks, workers, and Python services.
Use sync or async resources with typed Pydantic response models.

## Common Twitter & X Tasks

Map each task to its REST route.
Expand Down Expand Up @@ -125,9 +120,7 @@ from x_twitter_scraper import AsyncXTwitterScraper

async def main() -> None:
async with AsyncXTwitterScraper(
api_key=os.environ.get(
"X_TWITTER_SCRAPER_API_KEY"
), # This is the default and can be omitted
api_key=os.environ.get("X_TWITTER_SCRAPER_API_KEY"), # This is the default and can be omitted
http_client=DefaultAioHttpClient(),
) as client:
response = await client.x.tweets.search(
Expand Down Expand Up @@ -282,10 +275,10 @@ Check `.model_fields_set` to distinguish them:

```py
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.')
if "my_field" not in response.model_fields_set:
print('The response omitted "my_field".')
else:
print('The response set "my_field" to null.')
```

### Accessing Raw Response Data
Expand All @@ -300,7 +293,7 @@ response = client.x.tweets.with_raw_response.search(
q="from:elonmusk",
limit=10,
)
print(response.headers.get('X-My-Header'))
print(response.headers.get("X-My-Header"))

tweet = response.parse() # Parse the regular x.tweets.search() result.
print(tweet.has_next_page)
Expand Down Expand Up @@ -397,8 +390,8 @@ Call `.close()` or use a context manager to close them earlier.
from x_twitter_scraper import XTwitterScraper

with XTwitterScraper() as client:
# Make requests here.
...
# Make requests here.
...

# The HTTP client is closed.
```
Expand All @@ -420,6 +413,7 @@ Check the runtime version:

```py
import x_twitter_scraper

print(x_twitter_scraper.__version__)
```

Expand Down
20 changes: 6 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ conflicts = [
]

[dependency-groups]
# version pins are in uv.lock
# Version pins are in uv.lock.
dev = [
"coverage==7.15.4",
"pyright==1.1.411",
Expand Down Expand Up @@ -93,7 +93,7 @@ include = [
packages = ["src/x_twitter_scraper"]

[tool.hatch.build.targets.sdist]
# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc)
# Package non-hidden source, tests, docs, and metadata.
include = [
"/*.toml",
"/*.json",
Expand All @@ -114,7 +114,7 @@ content-type = "text/markdown"
path = "README.md"

[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
# replace relative links with absolute links
# Convert relative links to absolute links.
pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
replacement = '[\1](https://github.com/Xquik-dev/x-twitter-scraper-python/tree/main/\g<2>)'

Expand All @@ -129,9 +129,7 @@ filterwarnings = [
]

[tool.pyright]
# this enables practically every flag given by pyright.
# there are a couple of flags that are still disabled by
# default in strict mode as they are experimental and niche.
# Enable Pyright's stable strict checks.
typeCheckingMode = "strict"
pythonVersion = "3.10"

Expand All @@ -152,12 +150,7 @@ reportPrivateUsage = false
pretty = true
show_error_codes = true

# Exclude _files.py because mypy isn't smart enough to apply
# the correct type narrowing and as this is an internal module
# it's fine to just use Pyright.
#
# We also exclude our `tests` as mypy doesn't always infer
# types correctly and Pyright will still catch any type errors.
# Pyright checks `_files.py` and tests where mypy cannot infer types accurately.
exclude = ['src/x_twitter_scraper/_files.py', '_dev/.*.py', 'tests/.*']

strict_equality = true
Expand All @@ -169,8 +162,7 @@ warn_return_any = true
warn_unreachable = true
warn_unused_configs = true

# Turn these options off as it could cause conflicts
# with the Pyright options.
# Avoid conflicts with Pyright.
warn_unused_ignores = false
warn_redundant_casts = false

Expand Down