Twitter/X API & scraper for academic research
Search the full archive back to 2006, rehydrate post IDs 100 at a time, pull timelines, replies and followers. Academic access is tailored to your study: from requests granted to your project to deep discounts.
No credit card, and they never expire.
For my I need about
Academic access goes further: tailored to your study, from granted requests to deep discounts.
Apply with this planAcademic access, built around your study
Requests granted for your study
We add the requests your project needs to your Sorsa API account.
A deep discount on a plan
For long or large projects: any plan at an academic price.
What every approved project gets
- Every endpoint
- 20 requests per second
- The same docs and API as everyone
- Fast answers from the team
Who can apply
- PhD students
- Master’s students
- Faculty
- Postdocs
- Research staff
- NGO and newsroom researchers
Non-commercial research in any discipline, applied for from an institutional email.
Apply in about two minutes. Meanwhile, your 100 free requests work on every endpoint.
Apply for academic accessEvery method your study needs
One API key, the same endpoints for 2006 and for this morning.
Archive search
Keyword corpora, event windows, long time series.
"heat wave" lang:en since:2022-07-01 until:2022-08-01 -filter:nativeretweetsAbout 20 posts a request, back to 2006
Posts by ID
Rehydrate a shared dataset or replicate a published corpus.
POST /tweet-info-bulk {"tweet_links": [100 IDs]}100 posts a request, each with its author
Account timelines
Panels of politicians, journalists or organisations.
from: + dates, or /user-tweets for an account’s whole historyAbout 20 posts a request, no 3,200-post cap
Conversations
Reply trees, quote chains, who retweeted.
/comments, /quotes, /retweetersAbout 20 a request
Networks
Followers and following of any public account.
/followers?username=…200 profiles a page
Profiles and account context
Profiles 100 at a time; country, username changes and verification from About this account.
/info-batch, /about100 profiles a request
Lists, Communities, Spaces
One X List watches up to 5,000 accounts; community posts and members; Space metadata.
/list-tweets?list_id=…About 20 posts a request
Search operators, pagination and page sizes are in the docs: Historical data, Search operators, Posts by ID.
Every post arrives with its variables
Replying to @heat_records_archive
Our open dataset is out: 1.2 million posts about the 2022 European heat waves, with the post IDs and the code to rehydrate them. #heatwave
10:42 AM, Aug 18, 2023
96.4K Views211 Bookmarks
42 Replies318 Reposts27 Quotes1.2K Likes
userusername, followers_count, created_at, verified, locationconversation_id_strin_reply_to_tweet_idIts place in a threadfull_textlangEvery word, and its languageentitiesMedia and linkscreated_atPosted, to the secondview_countbookmark_countViews and bookmarkslikes_countretweet_countreply_countquote_countEngagement, as it standsPlan your design around these
Deleted posts and protected accounts aren’t returned. A rehydrated list comes back smaller than the original.
Counts are today’s. Likes, views and followers are as they stand when you collect; for counts over time, collect on a schedule and keep each snapshot.
Profiles are current. A post from 2014 comes with its author’s profile as it is now.
You poll, nothing streams. New posts arrive when you ask; one request reads an X List of up to 5,000 accounts.
Not available: who liked a post, a user’s likes or bookmarks, DMs, past follower lists.
Try your date range on the free requests before you plan the full collection.
From an ID list to a dataset
import csv, json, time, requests
URL = "https://api.sorsa.io/v3/tweet-info-bulk"
KEY = "YOUR_API_KEY"
ids = [row[0] for row in csv.reader(open("tweet_ids.csv"))]
with open("tweets.ndjson", "w") as out:
for i in range(0, len(ids), 100):
while True:
r = requests.post(URL, headers={"ApiKey": KEY},
json={"tweet_links": ids[i:i + 100]})
if r.status_code != 429:
break
time.sleep(1) # 20 requests per second per key
r.raise_for_status()
for tweet in r.json()["tweets"]:
out.write(json.dumps(tweet) + "\n")library(httr2)
library(jsonlite)
ids <- readLines("tweet_ids.txt")
batches <- split(ids, ceiling(seq_along(ids) / 100))
fetch <- function(batch) {
resp <- request("https://api.sorsa.io/v3/tweet-info-bulk") |>
req_headers(ApiKey = Sys.getenv("SORSA_API_KEY")) |>
req_body_json(list(tweet_links = as.list(batch))) |>
req_retry(max_tries = 5) |>
req_perform()
resp_body_json(resp)$tweets
}
tweets <- unlist(lapply(batches, fetch), recursive = FALSE)
write_json(tweets, "tweets.json", auto_unbox = TRUE)- A million IDs take about eight minutes at 20 requests per second.
- Failed requests are never charged.
- Writing it with an AI assistant? Give Claude Code, Cursor or Codex our docs file, or connect the MCP server. Build with AI
For your ethics board and your methods section
What reviewers ask
- What data?
- Public posts and profiles only. No protected accounts, no DMs.
- Any interaction?
- None. Sorsa API is read-only: it never posts, likes, follows or messages.
- Whose account?
- No X account or login is involved, yours or anyone’s.
- What does the provider keep?
- Which requests you made, for a year; the full responses, for 30 days.
- Who knows about the project?
- X never learns who is asking: your key, account and identity stay with us.
- Where does your copy live?
- Wherever your data management plan says. It’s yours.
- Paperwork?
- On request, we write a short letter about the data source for your ethics board.
The methods paragraph
2.1 Data collection
Posts were collected through Sorsa API (https://api.sorsa.io), an independent API for public X (Twitter) data, using full-archive search for 1 January to 31 March 2024.
Sharing your dataset? Share post IDs and your code, so others can rehydrate and check your work.
Apply for academic access
About two minutes. A person on our team reads every application and answers by email.
What happens next
- 1
Send the form.
- 2
We read it.
If something is unclear, we ask.
- 3
We agree your access.
The requests your study needs, or a deep discount, for as long as it runs.
- 4
You collect.
The access appears on your Sorsa API account; every key runs at 20 requests per second.
Meanwhile, your 100 free requests work on every endpoint. Start with 100 free requests
Prefer email? Write to contacts@sorsa.io from your institutional address.
Questions researchers ask
Read next
Twitter/X API & scraper for academic research
Academic access tailored to your study, from granted requests to deep discounts.
Academic access, built around your study
- Requests granted for your studyWe add the requests your project needs to your Sorsa API account.
- A deep discount on a planFor long or large projects: any plan at an academic price.
Every method your study needs
One API key, the same endpoints for 2006 and for this morning.
Every post arrives with its variables
userReplying to @heat_records_archive
in_reply_to_tweet_idOur open dataset is out: 1.2 million posts about the 2022 European heat waves, with the post IDs and the code to rehydrate them. #heatwave
full_text10:42 AM, Aug 18, 2023
created_at42 Replies318 Reposts27 Quotes1.2K Likes
likes_count …Old posts come with the same fields as today’s.
From an ID list to a dataset
import csv, json, time, requests
URL = "https://api.sorsa.io/v3/tweet-info-bulk"
KEY = "YOUR_API_KEY"
ids = [row[0] for row in csv.reader(open("tweet_ids.csv"))]
with open("tweets.ndjson", "w") as out:
for i in range(0, len(ids), 100):
while True:
r = requests.post(URL, headers={"ApiKey": KEY},
json={"tweet_links": ids[i:i + 100]})
if r.status_code != 429:
break
time.sleep(1) # 20 requests per second per key
r.raise_for_status()
for tweet in r.json()["tweets"]:
out.write(json.dumps(tweet) + "\n")library(httr2)
library(jsonlite)
ids <- readLines("tweet_ids.txt")
batches <- split(ids, ceiling(seq_along(ids) / 100))
fetch <- function(batch) {
resp <- request("https://api.sorsa.io/v3/tweet-info-bulk") |>
req_headers(ApiKey = Sys.getenv("SORSA_API_KEY")) |>
req_body_json(list(tweet_links = as.list(batch))) |>
req_retry(max_tries = 5) |>
req_perform()
resp_body_json(resp)$tweets
}
tweets <- unlist(lapply(batches, fetch), recursive = FALSE)
write_json(tweets, "tweets.json", auto_unbox = TRUE)- A million IDs take about eight minutes at 20 requests per second.
- Failed requests are never charged.
- Writing it with an AI assistant? Build with AI
For your ethics board and your methods section
What data?
Public posts and profiles only. No protected accounts, no DMs.
Any interaction?
None. Sorsa API is read-only: it never posts, likes, follows or messages.
Whose account?
No X account or login is involved, yours or anyone’s.
What does the provider keep?
Which requests you made, for a year; the full responses, for 30 days.
Who knows about the project?
X never learns who is asking: your key, account and identity stay with us.
Where does your copy live?
Wherever your data management plan says. It’s yours.
Paperwork?
On request, we write a short letter about the data source for your ethics board.
Apply for academic access
About two minutes. A person on our team reads every one.About two minutes. We answer by email.
- 1Send
- 2We read it
- 3We agree
- 4You collect
Prefer email? Write to contacts@sorsa.io from your institutional address.
Questions researchers ask
Read next
Who can apply
- PhD students
- Master’s students
- Faculty
- Postdocs
- Research staff
- NGO and newsroom researchers
- Undergraduates writing a thesis
- Teachers running a course
Non-commercial research in any discipline, applied for from an institutional email.
Apply for academic accessArchive search
Keyword corpora, event windows, long time series.
"heat wave" lang:en since:2022-07-01 until:2022-08-01 -filter:nativeretweetsAbout 20 posts a request, back to 2006
Open in the docsPosts by ID
Rehydrate a shared dataset or replicate a published corpus.
POST /tweet-info-bulk {"tweet_links": [100 IDs]}100 posts a request, each with its author
Open in the docsAccount timelines
Panels of politicians, journalists or organisations.
from: + dates, or /user-tweets for an account’s whole historyAbout 20 posts a request, no 3,200-post cap
Open in the docsConversations
Reply trees, quote chains, who retweeted.
/comments, /quotes, /retweetersAbout 20 a request
Open in the docsNetworks
Followers and following of any public account.
/followers?username=…200 profiles a page
Open in the docsProfiles and account context
Profiles 100 at a time; country, username changes and verification from About this account.
/info-batch, /about100 profiles a request
Open in the docsLists, Communities, Spaces
One X List watches up to 5,000 accounts; community posts and members; Space metadata.
/list-tweets?list_id=…About 20 posts a request
Open in the docsPlan your design around these
Deleted posts and protected accounts aren’t returned. A rehydrated list comes back smaller than the original.
Counts are today’s. Likes, views and followers are as they stand when you collect; for counts over time, collect on a schedule and keep each snapshot.
Profiles are current. A post from 2014 comes with its author’s profile as it is now.
You poll, nothing streams. New posts arrive when you ask; one request reads an X List of up to 5,000 accounts.
Not available: who liked a post, a user’s likes or bookmarks, DMs, past follower lists.
Try your date range on the free requests before you plan the full collection.
The methods paragraph
2.1 Data collection
Posts were collected through Sorsa API (https://api.sorsa.io), an independent API for public X (Twitter) data, using full-archive search for 1 January to 31 March 2024.
Sharing your dataset? Share post IDs and your code, so others can rehydrate and check your work.
Who can apply?
PhD and master’s students, faculty, postdocs and research staff at universities and research institutes, for non-commercial research in any discipline.PhD and master’s students, faculty and postdocs at universities and research institutes, for non-commercial research in any discipline. Apply from your institutional email.
What does academic access include?
Access tailored to your study: from the requests it needs, granted to your account, to a deep discount on a plan. Every endpoint, 20 requests per second, the same docs and support as every customer.
What happens after I apply?
About two minutes. A person on our team reads every application and answers by email.We review your application and answer by email. A person on our team reads your application and answers by email. If something is unclear we ask; then we agree the volume and the period with you and add the access to your Sorsa API account.
Can I start before I hear back?
Yes. Every account gets 100 free requests: no card, they never expire, and they work on every endpoint. That’s up to 10,000 posts by ID or 20,000 follower profiles, enough to pilot your queries.
I don’t have an institutional email.
Apply with the email you use and add a link to your faculty, lab, ORCID or Google Scholar page, so we can see your affiliation.
How far back does the data go?
To 2006. Full-archive search takes X’s own operators: from:, to:, since:, until:, lang:, exact phrases, hashtags and engagement filters. Account timelines go back to the first post, without a 3,200-post cap.
Can I rehydrate a dataset of post IDs?
Yes: 100 IDs per request, each post with its author’s full profile. A million IDs take 10,000 requests, about eight minutes at 20 requests per second. Posts deleted or made private since the original collection don’t come back, so expect fewer posts than IDs.
What isn’t in the data?
Deleted posts, protected accounts, DMs, lists of who liked a post, a user’s likes and bookmarks, and follower lists as they were in the past. Counts and profiles are as they stand when you collect. Nothing streams: you poll, and one request reads an X List of up to 5,000 accounts.
What should I tell my ethics board?
That the data is public posts and profiles; Sorsa API is read-only and never posts, likes, follows or messages; no X account or login is involved; and you don’t contact the people in the data. We keep a log of your requests for a year and the full responses for 30 days. On request, we write a short letter about the data source.
Can I share my dataset?
Share post IDs and your collection code: anyone can rehydrate them and check your work. How much more you share is up to your ethics approval and data management plan.
How do I cite Sorsa API?
In your methods section: “Data were collected through Sorsa API (https://api.sorsa.io), an independent API for public X (Twitter) data.” The BibTeX is in section 5 on this page.
Can my co-authors use the same access?
Yes. Your account holds several API keys: give each person their own and see each key’s usage in the dashboard.
Can you invoice my university?
Yes. Say so in the application and we’ll arrange an invoice for your project.
What does X offer researchers now?
X closed its Academic Research track in 2023. Its pay-per-use API bills $0.005 per post and $0.010 per profile; its EU route under the Digital Services Act covers only research on systemic risks in the EU, through a separate application. Sorsa API works for any discipline and topic, with one key and no X developer account.
How does this compare with other academic offers?
TwitterAPI.io refunds half of what a research project spends, up to $200, after you pay. Sorsa API tailors access to your study before you collect, from granted requests to deep discounts.
My project is commercial.
Then a plan fits: posts from $0.014 per 1,000, every endpoint on every plan. For bigger volumes, talk to sales.
All 16 questions
No question matches. Write to contacts@sorsa.io.