Skip to content

Twitter API Python guide: tweepy vs requests, with Reddit

A Twitter API Python guide for data jobs: tweepy on X's pay-per-use API, then requests for search, timelines, followers, cursors and Reddit.

Piyushh · October 14, 2026 · 6 min read

There are two ways to use the Twitter API in Python. The first is tweepy, the long-running Python library, pointed at X's official API with a Bearer Token from X's Developer Console and billed per request. The second is plain requests against a hosted API: one key, JSON back, and you don't need an X developer app. Most Twitter API Python tutorials cover only the first. Both work. Pick based on whether you need a first-party relationship with X or only the data.

This guide covers data jobs, meaning search results, timelines and follower lists pulled into Python for analysis. We set up tweepy first, then run the same jobs with requests, including cursor pagination, and finish by adding Reddit to the same script. If you're building outreach or DM automation in Python, XAutoDM's Twitter API Python guide covers posting and DMs.

Two Twitter API Python routes compared

tweepy + X's APIrequests + a hosted API
Account neededX developer app (console.x.com)The hosted API's key
BillingX pay-per-use, per resourceThe provider's credits
AuthBearer Token, or OAuth for user actionsOne header
Paginationtweepy.Paginator and next_tokenAn opaque cursor in each response
Posting from your own accountYes, with user-context authDepends on the provider
Reddit in the same codeNoYes, if the provider covers it

Tweepy and the official X API

What tweepy needs from X in 2026

Tweepy is still maintained. Version 4.17.0 went to PyPI on July 2, 2026, and the GitHub repo had commits the same day (both checked Oct 14, 2026). One trap: the changelog in the stable docs still ends at 4.14.0 from 2023, so go by PyPI for the current version.

The library is free, but the API behind it isn't. To call it, you sign in at console.x.com, create an app and copy its Bearer Token. Our X API key guide walks through the console. X's pricing page (checked Oct 14, 2026) lists reads per resource. Posts: Read is $0.005 per resource, and User: Read and Following/Followers: Read are $0.010 each. Pay-per-use caps you at 3 million post reads per billing cycle.

That change caught out tweepy users who had bots running on the old free tier. In a tweepy issue about posts failing with 503 errors, one user wrote, "Unfortunately don't believe free access is available any longer." Another followed up that "staff on the dev forum mentioned that it's exclusively pay-per-use now" (issue #2237). If an old tweepy script started failing this year, check the credit balance in your console before you debug the code.

A tweepy search with pagination

Tweepy's Client docs give this shape for search: a Client with your Bearer Token, search_recent_tweets, and Paginator to walk the pages.

import os
import tweepy

client = tweepy.Client(bearer_token=os.environ["X_BEARER_TOKEN"], wait_on_rate_limit=True)

paginator = tweepy.Paginator(
    client.search_recent_tweets,
    query='"crm recommendation" -is:retweet lang:en',
    tweet_fields=["created_at", "public_metrics", "author_id"],
    max_results=100,
)
for tweet in paginator.flatten(limit=300):
    m = tweet.public_metrics
    print(tweet.created_at, m["like_count"], tweet.text[:80])

The docs spell out a few details. search_recent_tweets returns posts from the last seven days, and full-archive search is a separate method, search_all_tweets. flatten(limit=300) stops after 300 posts, so at the listed $0.005 per post read the loop costs up to $1.50. Set a spending limit in the console before you put it in a cron job. Author names don't come back by default. You request expansions=["author_id"] and join the includes yourself.

The Twitter API with Python requests (a tweepy alternative)

If you only need to read public X data, a hosted API saves you the developer app, the OAuth setup and the X bill. ChatterAPI is one. It's independent and not affiliated with X Corp. Every endpoint returns the same envelope: data, a page with nextCursor and hasMore, and credits. So one helper covers every job:

import os
import requests

BASE = "https://api.chatterapi.com/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['CHATTERAPI_KEY']}"


def get(path, **params):
    r = session.get(f"{BASE}{path}", params=params, timeout=60)
    r.raise_for_status()
    return r.json()


def pages(path, max_pages=5, **params):
    """Yield rows page by page, following the opaque cursor."""
    cursor = None
    for _ in range(max_pages):
        body = get(path, **params, **({"cursor": cursor} if cursor else {}))
        yield from body["data"]
        cursor = body["page"]["nextCursor"]
        if not body["page"]["hasMore"] or not cursor:
            break

max_pages is your budget guard. Each page is billed once, so max_pages=5 on a 3-credit endpoint can't spend more than 15 credits. Failed calls cost nothing.

Search tweets with the Python Twitter API

x.search takes X's search operators in q, plus sort (latest or top), since, until and lang:

for t in pages("/x/tweets/search", max_pages=2, q="crm recommendation", sort="latest"):
    print(t["createdAt"][:10], t["author"]["handle"], t["likes"], t["text"][:80])

Below is one row of the response, trimmed. It's a real sample recorded with a test key, which is free and returns one recorded page whatever the input:

{
  "data": [
    {
      "id": "2099495333231288346",
      "text": "meta ads: how important is the “connect to your crm” recommendation? ...",
      "url": "https://x.com/HesomParhizkar/status/2099495333231288346",
      "createdAt": "2026-09-14T13:47:48.000Z",
      "likes": 2, "retweets": 0, "replies": 2, "views": 138,
      "author": { "handle": "HesomParhizkar", "followers": 584, "canDm": true }
    }
  ],
  "page": { "nextCursor": "AcWNIo1WFWYR7CN…", "hasMore": true },
  "credits": { "charged": 0, "remaining": 717 }
}

Author fields arrive inline, so you don't need an includes join. A page holds about 20 posts and costs 3 credits on a live key. For date-bounded searches and archiving an account, see how to search tweets by date. If you only need a single post's text without any key, how to view tweets without an account covers X's oEmbed endpoint. To try a query before writing code, use the free X tweet search tool.

Read a timeline

x.tweets returns an account's recent posts, newest first. Pass includeRetweets=False if you only want their own posts:

import pandas as pd

df = pd.DataFrame(pages("/x/users/levelsio/tweets", max_pages=3))
df["createdAt"] = pd.to_datetime(df["createdAt"])
print(df[["createdAt", "likes", "views", "isReply"]].head())

Each row carries likes, retweets, replies, quotes, views, bookmarks, isReply, isRetweet and quotedTweetId. The top row in the test sample is a @levelsio post from 2026-10-13 with 129 likes and 15,475 views. It costs 3 credits per page.

Export followers with cursors

Pagination matters most for follower lists. x.followers returns 20 rows a page for 3 credits:

followers = list(pages("/x/users/levelsio/followers", max_pages=1))
big = [f for f in followers if f["followers"] > 100_000]
print(len(followers), "followers fetched,", len(big), "with 100k+")
for f in big[:3]:
    print(f["handle"], f["followers"], f["canDm"])

On the test key this printed:

20 followers fetched, 10 with 100k+
dannypostma 187346 True
dvassallo 205470 False
arvidkahl 212888 True

Cursors are opaque and encrypted. Don't parse them or build your own. If a long export might get interrupted, store the last nextCursor and pass it back to resume. A 10,000-follower account is about 500 pages, or 1,500 credits, so do that math before you set max_pages. The free X follower exporter shows the same data in a browser.

If you came here from snscrape, whose Twitter scrapers started failing in June 2023 when X put content behind a login wall, our snscrape alternative page compares the options.

Add Reddit to the same script

The requests route pays off here. The same session, helper and key also read Reddit, so a cross-platform job only needs a second loop. This script collects posts asking for a CRM recommendation from both platforms into one CSV:

import csv

query = "crm recommendation"
rows = []
for t in pages("/x/tweets/search", max_pages=2, q=query, sort="latest"):
    rows.append({"platform": "x", "date": t["createdAt"], "author": t["author"]["handle"],
                 "text": t["text"].replace("\n", " "), "score": t["likes"], "url": t["url"]})
for p in pages("/reddit/search", max_pages=2, q=query, sort="new", time="month"):
    rows.append({"platform": "reddit", "date": p["createdAt"], "author": p["author"],
                 "text": p["title"], "score": p["score"], "url": p["url"]})

rows = list({r["url"]: r for r in rows}.values())  # dedupe by URL
rows.sort(key=lambda r: r["date"], reverse=True)
with open("crm_mentions.csv", "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=rows[0].keys())
    w.writeheader()
    w.writerows(rows)
print(len(rows), "rows;", sum(r["platform"] == "x" for r in rows), "from X,",
      sum(r["platform"] == "reddit" for r in rows), "from Reddit")

We ran it on a test key and it printed 45 rows; 20 from X, 25 from Reddit. A test key repeats one sample page, so the dedupe line collapsed the second pages. A live key would return four distinct pages. The cost is two X pages at 3 credits plus two Reddit pages at 2 credits, so 10 credits at most. reddit.search takes Reddit's own operators in q, plus subreddit, sort and time.

The Python Reddit API guide is this post's Reddit twin and covers that side in depth, including PRAW and threads with comments. If you'd rather make one call than run two loops, /v1/search searches both platforms for 5 credits, as shown in scraping Twitter and Reddit in one script. If an AI agent should run these queries instead of a script, the same endpoints are available as tools in the X MCP server.

When tweepy is the right choice

In several cases, Tweepy against X's own API is the better tool. Posting from your own brand account is first-party and the terms are clear. If you need endpoints a hosted API doesn't offer, or a direct contract with X for compliance, go direct. Tweepy also has years of Stack Overflow answers behind it. X now publishes its own Python SDK too, xdk (pip install xdk), if you'd rather use the generated first-party client.

The requests route fits when X is one data source among several, your work is mostly reads, and you'd rather not run an X app and pay a separate X bill. Whichever you pick, read X's terms for your use case, especially before you feed X data into model training.

Try it

ChatterAPI gives your code and your agents Reddit and X data through one key, over REST or MCP, with no developer account application. Sign up with Google for 100 free credits, no card needed. That covers about 33 tweet searches, or ten runs of the CSV script above. Plans start at $19 a month for 25,000 credits.

FAQ

How do I use the Twitter API with Python?
Either install tweepy, create an app in X's Developer Console and pass your Bearer Token to tweepy.Client, or call a hosted API with requests. Tweepy calls X's own API, which is pay-per-use. A hosted API like ChatterAPI needs only its own key and returns JSON you can loop over.
Is tweepy still maintained in 2026?
Yes. Tweepy 4.17.0 was published to PyPI on July 2, 2026, and the GitHub repository had commits that day. Note that the stable docs changelog still ends at an older release, so check PyPI for the current version.
Can I use the Twitter API in Python for free?
Not through X's API. As listed on Oct 14, 2026, X bills reads per resource, for example $0.005 per post read, with promotional credits for new paying customers. Tweepy itself is free. ChatterAPI gives 100 free credits at signup, about 33 tweet searches.
What is a good tweepy alternative?
For reading data, plain requests against a hosted API works without an X developer app. For the official route, X publishes its own Python SDK, xdk. snscrape, the old scraping library, has had its Twitter scrapers failing since June 2023.
How do I paginate Twitter API results in Python?
In tweepy, wrap a method in tweepy.Paginator and call flatten with a limit. With ChatterAPI, read page.nextCursor from each response and pass it back as the cursor parameter until page.hasMore is false.

More from the blog

Make your first call on 100 free credits.

Sign up with Google, no card. One key for Reddit and X, over REST or MCP.