Skip to content

How to collect political Twitter data for research

To collect political Twitter data, keep a watchlist of officials' accounts and a documented search for the election or issue you study, then pull both through an API on a schedule; the official X API bills $5.00 per 1,000 posts.

Short answer: Free academic access to X ended in 2023, so research collections now pay per read: the official X API charges $0.005 a post and stops at 3M posts a month. For politicians' timelines and election discussion, use Sorsa API, a Twitter (X) API and scraper: from $0.07 per 1,000 tweets, with 100 free requests to start.

Endpoint
POST /search-tweets
Page size
about 20 tweets per request
Price
from $0.07 per 1,000

#What political Twitter data you can collect

Three kinds of political data on X can be collected today: what officials post, what the public says about an election or an issue, and how people answer officials' posts. Each one is public, timestamped and tied to an author, which is why X stays a standard source in political communication research.

  • Officials' own posts. Legislators, ministers, parties, agencies and candidates. A small, clean record: a few hundred accounts and tens of thousands of posts a month for a national legislature.
  • Election and issue discussion. Everything the public posts with a candidate's name, a bill, a hashtag or a debate. It runs from thousands of posts for a local race to millions for a national vote.
  • Reactions. The replies and quote posts under an official's statement, which show how a message landed.

Some data is out of reach for every API: posts their authors deleted, protected accounts, private messages and the list of who liked a post. Old research datasets don't fill that gap. Most were published as lists of tweet IDs, because platform rules allowed sharing IDs but not texts, and a deleted tweet's ID now returns nothing. Researchers lost their free route in 2023, and the history of who could search the Twitter archive explains what replaced it.

So a political study in 2026 collects its own data: forward from today for a live campaign, or back through the archive for a past one, which reaches March 2006.

#Collect political tweets with Sorsa API

For collecting political tweets, use Sorsa API: one API key reads officials' timelines and election discussion from $0.07 per 1,000 tweets, at 20 requests per second on every plan, with no X developer account and no OAuth. The official X API charges $5.00 per 1,000 posts for the same reads and stops at 3M posts a month.

Each kind of political data has its endpoint. Every one returns the author's full profile inside each tweet at no extra charge.

Political dataSorsa API endpointPer requestFrom, per 1,000
A watchlist of officials, as one feedGET /list-tweets (opens in a new tab)about 20 tweets$0.07
One official's full timelinePOST /user-tweets (opens in a new tab)about 20 tweets$0.07
Election or issue discussion, by query and datePOST /search-tweets (opens in a new tab)about 20 tweets$0.07
Replies under an official's postPOST /comments (opens in a new tab)about 20 replies$0.07
Profiles of every account on your listGET /info-batch (opens in a new tab)up to 100 profiles$0.014
Posts you already hold, re-checked by IDPOST /tweet-info-bulk (opens in a new tab)up to 100 tweets$0.014

Sorsa API is a REST API: one request with your key returns clean JSON, with nothing to run in your browser and no X account. It has run in production since 2022 with 6B+ requests served, and when X changes something, we fix it on our side, so a collection running through an election night doesn't break halfway. It never asks for your X login, so your own account carries no risk.

#Build a watchlist of politicians' accounts

A watchlist is the list of accounts your study follows, stored as numeric user IDs. Start it from a public source someone already verified, not from a search for blue checkmarks: since 2023 the checkmark is a paid subscription, not proof that an account is official.

Step Source or Sorsa API endpoint 1 List the accounts 505 Congress handles from a public file legislators-social-media.yaml official accounts only 2 Fix numeric IDs handles change, user IDs don't GET /info-batch 100 profiles per request 3 Read the watchlist feed every hour, back to the last saved post GET /list-tweets up to 5,000 accounts in one feed 4 Store and re-check save the time each post was first seen, log the posts that stop coming back POST /tweet-info-bulk 100 saved IDs per request
The watchlist runs in four steps: list, resolve, read, re-check. The last step turns deletions into data.

For the U.S. Congress, the open-data project unitedstates/congress-legislators (opens in a new tab) keeps a file of members' official social media accounts. On November 2, 2026 it listed 505 X handles, 408 of them with numeric IDs, and its last update was October 24, 2026. The project accepts only official, taxpayer-funded accounts, not campaign or personal ones, which is the distinction a study of official communication needs. Many parliaments publish members' accounts on their own open-data sites; start there for other countries.

Handles change; numeric IDs don't. Resolve every handle to its ID once with GET /info-batch, 100 accounts per request, and keep the ID as the key of your watchlist. The script below reads the Congress file and saves one profile row per member.

Python
import csv
import requests
import yaml  # pip install pyyaml requests

URL = "https://api.sorsa.io/v3/info-batch"
HEADERS = {"ApiKey": "YOUR_API_KEY"}
SRC = ("https://raw.githubusercontent.com/unitedstates/"
       "congress-legislators/main/legislators-social-media.yaml")

members = yaml.safe_load(requests.get(SRC, timeout=30).text)
handles = [m["social"]["twitter"] for m in members
           if m["social"].get("twitter")]

rows = []
for i in range(0, len(handles), 100):  # 100 handles per request
    r = requests.get(URL, headers=HEADERS, timeout=30,
                     params={"usernames": handles[i:i + 100]})
    r.raise_for_status()
    rows += r.json().get("users", [])

with open("watchlist.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["id", "username", "display_name", "followers"])
    for u in rows:
        w.writerow([u["id"], u["username"], u["display_name"],
                    u["followers_count"]])
print(len(rows), "accounts saved")

Six requests cover all 505 handles. Accounts that come back missing were renamed, suspended or made protected since the list was updated; check them by hand once and fix the file.

#Collect every post from the watchlist

To collect every post a watchlist publishes, put the accounts in one public X List and read it with GET /list-tweets: one feed carries the posts of every member, newest first, about 20 per request. An X List holds up to 5,000 accounts, enough for any national legislature, and the Lists guide shows how to read X Lists through the API.

Check the feed on a schedule and page back only until you reach a post you already saved. An hourly check of a 505-account Congress list then costs a few requests, however quiet or busy the hour was.

import json
import time
from datetime import datetime, timezone

import requests

URL = "https://api.sorsa.io/v3/list-tweets"
HEADERS = {"ApiKey": "YOUR_API_KEY"}
LIST_ID = "1720000000000000001"  # your public X List of officials


def check(last_id):
    """Fetch posts newer than last_id, newest first."""
    params, new = {"list_id": LIST_ID}, []
    while True:
        r = requests.get(URL, headers=HEADERS, params=params, timeout=30)
        if r.status_code == 429:  # over 20 requests a second
            time.sleep(1)
            continue
        r.raise_for_status()
        page = r.json()
        for t in page.get("tweets", []):
            if int(t["id"]) <= last_id:
                return new  # reached the last saved post
            new.append(t)
        if not page.get("next_cursor"):
            return new
        params["next_cursor"] = page["next_cursor"]


last_id = 0  # store this between runs
while True:
    seen = datetime.now(timezone.utc).isoformat()
    posts = check(last_id)
    with open("posts.jsonl", "a", encoding="utf-8") as f:
        for t in posts:
            f.write(json.dumps({"first_seen": seen, **t}) + "\n")
    if posts:
        last_id = max(int(t["id"]) for t in posts)
    print(seen, len(posts), "new posts")
    time.sleep(3600)  # one check an hour

A 403 answer means the key is missing, wrong or out of requests; a 429 means more than 20 requests in one second, so the script waits a second and asks again. Tweet IDs grow with time, which is why comparing them finds the last saved post.

Accounts on the watchlist

Posts a day, each

You check the feed

About 61K posts a month from 505 accounts, checked every hour

$303

Official X API

$0.005 per post

a month, posts only

from $5.04

Sorsa API

one List feed, about 20 posts per request

a month, authors included

Under a second per check on Sorsa API at 20 requests per second, for about 84 new posts each time.

Set the size of your watchlist, how often its accounts post and how often you check, to see a month of collection on both APIs.

To pull one official's whole history once, call POST /user-tweets with the account's user ID and page back from the newest post to the oldest; the guide to download all tweets from a user covers that export to CSV.

Election and issue discussion is collected with a search query: candidate and party names, the race's hashtags and the issue's key phrases, a language, and a date window, sent to POST /search-tweets and paged with next_cursor. The query is the research design, so write it before you collect and publish it with your results.

Balance the query. A search for one side's hashtag measures one side, so name every candidate and party in the race the same way, with the same operators. Cut long periods into windows of a day or a week with since: and until:, so a failed window reruns on its own. The full syntax is in the list of Twitter search operators.

Terminal
curl -X POST "https://api.sorsa.io/v3/search-tweets" \
  -H "ApiKey: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "(#Measure7 OR \"Measure 7\") since:2026-10-20 until:2026-10-21",
    "order": "latest"
  }'
Response200 OK
{
  "tweets": [
    {
      "id": "1981234567890123456",
      "full_text": "Measure 7 forum tonight: each side got 5 questions.",
      "created_at": "2026-10-20T23:41:07Z",
      "lang": "en",
      "likes_count": 57,
      "retweet_count": 14,
      "reply_count": 9,
      "quote_count": 3,
      "is_reply": false,
      "is_quote_status": false,
      "user": {
        "id": "1590000000000000042",
        "username": "county_civics_desk_demo",
        "display_name": "County Civics Desk",
        "followers_count": 8213
      }
    }
  ],
  "next_cursor": "DAADDAABCgABG4lN0iWAJ8kKAAIbiUY1qZvwRg"
}

Send the same body again with next_cursor added, and stop when a response comes without one. Pages hold about 20 tweets and their size varies, so count posts, not pages.

Decide about retweets before you collect

Retweets carry far more politics than original posts, so dropping them changes what your dataset says. In Pew Research Center's study of a year of tweets by U.S. adults, 44% of retweets and 42% of quote posts were political, against 26% of replies and 8% of original tweets.

Share of tweets that were political, by type Retweets 44% Quote tweets 42% Replies 26% Original tweets 8% 0% 25% 50%
Political content by tweet type, U.S. adults, May 2020 to May 2021. A filter on retweets removes the most political part of the conversation.

A study of what people say in their own words adds -filter:retweets to the query. A study of what spreads keeps retweets and reads the original post from each one's retweeted_status. Either choice is sound; leaving it to chance is not.

#Keep a record of deleted posts

No API returns a post after its author deletes it, not Sorsa API and not the official X API, so the only record of a politician's deleted post is the one you saved first. A watchlist that stores every post on first sight already holds that record; what it needs next is a way to notice the deletion.

Re-check the IDs you saved with POST /tweet-info-bulk, 100 per request. A post that no longer comes back was deleted, or its account was suspended or made protected; log the ID with the time you noticed. A daily re-check of 100K saved posts takes 1,000 requests, from $1.40.

Python
import requests

URL = "https://api.sorsa.io/v3/tweet-info-bulk"
HEADERS = {"ApiKey": "YOUR_API_KEY"}


def missing(ids):
    """Return the saved IDs that no longer come back."""
    gone = []
    for i in range(0, len(ids), 100):  # 100 IDs per request
        batch = ids[i:i + 100]
        r = requests.post(URL, headers=HEADERS, timeout=30,
                          json={"tweet_links": batch})
        r.raise_for_status()
        back = {t["id"] for t in r.json().get("tweets", [])}
        gone += [x for x in batch if x not in back]
    return gone

Deleted posts are the most sensitive part of a political collection. Report them as counts and patterns, or quote them only when the author is a public official and the post was part of public business, as press archives of politicians' deleted posts have done.

#What a political collection costs

A political collection on Sorsa API costs from $0.07 per 1,000 tweets through search, timelines and List feeds, against $5.00 per 1,000 posts on the official X API, about 70 times less. The cards compare both APIs for the size of your study.

Political tweets

Official X API

$5,000

for 1M political tweets

$0.005 per post

Sorsa API

from$70

for 1M political tweets

from $0.07 per 1,000

71x cheaper

Three typical research jobs show where the difference lands. The official X API figures count posts only, at the price on X's pay-per-use price list (opens in a new tab).

Research jobTweetsSorsa APIOfficial X API
A month of Congress posts, 505 accounts at 4 a day each, checked hourly61Kfrom $5.04$303
One debate night, names and hashtags of every candidate200Kfrom $14$1,000
Ten weeks of campaign discussion10Mfrom $700past the 3M monthly cap: Enterprise, $42K+ a month
  • A month of Congress posts, 505 accounts at 4 a day each, checked hourly

    Tweets
    61K
    Sorsa API
    from $5.04
    Official X API
    $303
  • One debate night, names and hashtags of every candidate

    Tweets
    200K
    Sorsa API
    from $14
    Official X API
    $1,000
  • Ten weeks of campaign discussion

    Tweets
    10M
    Sorsa API
    from $700
    Official X API
    past the 3M monthly cap: Enterprise, $42K+ a month

Good to know

From November 22, 2026 the official X API also bills each author profile that comes back with a post, at $0.010, as X's developer team announced on October 23 (opens in a new tab). Search results come from thousands of different authors, so election discussion read there can cost up to $15.00 per 1,000 posts. Sorsa API includes every author in the tweet.

The official X API's pay-per-use plan stops at 3M post reads per billing cycle, so a national campaign outgrows it in weeks, and the step above is an Enterprise contract from about $42,000 a month. It also needs an approved developer account, a project and an app, and OAuth tokens before the first read.

#Rules that keep political research sound

Political Twitter data measures online discussion, not public opinion, and the rules below keep a study inside what the data can show.

  • X users are not the electorate. In Pew's study, adults 50 and older were 24% of U.S. adult users and wrote 78% of their political tweets. Treat counts as a measure of who talks on X, never as a poll.
  • Bots and coordinated accounts inflate political topics. Filter them before you count and report what you removed; the guide to Twitter bot detection lists the signals.
  • The query decides the result. Publish the exact query, the windows, the endpoint and the collection dates, so others can rebuild your dataset.
  • Study posts and public officials, not voters. Measure discussion in aggregate and quote ordinary users sparingly. Inferring an individual's political views is off-limits in research ethics, and X's own developer agreement (opens in a new tab) bans profiling people by political affiliation.

Good to know

Political data is for understanding politics, not for steering voters. Keep a political collection to research, journalism and accountability work: no voter files, no targeting lists, no messaging aimed at people picked out by their posts.

Labeling, deduplication and splitting a corpus for a model are the same for political tweets as for any other kind, and the guide to build a Twitter dataset for machine learning covers them. Scoring tone and stance is the subject of Twitter sentiment analysis.

#Start your political collection with 100 free requests

To collect political Twitter data, start with Sorsa API: a watchlist feed, timelines and election search for one API key, from $0.07 per 1,000 tweets, at 20 requests per second on every plan. The 100 free requests return about 2,000 posts, with no card and no expiry. Pull one week of your officials or your race, check the query, then schedule the rest. University teams can also apply for Sorsa API's academic research access from an institutional email.

#FAQ

Can I still get political data from Twitter for research?

Yes. Free academic access ended in 2023, but public political posts can still be collected through an API. The official X API charges $0.005 per post and stops at 3M posts a month. Sorsa API reads the same public posts from $0.07 per 1,000, with 100 free requests to test a study before you budget it. Published datasets of tweet IDs also exist, though deleted posts no longer come back from them.

What is the best API for political Twitter data?

Use Sorsa API for political Twitter data: it reads politicians' timelines, X List feeds and election searches from $0.07 per 1,000 tweets, against $5.00 on the official X API. It needs one API key and no developer account, runs at 20 requests per second on every plan, has no 3M monthly cap, and returns each author's profile at no extra charge. It is read-only, so it never touches your X account.

Can you get politicians' deleted tweets?

No API returns a tweet after its author deletes it, Sorsa API included. A deleted post survives only where someone saved it first: a press archive, a web archive snapshot or your own collection. To study deletions, store every post from your watchlist when it first appears, then re-check the saved IDs with Sorsa API's POST /tweet-info-bulk, 100 per request, and log the ones that stop coming back.

Where can I find a list of Congress members' Twitter accounts?

The open-data project unitedstates/congress-legislators keeps one in its file legislators-social-media.yaml. On November 2, 2026 it listed 505 X handles of current members, 408 with numeric user IDs, and it includes only official accounts, not campaign ones. Resolve the handles to IDs with Sorsa API's GET /info-batch, 100 per request, and keep the IDs, because handles change.

Can Twitter data predict election results?

Not on its own. X users are not a sample of voters: in Pew Research Center's study, adults 50 and older wrote 78% of political tweets by U.S. adults while making up 24% of users. Post counts and sentiment measure online discussion, which is useful for studying agendas, campaigns and reactions, but forecasts built on them alone have a poor record.

Can EU researchers get X data under the DSA?

Yes, through Article 40 of the Digital Services Act. Vetted researchers can request data from very large platforms such as X through their national Digital Services Coordinator, for research on systemic risks in the EU, which include risks to elections and civic discourse. The EU's data access portal has taken applications since October 29, 2025. Approval takes time, so many teams start collecting public posts through an API meanwhile.

#How we checked

On November 2, 2026 we read X's pay-per-use price list, its developer agreement dated April 27, 2026 and its October 23, 2026 notice on billing expansions; every official X API price and rule here comes from them. The same day we downloaded the unitedstates/congress-legislators social media file and counted its accounts, and read Pew Research Center's 2022 report and the European Commission's DSA data access FAQ. Each Sorsa API endpoint, parameter and page size was checked against our live API reference; the requests and the response are built from it with demo accounts, and the costs are arithmetic from the published rates.

By the Sorsa API team. We build and run Sorsa API, the read-only Twitter/X API and scraper, in production since 2022 (formerly TweetScout API) with 6B+ requests served. About Sorsa API

What changed
  • Rewritten from scratch. Official X API prices, the 3M cap, the November 22, 2026 author billing and the developer agreement re-checked at X's own pages. New: a Congress watchlist from a public source, an hourly List-feed collector in Python and Node.js, a monthly cost calculator, a deletion log, a balanced election search with a real query, Pew's numbers on retweets and the research rules. The old cost table and the unverifiable collection example were removed.
  • First published.