Tweepy Tutorial (2026): Search Tweets with X API v2
A Tweepy tutorial for X API v2 in 2026: install 4.17.0, call search_recent_tweets with fields and expansions, page results, and see what each call costs.
Tarun Khatri
Published · Updated · 7 min read
This Tweepy tutorial shows you how to search tweets with Tweepy 4.17.0 and X's API v2, from a bearer token to paged results.
It's for Python developers who read X (Twitter) data through the official API. Every method comes from Tweepy's source, and every price from X's docs, as of October 2026.
We have no X key, so we ran each snippet against a local stub that returns X's documented JSON. The output below is real; the posts in it are X's own samples.
Your first search:
import os
import tweepy
client = tweepy.Client(bearer_token=os.environ["X_BEARER_TOKEN"])
response = client.search_recent_tweets("python lang:en -is:retweet", max_results=10)
for tweet in response.data or []:
print(tweet.id, tweet.text)
You need Python 3.9 or newer, plus an X developer app with a bearer token and credits.
- Install Tweepypip install tweepy==4.17.0
- Create a ClientPass the bearer token from the Developer Console.
- Search recent tweetssearch_recent_tweets with tweet_fields and expansions.
- Read includesMatch each author_id to includes["users"].
- Page with PaginatorSet limit so the loop stops.
- Read one accountget_user, then get_users_tweets.
Step 1: Install Tweepy 4.17.0
Tweepy 4.17.0 is the current release on PyPI, out since 2 July 2026. It needs Python 3.9 or newer.
pip install tweepy==4.17.0
export X_BEARER_TOKEN="your-bearer-token"
You copy the token from your app in the X Developer Console. Our guide on how to get an X API key walks through it.
Step 2: Create a tweepy.Client with a bearer token
tweepy.Client is Tweepy's client for X's API v2. A bearer token gives it app-only access, which is enough to read public posts.
import os
import tweepy
client = tweepy.Client(bearer_token=os.environ["X_BEARER_TOKEN"])
To post, reply or like, you need user-context auth instead, such as the four OAuth 1.0a keys: consumer_key, consumer_secret, access_token and access_token_secret.
Step 3: Search tweets with Tweepy's search_recent_tweets
search_recent_tweets calls X's recent search, which covers the last 7 days. A post comes back with only id, text and edit_history_tweet_ids unless you ask for more:
response = client.search_recent_tweets(
query="python lang:en -is:retweet",
max_results=10,
tweet_fields=["created_at", "public_metrics", "author_id"],
expansions=["author_id"],
user_fields=["username", "verified"],
)
for tweet in response.data or []:
print(tweet.id, tweet.created_at, tweet.public_metrics["like_count"])
Our stub run printed:
1234567890123456789 2024-01-15 10:30:00+00:00 42
Tweepy turns each list into the comma string X expects. This is the request it sent:
GET https://api.twitter.com/2/tweets/search/recent
query=python lang:en -is:retweet
max_results=10
tweet.fields=created_at,public_metrics,author_id
expansions=author_id
user.fields=username,verified
Note the host. Tweepy 4.17.0 still calls api.twitter.com, while X's docs use api.x.com. Both hosts answered our keyless test with a 401 on 22 October 2026.
X's docs set these limits:
max_resultsruns from 10 to 100, and the default is 10.- A query can be 512 characters, or 4,096 on Enterprise.
- Recent search allows 450 requests per 15 minutes for each app.
- Older posts need
search_all_tweets, X's full-archive search, open to pay-per-use accounts.
Step 4: Read includes to get each tweet's author
Every call returns a Response with four parts: data, includes, errors and meta. The author isn't inside the tweet. It sits in includes["users"], matched by author_id:
users = {user.id: user for user in response.includes.get("users", [])}
for tweet in response.data or []:
author = users.get(tweet.author_id)
print(f"@{author.username}: {tweet.text}")
@pythondev: Just started learning Python and loving it!
Our first stub run crashed with KeyError: 'name', because X's quickstart sample leaves out the user's name. X's data dictionary lists it as a default field, so add it if you mock X in tests.
Step 5: Page through results with tweepy.Paginator
Paginator passes X's next_token back for you and stops when meta has none. Its limit caps requests, and flatten(limit=...) caps tweets:
for tweet in tweepy.Paginator(
client.search_recent_tweets,
"python lang:en -is:retweet",
max_results=100,
limit=5, # at most 5 requests
).flatten(limit=300):
print(tweet.id, tweet.text[:50])
Our stub served two pages, so Paginator made two requests and stopped:
1234567890123456789 Just started learning Python and loving it!
1234567890123456788 Python tip: use list comprehensions for cleaner co
Always set limit. A broad query can page for a long time, and each full page of 100 posts costs 50 cents.
Step 6: Get a user and their tweets
get_user looks an account up by username or id. Then get_users_tweets reads its timeline by id:
user = client.get_user(
username="xdevelopers", user_fields=["description", "public_metrics"]
).data
print(user.id, user.name, user.public_metrics["followers_count"])
posts = client.get_users_tweets(
user.id,
max_results=5,
exclude=["retweets", "replies"],
tweet_fields=["created_at", "public_metrics"],
)
for tweet in posts.data or []:
print(tweet.created_at.date(), tweet.public_metrics["like_count"], tweet.text)
2244994945 X Developers 570842
2024-01-15 100 Hello world!
Here max_results runs from 5 to 100. X's docs say a timeline reaches back 3,200 posts, and Tweepy's docstring says exclude=replies cuts that to 800.
If your Tweepy code fails: errors and fixes
Tweepy raises a named exception for most error codes. Catch the specific ones first:
try:
response = client.search_recent_tweets("python", max_results=10)
except tweepy.TooManyRequests as error:
print("rate limited until", error.reset_time)
except tweepy.Unauthorized:
print("check X_BEARER_TOKEN")
except tweepy.HTTPException as error:
print(error.response.status_code, error.api_messages)
Unauthorized(401): the token is wrong or was regenerated.Forbidden(403): your app lacks access to that endpoint.TooManyRequests(429): wait untilreset_time, new in 4.17.0. Or passwait_on_rate_limit=Trueand Tweepy sleeps for you.- Out of credits: X says requests fail until you buy more. A status with no class of its own lands in
HTTPException; our X API 402 guide covers this case.
What does each call in this Tweepy tutorial cost?
X's API is pay-per-use. Its pricing page lists these rates, as of October 2026:
Here is what the calls above cost at full size:
| Tweepy call | X bills it as | Cost |
|---|---|---|
search_recent_tweets, 100 posts |
Posts: Read | $0.50 |
Paginator, 5 pages of 100 |
Posts: Read | $2.50 |
get_user |
User: Read | $0.010 |
get_users_tweets, 100 posts |
Posts: Read | $0.50 |
create_tweet |
Post: Create | $0.015 |
create_tweet with a link |
Post: Create | $0.200 |
A post fetched twice in one UTC day is charged once. X's page doesn't say whether authors in includes count as user reads, so check the Developer Console after your first runs.
The same search on Weir, for public reads only
If you only read public posts, a REST API can cost less per post. We run Weir, so weigh this knowing that.
import os, requests
r = requests.post(
"https://api.weirapp.io/v1/x/search",
headers={"Authorization": f"Bearer {os.environ['WEIR_API_KEY']}"},
json={"fromUsers": ["XDevelopers"], "query": "-filter:nativeretweets",
"onlyOriginal": True, "limit": 3},
timeout=60,
)
for post in r.json()["data"]:
print(post["createdAt"][:10], post["likeCount"], post["text"][:50])
We ran this search through Weir's MCP server on 22 October 2026. It returned 3 posts for 4 credits. The newest, trimmed:
{
"id": "2111172163776708859",
"text": "New guide to the 𝕏 Chat API from @santiagomed: ...",
"createdAt": "2026-10-16T19:07:22.000Z",
"author": { "username": "XDevelopers", "followersCount": 697852 },
"likeCount": 120,
"viewCount": 15052
}
The author sits inside each post, so there's no includes step. Weir's query passes X's web search operators through, and that dialect drops reposts with -filter:nativeretweets.
A Weir search costs 1 credit per call plus 1 per post, at about 20 posts a page.
That's 500 Weir calls, or 10,500 credits, and 5,000 credits a month are free. Our guide to getting tweets with Python has the full script, and the Twitter search API guide compares endpoints.
When not to use Weir
- You post, reply, like or follow. Weir only reads. Tweepy with user-context keys is the official way to write.
- You need a contract with X itself. Use the official API.
- You read your own account's data. X's Owned Reads cost $0.001 per resource.
- Price per post is all that matters. TwitterAPI.io lists $0.15 per 1,000 tweets, and it can post.
FAQ
Does Tweepy still work in 2026?
Yes. Tweepy 4.17.0 shipped on 2 July 2026 and works with X's API v2 through tweepy.Client. X's own docs list it among community libraries with v2 support. You still need a developer app, a bearer token and credits.
How do I search tweets with Tweepy?
Create tweepy.Client(bearer_token=...), then call client.search_recent_tweets(query, max_results=100). Add tweet_fields for dates and counts, and expansions=["author_id"] for authors. Wrap the call in tweepy.Paginator with a limit to read more than one page.
Should I use tweepy.API or tweepy.Client?
Use tweepy.Client for new code. Tweepy's docs file API under X's older v1.1 endpoints and Client under v2. The top "tweepy tutorial" results we found on 22 October 2026 still showed API with Python 2 code from Tweepy 2 and 3.
How many tweets can search_recent_tweets return?
Up to 100 per request, from the last 7 days. Paginator fetches more pages until it hits your limit or the results run out, and X bills every page.
Sources
All checked 22 October 2026:
- Tweepy 4.17.0 on PyPI: https://pypi.org/project/tweepy/
- Tweepy release notes: https://github.com/tweepy/tweepy/releases/tag/v4.17.0
- Tweepy source at v4.17.0: https://github.com/tweepy/tweepy/tree/v4.17.0/tweepy
- Tweepy hosted docs: https://docs.tweepy.org/en/stable/
- X pricing and billing FAQ: https://docs.x.com/x-api/getting-started/pricing and https://docs.x.com/x-api/fundamentals/post-cap
- X search: https://docs.x.com/x-api/posts/search/introduction, https://docs.x.com/x-api/posts/search/quickstart/recent-search and https://docs.x.com/x-api/posts/search-recent-posts
- X rate limits: https://docs.x.com/x-api/fundamentals/rate-limits
- X fields, expansions, pagination and data dictionary: https://docs.x.com/x-api/fundamentals/fields, https://docs.x.com/x-api/fundamentals/expansions, https://docs.x.com/x-api/fundamentals/pagination and https://docs.x.com/x-api/fundamentals/data-dictionary
- X timelines: https://docs.x.com/x-api/posts/timelines/introduction and https://docs.x.com/x-api/users/get-posts
- X errors and libraries: https://docs.x.com/x-api/fundamentals/response-codes-and-errors and https://docs.x.com/tools-and-libraries
- TwitterAPI.io: https://twitterapi.io/pricing and https://docs.twitterapi.io/api-reference/endpoint/create_tweet_v2
- Weir billing and pagination: https://docs.weirapp.io/billing and https://docs.weirapp.io/pagination
- Our Tweepy stub run and Weir search of @XDevelopers, 22 October 2026