Get Twitter (X) Data in Python (2026 Tutorial)

To get Twitter/X data in Python, install the requests library, get a TwitterAPI.io API key (Google sign-in, no OAuth), and call the REST endpoints with your key in an X-API-Key header — for example GET /twitter/tweet/advanced_search to search tweets or /twitter/user/last_tweets for an account's timeline. This tutorial walks through the whole pipeline with runnable code: search, user data, pagination, rate handling, and saving to pandas or CSV.
If you've used Tweepy with the official API before, this will feel simpler — there's no OAuth dance, no app registration, and no developer-account approval. You authenticate every request with a single API key in a header, and the endpoints return plain JSON that maps directly to Python dicts. That makes it ideal for data analysis, monitoring scripts, and feeding ML pipelines.
We'll build up from a single search call to a reusable collector with pagination and error handling, then load the results into pandas. Every snippet runs as-is once you drop in your API key (the $1 trial credit, no card, covers ~6,000 calls — plenty for this tutorial).
Step 1 — Set up: install requests and get an API key
You only need one library for the HTTP calls: pip install requests. For analysis, add pandas: pip install pandas. No Twitter-specific SDK is required — the API is plain REST.
Get your API key by signing in with Google at twitterapi.io and copying it from the dashboard. New accounts include a $1 trial credit (~6,000 calls) with no credit card, which is more than enough to follow this tutorial.
Keep the key out of your code — read it from an environment variable (e.g. os.environ["TWITTERAPI_KEY"]) so you never commit it. Every request authenticates by sending the key in an X-API-Key header.
Step 2 — Search tweets with advanced_search
The workhorse endpoint is /twitter/tweet/advanced_search. It accepts a query (the same operators you'd type into X search — keywords, from:user, #hashtag, lang:en, date ranges) and a queryType of Latest or Top.
A single GET returns a page of matching tweets plus a next_cursor for the following page. Each tweet object includes the text, author, engagement counts, and timestamps — everything you need for analysis.
Start with one call to confirm your key works, then wrap it in a loop (Step 3) to collect more than one page. The code snippet at the end of this tutorial shows the complete, paginated version.
Step 3 — Paginate to collect more than one page
Each response includes next_cursor. To collect a larger result set, pass that cursor back as the cursor parameter on the next request, and repeat until the cursor is empty (or you hit a self-imposed page limit).
Always cap your loop with a max_pages guard so a broad query doesn't run away and burn credits. For most analyses, a few hundred to a few thousand tweets is plenty — and you control exactly how many calls you make.
Unlike the official API, there's no fixed 15-minute rate window to schedule around. There's a soft server-side protection (keep batch jobs around 30-50 requests/second), but you won't hit hard quota walls mid-collection.
Step 4 — Get user data and timelines
Beyond search, two endpoints cover most user-data needs. /twitter/user/info returns a profile (bio, follower/following counts, verified status, creation date) for a username — useful for enriching or scoring accounts. /twitter/user/last_tweets returns a specific account's recent posts, paginated the same way as search.
For follower analysis, /twitter/user/followers returns an account's followers (paginated). Combine these: search for tweets on a topic, pull each unique author's profile with /twitter/user/info, and you have an enriched dataset for influencer or lead analysis.
All of these take the same X-API-Key header and return JSON you can drop straight into a Python dict or a pandas row.
Step 5 — Load into pandas and save to CSV
Once you've collected a list of tweet dicts, pandas.json_normalize(tweets) flattens nested fields into a DataFrame in one line. From there you can filter, group, compute engagement rates, or plot.
Save with df.to_csv("tweets.csv", index=False) for a portable dataset, or df.to_parquet(...) for a compact columnar file you can reload fast. For ongoing collection, append to a database instead.
This is where the pay-per-call model shines for analysis: you pull exactly the data your notebook needs, when it needs it, without provisioning a monthly quota you may not use.
Error handling and best practices
Check status codes: call r.raise_for_status() and wrap collection in try/except so one bad page doesn't crash a long run. A 401 means the API key is wrong; a 429 (rare here) means slow down.
Back off on transient errors: add a short exponential backoff (e.g. retry after 1s, 2s, 4s) so a momentary network blip doesn't lose your run.
Cap pages and log progress: print how many tweets you've collected per page so you can see runaway queries early and stop them.
Keep the key in an env var: never hard-code or commit the API key. Rotate it from the dashboard if it's ever exposed.
Throttle batch jobs: keep concurrent collection around 30-50 requests/second to stay well under the soft server-side limit.
# Twitter (X) data in Python — full collector with pagination + pandas
# pip install requests pandas
import os, time
import requests
import pandas as pd
API_KEY = os.environ["TWITTERAPI_KEY"] # never hard-code the key
BASE = "https://api.twitterapi.io"
HEADERS = {"X-API-Key": API_KEY}
def _get(url, params, retries=3):
"""GET with simple exponential backoff."""
for attempt in range(retries):
try:
r = requests.get(url, headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
return r.json()
except requests.RequestException:
if attempt == retries - 1:
raise
time.sleep(2 ** attempt)
def search_tweets(query, max_pages=5):
"""Collect tweets matching an advanced-search query, paginated."""
cursor, rows = None, []
for page in range(max_pages):
params = {"query": query, "queryType": "Latest"}
if cursor:
params["cursor"] = cursor
data = _get(f"{BASE}/twitter/tweet/advanced_search", params)
tweets = data.get("tweets", [])
rows.extend(tweets)
print(f"page {page + 1}: +{len(tweets)} (total {len(rows)})")
cursor = data.get("next_cursor")
if not cursor:
break
return rows
def user_profile(username):
"""Fetch a profile for enrichment/scoring."""
return _get(f"{BASE}/twitter/user/info", {"userName": username})
if __name__ == "__main__":
tweets = search_tweets('"machine learning" lang:en', max_pages=3)
df = pd.json_normalize(tweets)
df.to_csv("tweets.csv", index=False)
print(f"saved {len(df)} tweets -> tweets.csv")
print(user_profile("openai"))Questions readers ask
What's the easiest way to get Twitter data in Python?
Use the requests library against a third-party REST API like TwitterAPI.io: get an API key (no OAuth), then GET /twitter/tweet/advanced_search or /twitter/user/last_tweets with the key in an X-API-Key header. The JSON maps directly to Python dicts and into pandas. It's simpler than the official API + Tweepy because there's no OAuth or app-registration step.
Do I still need Tweepy in 2026?
Not for this approach. Tweepy wraps the official X API and its OAuth flow; with a third-party REST API you just use requests and a single API-key header. Tweepy is still fine if you're committed to the official API, but for straightforward data collection plain requests against a pay-per-call API is simpler and avoids the OAuth and approval overhead.
How do I paginate through tweets in Python?
Each response from advanced_search (and user/last_tweets) includes a next_cursor field. Pass it back as the cursor parameter on the next request and repeat until it's empty or you reach a max_pages guard. Always cap the loop so a broad query doesn't run away and consume credits — the code sample above shows the pattern.
How do I load tweets into pandas?
Collect the tweet objects into a list, then call pandas.json_normalize(tweets) to flatten nested fields into a DataFrame in one line. From there, df.to_csv('tweets.csv', index=False) saves a portable dataset, or use to_parquet for a compact columnar file. You can then filter, group, and compute engagement metrics with normal pandas.
How do I handle rate limits in Python?
This API has no fixed 15-minute window like the official API — there's a soft server-side protection, so keep batch jobs around 30-50 requests/second. Add a short exponential backoff (retry after 1s, 2s, 4s) on transient errors, and check r.raise_for_status() so a 401 (bad key) or rare 429 (slow down) surfaces clearly instead of corrupting your dataset.
Is it free to get Twitter data in Python?
To start, yes: TwitterAPI.io's $1 trial credit (~6,000 calls, no card) covers a full tutorial and prototype. After that it's pay-per-call (~$0.15 per 1,000 tweets, no monthly floor). The official API's free access can't run a tweet search at all, so for Python data work the third-party trial is the practical free starting point.
Can I get historical tweets in Python?
Yes — advanced_search supports date-range operators in the query (since:/until: or explicit since_time/until_time params), so you can page back through historical public tweets in Python. For very large historical pulls, paginate in batches and store incrementally so a long run is resumable.
Continue
- Requests — official Python HTTP library documentation
- pandas — official documentation
- X API — official documentation (search operators reference)
- Tweepy vs TwitterAPI.io — Python migration guide →
- Scrape Twitter (X) without the official API →
- API docs — all endpoints + params →
- Pay-per-call pricing + $1 trial →
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API key