WEIR

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.


Published · Updated · 7 min read

Cover image: Live X posts, scored by VADER

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"])
  1. Get a keySign up free and set WEIR_API_KEY.
  2. Download postsSearch your topic, one UTC day at a time.
  3. Score each postVADER gives a compound score from -1 to +1.
  4. Save the daily CSVYour average score and label shares per day.
  5. 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 null cursor, 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_pages caps your bill.
  • Let a 402 stop 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:

≥ 0.05compound score: positive
-0.05 to 0.05neutral
≤ -0.05negative

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.unescape restores & and >. Posts arrive with &amp; and &gt; 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:

VADER labels on 18 posts about Spotify, 20-21 Oct 2026
Neutral44%
Positive39%
Negative17%

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:

Pulling 10,000 posts, prices as of October 2026
X API$50
Weir, $5 pack$2.10
TwitterAPI.io$1.50
Weir, $1,000 pack$1.05
  • 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. Write since and until with a Z, not the +00:00 that isoformat() adds.
  • Page two returns 400. Send the cursor with the same body that made it.
  • A 402 stops the run. You're out of credits until a pack or next month's free grant.
  • Too many requests (429). Wait for Retry-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: