Download all tweets from a user on X
To download all tweets from an X account, page through its timeline and save each page to CSV; X's own timeline endpoint stops at the newest 3,200 posts, so a full history takes an endpoint without that cap.
Short answer: X's archive gives your own account's full history free, though X says it may take a few days. For anyone else, the official X API's user timeline stops at the newest 3,200 posts. For every post, use Sorsa API's POST /user-tweets: 20 posts per request with no cap, from $0.07 per 1,000; your 100 free requests cover the first 2,000.
- Endpoint
POST /user-tweets- Page size
- 20 posts per request
- Price
- from $0.07 per 1,000 posts
#Which way should you use?
Two facts pick the route: whose tweets you need, and how many of them. Your own account's full history comes from X's archive, free. Anyone else's posts come from an export that pages through their public timeline, and how far back it reaches depends on the endpoint behind it.
For an account's latest posts, the free Export Tweets tool saves 20 at a time as CSV, Excel or JSON, with views, likes, bookmarks and X's labels in 25 columns. It needs no sign-up and no extension; type a handle below to try it.
The form opens the free Export Tweets tool with the account filled in.
Every post an account has ever written is a bigger job, and five numbers decide whether a route can do it:
- How far back it reaches. X's own user timeline endpoint returns only the newest 3,200 posts.
- The price per 1,000 posts. The official X API bills $0.005 per post, so 1,000 posts cost $5.00.
- What it needs from you. The official X API wants a developer account, an app and prepaid credits.
- How fast it runs. A long history takes hundreds or thousands of requests, so requests per second matter.
- What it puts at risk. Tools that read through your logged-in X session can get that account flagged for automated activity, which is why some of them advise exporting from a spare account.
#Download every post with Sorsa API
For the whole history of any public account, use Sorsa API, a Twitter (X) API and scraper: POST /user-tweets pages the entire timeline with no 3,200 cap, 20 posts per request, from $0.07 per 1,000 posts against $5.00 on the official X API.
It meets the other three numbers too. Sorsa API needs one API key instead of a developer account, answers 20 requests per second on every plan, free accounts included, and never asks for your X login or cookies, so no X account of yours is involved in a download.
Four routes run through Sorsa API with the same key and the same 100 free requests; the free tool needs no key at all, and X's archive covers only the account you sign in with.
| Route | Use it for | What you get | Cost |
|---|---|---|---|
| Sorsa API in the API Playground | Every post of an account, no code | Pages of 20 posts as CSV, Excel or JSON | First 2,000 posts free, then from $0.07 per 1,000 |
Sorsa API, POST /user-tweets | Every post, in code or on a schedule | The whole timeline, 20 posts per request | First 2,000 posts free, then from $0.07 per 1,000 |
Sorsa API, POST /search-tweets | One period of an account | Posts between two dates, back to 2006 | From $0.07 per 1,000 |
Sorsa API, POST /tweet-info-bulk | Posts you already have links to | Up to 100 posts per request | From $0.014 per 1,000 |
| Export Tweets tool | The latest 20 posts, once | One file of 20 posts | Free |
| X's archive of your data | Everything from your own account | A .zip of HTML and JSON, DMs included | Free; X says it may take a few days |
Sorsa API in the API Playground
- Use it for
- Every post of an account, no code
- What you get
- Pages of 20 posts as CSV, Excel or JSON
- Cost
- First 2,000 posts free, then from $0.07 per 1,000
Sorsa API,
POST /user-tweets- Use it for
- Every post, in code or on a schedule
- What you get
- The whole timeline, 20 posts per request
- Cost
- First 2,000 posts free, then from $0.07 per 1,000
Sorsa API,
POST /search-tweets- Use it for
- One period of an account
- What you get
- Posts between two dates, back to 2006
- Cost
- From $0.07 per 1,000
Sorsa API,
POST /tweet-info-bulk- Use it for
- Posts you already have links to
- What you get
- Up to 100 posts per request
- Cost
- From $0.014 per 1,000
Export Tweets tool
- Use it for
- The latest 20 posts, once
- What you get
- One file of 20 posts
- Cost
- Free
X's archive of your data
- Use it for
- Everything from your own account
- What you get
- A .zip of HTML and JSON, DMs included
- Cost
- Free; X says it may take a few days
#Download every tweet without writing code
The API Playground sends Sorsa API requests from a form: choose User Tweets, type the handle, run it, and export the posts as CSV, Excel or JSON. It suits a one-off download where a script would be overkill.
-
Create a free account. Sign up and copy your API key from the dashboard. The first 100 requests are free, with no card.
-
Open the playground. Go to the API playground and paste the key at the top.
-
Choose User Tweets. Type the account's handle without the @.
-
Set the number of pages. Each page is one request of 20 posts, so 100 pages bring back 2,000 posts. The Playground shows the cost before anything runs.
-
Run and export. Switch the result to CSV, Excel or JSON and save the file.
For an account with years of history, split the work by date: run Search Tweets in the same Playground with from: and a date range, one year per run, as the section on the 3,200 cap shows below.
#How to download all tweets with the API, step by step
In code, the whole download is one loop around Sorsa API's POST /user-tweets: one key in the ApiKey header, 20 posts per request, and a cursor that pages back to the account's first post. A cursor is a bookmark the API hands back with each page; sending it with the next request returns the page after it.
-
Get a key. Sign up and copy the key from the dashboard. There is no developer account to open, no OAuth and no approval.
-
Check the size of the job.
GET /info(opens in a new tab) returns the profile withtweets_count; divide it by 20 to get the number of requests.Terminal curl -G "https://api.sorsa.io/v3/info" \ -H "ApiKey: YOUR_API_KEY" \ --data-urlencode "username=northfield_climatelab" -
Request the first page. Send the handle to
POST /user-tweets(opens in a new tab) as JSON. A user ID or a profile link works in place of the handle.Terminal curl -X POST "https://api.sorsa.io/v3/user-tweets" \ -H "ApiKey: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"username": "northfield_climatelab"}'Response200 OK { "tweets": [ { "id": "2116531887928250368", "full_text": "October was our warmest month on record. #climatedata", "created_at": "2026-10-31T14:05:00Z", "lang": "en", "likes_count": 812, "retweet_count": 240, "reply_count": 61, "quote_count": 38, "view_count": 96400, "bookmark_count": 410, "is_reply": false, "is_quote_status": false, "entities": [ { "type": "photo", "link": "https://pbs.twimg.com/media/Gx1nQ7aW8AAzk3T.jpg" } ], "user": { "username": "northfield_climatelab", "display_name": "Northfield Climate Lab", "followers_count": 48900, "tweets_count": 12400 } } ], "next_cursor": "DAABCgABG8vZ0Qm__-oKAAIbuv3MBVvA0AgAAwAAAAIAAA" } -
Follow the cursor. Put each
next_cursorinto the body of the next request. A response with no cursor is the last page: the timeline is complete.Save the cursor after every page, and a stopped download resumes where it left off instead of starting over. -
Write rows as they arrive. The script below appends each page to a CSV, keeps the media links in their own column and saves the cursor after every page, so a stopped run picks up where it left off.
import csv import os import time import requests API = "https://api.sorsa.io/v3/user-tweets" HEADERS = {"ApiKey": "YOUR_API_KEY"} FIELDS = ["id", "created_at", "full_text", "lang", "likes_count", "retweet_count", "reply_count", "quote_count", "view_count", "bookmark_count", "is_reply", "in_reply_to_username", "media"] def download(username, out="tweets.csv"): state = out + ".cursor" cursor = open(state).read().strip() if os.path.exists(state) else None first_run = not os.path.exists(out) with open(out, "a", newline="", encoding="utf-8") as f: rows = csv.DictWriter(f, FIELDS, extrasaction="ignore") if first_run: rows.writeheader() while True: body = {"username": username} if cursor: body["next_cursor"] = cursor r = requests.post(API, headers=HEADERS, json=body, timeout=60) if r.status_code == 429: # over 20 requests in a second time.sleep(1) continue r.raise_for_status() # 403: key missing, wrong or used up page = r.json() for t in page.get("tweets") or []: media = [e["link"] for e in t.get("entities") or [] if e.get("type") in ("photo", "video")] rows.writerow({**t, "media": " ".join(media)}) f.flush() cursor = page.get("next_cursor") if not cursor: break with open(state, "w") as s: s.write(cursor) if os.path.exists(state): os.remove(state) download("northfield_climatelab")
Replies and reposts
By default POST /user-tweets returns the account's own posts. Add "with_replies": true to the body to include its replies, or set "type" to "replies" or "retweets" to download only those. A repost carries the original post in full under retweeted_status.
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 scripts wait a second and retry. Failed requests are never charged, and the guide to Twitter API in Python covers retries and pagination for every other endpoint.
#How to get more than 3,200 tweets from a user
Use an endpoint without the cap: the 3,200 limit belongs to the official X API's user timeline, which returns only an account's 3,200 most recent posts, while Sorsa API's POST /user-tweets keeps paging back to the first post.
X's own timeline documentation (opens in a new tab) gives the number: "Up to 3,200 most recent Posts". The same documentation lists full-archive search (opens in a new tab) back to March 2006 for pay-per-use customers, at $0.005 per post behind a developer account and an app. So the cap is a property of one endpoint, not of X's data, and guides that call 3,200 a wall for every tool are describing that endpoint.
Sorsa API sells that whole history at the same rate as the newest page: an account with 12.4K posts is 620 requests, from $0.87. The official X API's full-archive search would bill the same posts at $62.00 and stop for the month at 3M post reads.
Download one period by date
To download part of a history, search the account by date: POST /search-tweets (opens in a new tab) with from:, since: and until: returns that account's posts between two dates, back to 2006, 20 per request with the same cursor.
{
"query": "from:northfield_climatelab since:2019-01-01 until:2020-01-01",
"order": "latest"
}
One window per year keeps each run short and easy to retry. Add filter:media to keep only posts with photos or video, or -filter:replies to drop replies; the Twitter search operators guide lists the rest. Searching every account's posts on a topic back to 2006 is a different job, covered in historical Twitter data.
#What a full download costs
A full download costs one request per 20 posts: an account with 12.4K posts takes 620 requests, from $0.87 on Sorsa API, while the official X API bills the same posts $62.00 at $0.005 each.
At every size Sorsa API costs from $0.07 per 1,000 posts against $5.00, and the 100 free requests cover any account up to 2,000 posts.
| Posts on the account | Requests | Sorsa API, from | Official X API |
|---|---|---|---|
| 3,200 | 160 | $0.22 | $16.00 |
| 12.4K | 620 | $0.87 | $62.00 |
| 50K | 2,500 | $3.50 | $250.00 |
| 200K | 10K | $14.00 | $1,000.00 |
3,200
- Requests
- 160
- Sorsa API, from
- $0.22
- Official X API
- $16.00
12.4K
- Requests
- 620
- Sorsa API, from
- $0.87
- Official X API
- $62.00
50K
- Requests
- 2,500
- Sorsa API, from
- $3.50
- Official X API
- $250.00
200K
- Requests
- 10K
- Sorsa API, from
- $14.00
- Official X API
- $1,000.00
Type the post count from any profile to price your own download.
The count under the name on any X profile. Or pick one:
Your 100 free requests cover the first 2,000 posts, 16% of this account.
The free share and both prices update as you type; posts per request stay at 20.
You pay per request, never per post: a page of 20 costs the same as a last page of 3, and failed requests cost nothing. Starter's 10K requests a month cover 200K posts, and each plan's rate per 1,000 requests is on the pricing page.
Each account's pages run one after another, because every request needs the cursor from the one before. Several accounts download side by side, up to 20 requests per second per key on every plan, free accounts included.
#What each row of the file holds
Every post comes back as one JSON object with its text, date, counts, media and author, so a CSV row needs no second request. The fields below are the ones the scripts above write, taken from the live API reference.
| Field | What it holds | Example |
|---|---|---|
id | The post's ID, as a string; keep it as text so spreadsheets don't round it | "2116531887928250368" |
created_at | When it was posted, ISO 8601 in UTC | "2026-10-31T14:05:00Z" |
full_text | The full text, long posts included | "October was our warmest..." |
likes_count, retweet_count, reply_count, quote_count | Engagement counts | 812 |
view_count, bookmark_count | Views and bookmarks | 96400 |
is_reply, in_reply_to_username | Whether the post answers someone, and whom | false |
quoted_status, retweeted_status | The quoted or reposted post, in full | an object |
entities | Photos, videos and links, each with type and link | "photo" |
made_with_ai, paid_partnership | X's labels on the post | false |
user | The author's full profile | an object |
id- What it holds
- The post's ID, as a string; keep it as text so spreadsheets don't round it
- Example
"2116531887928250368"
created_at- What it holds
- When it was posted, ISO 8601 in UTC
- Example
"2026-10-31T14:05:00Z"
full_text- What it holds
- The full text, long posts included
- Example
"October was our warmest..."
likes_count,retweet_count,reply_count,quote_count- What it holds
- Engagement counts
- Example
812
view_count,bookmark_count- What it holds
- Views and bookmarks
- Example
96400
is_reply,in_reply_to_username- What it holds
- Whether the post answers someone, and whom
- Example
false
quoted_status,retweeted_status- What it holds
- The quoted or reposted post, in full
- Example
- an object
entities- What it holds
- Photos, videos and links, each with
typeandlink - Example
"photo"
made_with_ai,paid_partnership- What it holds
- X's labels on the post
- Example
false
user- What it holds
- The author's full profile
- Example
- an object
The CSV opens in Excel and Google Sheets as it is; the JSON lines file from the Node.js tab suits notebooks and AI datasets. To keep a sheet filled on a schedule instead, see how to export Twitter data to Google Sheets.
Good to know
Downloads cover public posts only. Posts of protected accounts, deleted posts, and the lists of who liked a post or what an account liked or bookmarked are private on X, so no API returns them. A post deleted after your download stays in your file.
#How to download all media from an X account
Read the account's posts with POST /user-tweets and save every entity whose type is photo or video: each one carries a direct link to the file, so the loop that saves the text can save the media too.
import os
import requests
def save_media(tweets, folder="media"):
os.makedirs(folder, exist_ok=True)
for t in tweets:
for e in t.get("entities") or []:
if e.get("type") not in ("photo", "video"):
continue
name = t["id"] + "-" + e["link"].split("/")[-1].split("?")[0]
data = requests.get(e["link"], timeout=60).content
with open(os.path.join(folder, name), "wb") as f:
f.write(data)
Call it with each page's tweets inside the download loop. GET /info reports the account's media_count before you start, so you know how many files to expect.
Each entity also carries a preview, a thumbnail you can save instead of the full video when you only need to see what was posted. Naming every file after its post's ID, as the snippet does, lets you join the media folder to the CSV later, because the media column of each row lists the same links. For the media of one post, paste its link into the free Media Downloader; larger media jobs are covered in the guide to downloading Twitter media by API.
#Save a list of tweets by link
Send up to 100 post links or IDs to POST /tweet-info-bulk (opens in a new tab) and every post comes back in one request, from $0.014 per 1,000 posts: 250x less than the official X API's $5.00.
curl -X POST "https://api.sorsa.io/v3/tweet-info-bulk" \
-H "ApiKey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tweet_links": [
"https://x.com/northfield_climatelab/status/2116531887928250368",
"2115035779691450372"]}'
Use it for posts collected by hand, the sources behind a report, or a thread worth keeping. A link can be a full x.com or twitter.com address or the bare ID, and both can sit in one list; a thousand saved posts take 10 requests. Each post arrives with the same fields as a timeline page, author included.
Save a post the day you find it when it matters to you. A link to a post that was deleted or turned protected simply doesn't come back, the rest of the batch arrives as usual, and a post deleted on X can't be fetched afterwards by anyone.
#How to download all of your own tweets
For your own account, request X's archive: it is free and holds every post since your first, plus your DMs, media, followers and Lists, as HTML and JSON files in a .zip. The X Help Center (opens in a new tab) gives these steps:
-
Open your X data. Go to Settings and privacy, then Account, then Your X data.
-
Verify it's you. Enter the code X sends to your email or phone.
-
Request the data. Under Download your data, press Request data next to X.
-
Download the .zip. X emails a link, or sends a push notification, when the file is ready; its help page says that may take a few days.
The archive answers "everything I ever posted". When you need your public posts as a spreadsheet today, with views and bookmarks in columns, run the API steps above on your own handle: the same 20 posts per request, and no wait for an email.
#Download your first account today
Every post of any public account is one handle and one loop away. Sorsa API returns 20 posts per request with no 3,200 cap, from $0.07 per 1,000 against $5.00 on the official X API, and the first 2,000 posts are free. Start with the account you need now, then add the next ones side by side at 20 requests per second; the guide on how to pull tweets, followers and profiles from X covers every other kind of download the same way.
#FAQ
Can you download all tweets from someone else's account?
Yes, from any public account. Sorsa API's POST /user-tweets takes the handle and returns every post on the timeline, 20 per request, with no permission needed from the account, because the posts are public on X. Protected accounts show their posts only to followers they approve, so they can't be downloaded, and a request that returns nothing for one costs nothing.
Is there a free way to download all tweets from a user?
Yes, three. The free Export Tweets tool saves an account's latest 20 posts with no sign-up. Every Sorsa API account starts with 100 free requests, enough for the first 2,000 posts of any public account, with no card and no expiry. For your own account, X's archive is free and complete, though X says it may take a few days to arrive.
Which API should I use to download all tweets from a user?
Use Sorsa API. Its POST /user-tweets returns 20 posts per request with no 3,200 cap, from $0.07 per 1,000 posts, while the official X API charges $5.00 per 1,000 and its user timeline stops at the 3,200 most recent posts. Sorsa API needs one API key instead of a developer account and OAuth, and answers 20 requests per second on every plan.
Can you download tweets from a private account?
No. A protected account's posts are visible only to the followers it approves, so no API returns them, the official X API included. Sorsa API reads public data only and never asks for anyone's X login, and a request for a protected account that comes back empty is not charged.
Can I download someone's likes or bookmarks?
No. X keeps the list of posts an account liked, and its bookmarks, private, so no API returns them. What you can download is the like and bookmark counts on every post, likes_count and bookmark_count, and, through GET /info, the total number of posts an account has liked, favourites_count.
Will the account know I downloaded its tweets?
No. Reading public posts through Sorsa API sends nothing to the account and uses no X account of yours: the request goes from your key to Sorsa API. Nothing appears in the account's notifications, and your own X account stays out of it.
#How we checked
We wrote every request in this guide from the live Sorsa API reference on November 2, 2026, and checked each endpoint, body field, page size and response field against it. The responses are trimmed examples in the shape that reference documents, for a fictional account; we did not run them with a live key for this article. X's 3,200 cap, its prices and the archive steps come from docs.x.com and the X Help Center, read the same day, and every cost is arithmetic on Sorsa API's $1.40 per 1,000 requests and X's $0.005 per post.
Sources
- X API: Timelines (user posts, up to 3,200 most recent) (opens in a new tab)
- X API: Search (full archive back to March 2006) (opens in a new tab)
- X API: pay-per-use pricing (opens in a new tab)
- X Help Center: How to download your X archive (opens in a new tab)
- Sorsa API reference: User Tweets (opens in a new tab)
- Sorsa API docs: Historical data (opens in a new tab)