Twitter Sentiment Analysis in Python on Live Posts (2026)
A working script for Twitter sentiment analysis in Python: pull live X posts, score each with VADER, and save daily averages and label shares to CSV.
Tarun Khatri
Published · Updated · 7 min read
Twitter sentiment analysis in Python takes three steps: pull recent posts by API, score each one with VADER, and average the scores by day.
This tutorial is for you if you want live posts from X (Twitter), not an old fixed dataset. You get a script that writes a daily CSV, our results on real posts, and where VADER goes wrong. We run Weir, the API your script calls.
Paste this to score your first posts:
import os, requests
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
r = requests.post("https://api.weirapp.io/v1/x/search", timeout=60,
headers={"Authorization": f"Bearer {os.environ['WEIR_API_KEY']}"},
json={"query": "Spotify", "language": "en", "limit": 20})
analyzer = SentimentIntensityAnalyzer()
for post in r.json()["data"]:
print(analyzer.polarity_scores(post["text"])["compound"], post["url"])
- Get a keySign up free and set WEIR_API_KEY.
- Download postsSearch your topic, one UTC day at a time.
- Score each postVADER gives a compound score from -1 to +1.
- Save the daily CSVYour average score and label shares per day.
- Check the labelsRead a sample before you trust the numbers.
Step 1: Get a key and install two packages
Sign up with no card and create a key. Then install the two packages you need:
pip install requests vaderSentiment
export WEIR_API_KEY="weir_live_..."
VADER is a free, rule-based scorer under the MIT license, and its latest release dates from May 2020. It needs no model download and no GPU, so you can run it on any laptop.
Step 2: Download Twitter data for sentiment analysis
You download posts with Weir's search, POST /v1/x/search. Send your topic as query and keep language: "en", because VADER scores English words.
Save this as sentiment_daily.py. Its first part fetches the posts:
import csv, html, os, requests
from datetime import date, timedelta
API = "https://api.weirapp.io/v1/x/search"
HEADERS = {"Authorization": f"Bearer {os.environ['WEIR_API_KEY']}"}
def fetch(body, max_pages=5):
for _ in range(max_pages): # a page costs 1 credit + 1 per post
r = requests.post(API, json=body, headers=HEADERS, timeout=60)
r.raise_for_status() # a 402 means out of credits: stop
page = r.json()
yield from page["data"]
if not page["meta"]["nextCursor"]:
return
body = {**body, "cursor": page["meta"]["nextCursor"]}
Three rules keep your loop and your bill safe:
- Stop only on a
nullcursor, as Weir's pagination docs say. The last call is often an empty page. - Cap the pages. Each page costs 1 credit plus 1 per post, so
max_pagescaps your bill. - Let a
402stop the script. It means you're out of credits, and retrying won't help.
Step 3: Score each post with VADER
VADER sums a score for each word, adjusts it with rules, and scales the total to between -1 and +1. Its README calls that total the compound score and gives the usual cutoffs:
The second part of your script labels each score and pulls one UTC day at a time:
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
analyzer = SentimentIntensityAnalyzer()
def label(compound): # VADER's usual cutoffs
if compound >= 0.05:
return "positive"
return "negative" if compound <= -0.05 else "neutral"
def day_scores(topic, day):
body = {"query": topic, "language": "en", "limit": 100,
"since": f"{day}T00:00:00Z",
"until": f"{day + timedelta(days=1)}T00:00:00Z"}
return [analyzer.polarity_scores(html.unescape(post["text"]))["compound"]
for post in fetch(body)]
Two details in this part matter:
- Each day gets its own window, so a busy day can't crowd out a quiet one.
html.unescaperestores&and>. Posts arrive with&and>codes, as ours did.
Step 4: Save a daily average and label shares to CSV
The last part writes one row per day to your CSV:
TOPIC, START, DAYS = "Spotify", date(2026, 10, 20), 2
LABELS = ("positive", "neutral", "negative")
with open("sentiment_daily.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["day", "posts", "avg_compound", *LABELS])
for n in range(DAYS):
day = START + timedelta(days=n)
scores = day_scores(TOPIC, day)
if not scores:
continue
labels = [label(s) for s in scores]
shares = [round(labels.count(k) / len(labels), 2) for k in LABELS]
writer.writerow([day, len(scores), round(sum(scores) / len(scores), 3), *shares])
Set TOPIC, START and DAYS for your own topic, then run python sentiment_daily.py.
What our Twitter sentiment analysis in Python found
We tested the script on 22 October 2026, with Spotify as the topic. Each daily search returned nine posts. Together, the searches cost 20 credits.
Here is the CSV the script wrote from them:
day,posts,avg_compound,positive,neutral,negative
2026-10-20,9,0.07,0.44,0.33,0.22
2026-10-21,9,0.102,0.33,0.56,0.11
Both days lean a little positive. Across all 18 posts, the labels split like this:
Don't read Spotify's mood from this. It's a test of the script:
- Nine posts a day is too few. Each day's posts came from its last three minutes, UTC.
- One "English" post was Portuguese. X's language tag is its own guess.
- We'd change a third of the labels after reading every post.
To spread your sample across a day, set since and until an hour apart and use max_pages=1. To collect posts as they arrive, a brand mention monitor fits better.
Where VADER gets Twitter sentiment wrong
The labels we'd change followed a pattern you'll meet too. An upbeat podcast promo scored -0.69. Slang praise scored 0, and a show with a negative word in its name came out negative.
You can see the same pattern in test lines we wrote ourselves:
| Our test line | Compound | VADER | A person |
|---|---|---|---|
| Great, another outage | +0.62 | positive | negative |
| Love waiting on hold | +0.64 | positive | negative |
| sick update, no cap | -0.67 | negative | positive |
| the new update slaps | 0.00 | neutral | positive |
| Me encanta esta app | 0.00 | neutral | positive |
| new update 😡 | 0.00 | neutral | negative |
| not bad at all | +0.43 | positive | positive |
Sarcasm and slang flip the label, and Spanish scores as neutral. Negation works, and some emoji count, like 😍.
Which Twitter API for sentiment analysis?
Your scorer is free; your data isn't. Weir returns posts, not sentiment scores, so any X sentiment analysis API setup pairs a data source with a scorer like VADER.
Here is what 10,000 posts cost you:
- X's own API lists $0.005 per post read on its pricing page.
- TwitterAPI.io lists $0.15 per 1,000 tweets, less than Weir's small packs.
- Weir charges 1 credit per post plus 1 per page.
By default, the script reads up to five pages a day. That's about 105 credits a day, and Weir's free credits cover a month of it. Price bigger jobs with the X API cost calculator.
If your sentiment script doesn't work
- The first call returns
400. Writesinceanduntilwith aZ, not the+00:00thatisoformat()adds. - Page two returns
400. Send the cursor with the same body that made it. - A
402stops the run. You're out of credits until a pack or next month's free grant. - Too many requests (
429). Wait forRetry-After, as our guide to getting tweets with Python shows. - Your CSV skips a day. No English posts matched it, so widen the query.
When not to use Weir
- Price per post is all that matters. TwitterAPI.io lists $0.15 per 1,000 tweets.
- Your company needs a contract with X. X's own API charges $0.005 per post read.
- Access must never change. Weir isn't licensed by X, so it can.
- A dashboard suits you better than a script. Weir returns data, and you build the charts.
Weir reads public posts only and can't post. Platform terms still apply to how you use the data, so check X's terms for your project. This isn't legal advice.
FAQ
Can VADER detect sarcasm in tweets?
No, because VADER scores words, not intent. Our sarcastic test line, "Great, another outage", scored +0.62, which VADER labels positive. A transformer trained on tweets reads more context, but you should still read a sample by hand.
Can I do Twitter sentiment analysis in Python for free?
Yes, at small volumes: VADER is free under the MIT license, and Weir's free tier covers about 4,760 posts a month with no card. X's own API charges $0.005 per post read, as of October 2026.
Is VADER or a transformer better for X sentiment analysis in Python?
Start with VADER: it's free, quick to set up and runs without a GPU. Move to a transformer, such as CardiffNLP's Twitter RoBERTa model, when sarcasm and slang errors cost you. Test it on your own sample first, since it needs more compute.
How do I analyze tweets that aren't in English?
VADER scores English words, and its README says it doesn't translate, so our Spanish test line scored 0. Keep language: "en" in your search, or try CardiffNLP's XLM-T sentiment model. Its model card says it was tuned on eight languages.
Sources
All checked on 22 October 2026:
- VADER README, with Hutto and Gilbert's ICWSM 2014 paper: https://github.com/cjhutto/vaderSentiment
- vaderSentiment on PyPI (3.3.2, May 2020, MIT license): https://pypi.org/project/vaderSentiment/
- CardiffNLP model cards: https://huggingface.co/cardiffnlp/twitter-roberta-base-sentiment-latest and https://huggingface.co/cardiffnlp/twitter-xlm-roberta-base-sentiment
- X API pricing: https://docs.x.com/x-api/getting-started/pricing
- TwitterAPI.io pricing: https://twitterapi.io/pricing
- Weir searchX reference: https://docs.weirapp.io/reference/x/searchX
- Weir pagination and billing: https://docs.weirapp.io/pagination and https://docs.weirapp.io/billing
- Our two Weir searches for "Spotify", 22 October 2026: 20 credits