Skip to content

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.

Export Tweets: the latest 20 posts of any account

Free. No sign-up, no extension. CSV, Excel or JSON with 25 columns: text, date, views, likes, reposts, replies, bookmarks, media links and X's labels.

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.

RouteUse it forWhat you getCost
Sorsa API in the API PlaygroundEvery post of an account, no codePages of 20 posts as CSV, Excel or JSONFirst 2,000 posts free, then from $0.07 per 1,000
Sorsa API, POST /user-tweetsEvery post, in code or on a scheduleThe whole timeline, 20 posts per requestFirst 2,000 posts free, then from $0.07 per 1,000
Sorsa API, POST /search-tweetsOne period of an accountPosts between two dates, back to 2006From $0.07 per 1,000
Sorsa API, POST /tweet-info-bulkPosts you already have links toUp to 100 posts per requestFrom $0.014 per 1,000
Export Tweets toolThe latest 20 posts, onceOne file of 20 postsFree
X's archive of your dataEverything from your own accountA .zip of HTML and JSON, DMs includedFree; 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.

  1. Create a free account. Sign up and copy your API key from the dashboard. The first 100 requests are free, with no card.

  2. Open the playground. Go to the API playground and paste the key at the top.

  3. Choose User Tweets. Type the account's handle without the @.

  4. 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.

  5. 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.

  1. Get a key. Sign up and copy the key from the dashboard. There is no developer account to open, no OAuth and no approval.

  2. Check the size of the job. GET /info (opens in a new tab) returns the profile with tweets_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"
    
  3. 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"
    }
    
  4. Follow the cursor. Put each next_cursor into the body of the next request. A response with no cursor is the last page: the timeline is complete.

    POST /user-tweets handle + cursor Write 20 rows to tweets.csv Save the cursor to a .cursor file No cursor left the file is complete repeat with the saved cursor Stopped halfway? Run the script again: it reads the saved cursor and goes on.
    Save the cursor after every page, and a stopped download resumes where it left off instead of starting over.
  5. 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.

Example account: 12.4K posts, from 2011 to 2026 Official X API user timeline 8.2K older posts, never returned Newest 3,200 Sorsa API POST /user-tweets All 12.4K posts, back to the first one 3,200 cap 2011 2014 2017 2020 2023 2026 Older posts to the left, newest on the right
On a 12.4K-post account, the official X API's timeline stops after the newest 3,200 posts; Sorsa API's timeline runs to the first one.

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.

Request body
{
  "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 accountRequestsSorsa API, fromOfficial X API
3,200160$0.22$16.00
12.4K620$0.87$62.00
50K2,500$3.50$250.00
200K10K$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:

$62.00
on the official X API
$5.00 per 1,000 posts, through full-archive search: its user timeline stops at the newest 3,200
from $0.87
on Sorsa API
620 requests at 20 posts each, 6% of Starter's 10K a month

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.

FieldWhat it holdsExample
idThe post's ID, as a string; keep it as text so spreadsheets don't round it"2116531887928250368"
created_atWhen it was posted, ISO 8601 in UTC"2026-10-31T14:05:00Z"
full_textThe full text, long posts included"October was our warmest..."
likes_count, retweet_count, reply_count, quote_countEngagement counts812
view_count, bookmark_countViews and bookmarks96400
is_reply, in_reply_to_usernameWhether the post answers someone, and whomfalse
quoted_status, retweeted_statusThe quoted or reposted post, in fullan object
entitiesPhotos, videos and links, each with type and link"photo"
made_with_ai, paid_partnershipX's labels on the postfalse
userThe author's full profilean 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 type and link
    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.

Python
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.

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.

Terminal
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:

  1. Open your X data. Go to Settings and privacy, then Account, then Your X data.

  2. Verify it's you. Enter the code X sends to your email or phone.

  3. Request the data. Under Download your data, press Request data next to X.

  4. 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.

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, answer first. The 3,200 cap traced to X's own documentation, a no-code route through the API Playground, a resumable Python and Node.js script, a cost estimator for any account size, sections on media and saving posts by link, and every price checked against docs.x.com.
  • First published.