Skip to content

Twitter API expansions and tweet authors

On the official X API, a post comes back with only its ID and text; adding expansions=author_id returns the author's profile in a separate includes block, and from November 22, 2026 each author returned that way is billed $0.010.

Short answer: Add expansions=author_id and user.fields to the request, then match each post's author_id to a user in includes.users. From November 22, 2026, a post with its author costs up to $0.015. Sorsa API puts the full author profile inside every tweet: POST /tweet-info-bulk reads 100 tweets per request from $0.014 per 1,000, and 100 free requests cover the first 10K.

Endpoint
POST /tweet-info-bulk
Page size
100 tweets per request
Price
from $0.014 per 1,000

#What Twitter API expansions are and what they return

An expansion is a request parameter of the official X API v2 that adds a related object, such as the author, a photo or a quoted post, to the response. Without one, a post lookup returns three fields: id, text and edit_history_tweet_ids. The author is not there, and not even the author's ID is.

With expansions=author_id, each post gains an author_id, and the response gains an includes.users list with one entry per author. By default that entry holds only id, name and username. Anything else, such as the bio, follower counts or the avatar, has to be named in user.fields.

The list below holds the post expansions developers reach for most, and what each one costs under the X API pricing that applies from November 22, 2026. Every user object costs twice as much as the post it came with.

ExpansionAdds to includesOfficial X API, per object from Nov 22
author_idThe post's author$0.010
referenced_tweets.idThe quoted, replied-to or reposted post$0.005
referenced_tweets.id.author_idThat post's author$0.010
in_reply_to_user_idThe account being replied to$0.010
entities.mentions.usernameEach mentioned account$0.010
attachments.media_keysEach photo, video or GIF$0.005
attachments.poll_idsThe poll$0.005
geo.place_idThe tagged place$0.005
  • author_id

    Adds to includes
    The post's author
    Official X API, per object from Nov 22
    $0.010
  • referenced_tweets.id

    Adds to includes
    The quoted, replied-to or reposted post
    Official X API, per object from Nov 22
    $0.005
  • referenced_tweets.id.author_id

    Adds to includes
    That post's author
    Official X API, per object from Nov 22
    $0.010
  • in_reply_to_user_id

    Adds to includes
    The account being replied to
    Official X API, per object from Nov 22
    $0.010
  • entities.mentions.username

    Adds to includes
    Each mentioned account
    Official X API, per object from Nov 22
    $0.010
  • attachments.media_keys

    Adds to includes
    Each photo, video or GIF
    Official X API, per object from Nov 22
    $0.005
  • attachments.poll_ids

    Adds to includes
    The poll
    Official X API, per object from Nov 22
    $0.005
  • geo.place_id

    Adds to includes
    The tagged place
    Official X API, per object from Nov 22
    $0.005

The rates come from X's expansions billing notice of October 23, 2026 (opens in a new tab), which says objects returned through expansions will be billed "at the standard PPU rate for that resource, the same as direct lookups." Before that date they cost nothing on top of the post.

#Get each tweet with its author in one call on Sorsa API

Sorsa API, a Twitter (X) API and scraper, returns every tweet with a user object inside it: the author's full public profile, 19 fields, at no extra cost. There is no expansion to add, no second list to join and no per-author charge. One request with your API key returns the tweets, and each one already knows who wrote it.

  1. Get a key. Sign up and copy the API key from your dashboard. No X developer account and no OAuth are involved.

  2. Send up to 100 tweet links or IDs. POST /tweet-info-bulk (opens in a new tab) takes a list of tweet URLs or bare IDs and returns them in one response, with no paging.

    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": ["1849212213712385091",
           "https://x.com/kai_builds_tools/status/1849212213712385092"]}'
    
    Response200 OK
    {
      "tweets": [
        {
          "id": "1849212213712385091",
          "full_text": "Shipped the new export queue today.",
          "created_at": "2026-10-24T09:12:44Z",
          "likes_count": 214,
          "retweet_count": 31,
          "view_count": 18402,
          "user": {
            "id": "1592041833471827968",
            "username": "kai_builds_tools",
            "display_name": "Kai Builds",
            "description": "Data tooling for small teams.",
            "followers_count": 1840,
            "followings_count": 312,
            "tweets_count": 2291,
            "verified": false,
            "created_at": "2022-11-14T08:03:10Z",
            "location": "Lisbon",
            "profile_image_url": "https://pbs.twimg.com/profile_images/1/kai.jpg"
          }
        }
      ]
    }
    
  3. Read the author straight from each tweet. The profile sits under tweet["user"], so a loop over the tweets is all the code there is.

    import requests
    
    URL = "https://api.sorsa.io/v3/tweet-info-bulk"
    HEADERS = {"ApiKey": "YOUR_API_KEY"}
    
    
    def tweets_with_authors(ids):
        for start in range(0, len(ids), 100):
            batch = ids[start:start + 100]
            r = requests.post(URL, headers=HEADERS,
                              json={"tweet_links": batch})
            r.raise_for_status()
            for tweet in r.json()["tweets"]:
                author = tweet["user"]
                yield (tweet["id"], author["username"],
                       author["followers_count"], tweet["full_text"])
    

A 403 means the key is missing or out of requests; a 429 means more than 20 requests in one second, so wait a moment and retry. Failed requests are never charged.

The same user object comes with tweets from search, timelines, replies and quotes. POST /search-tweets (opens in a new tab) returns about 20 tweets per page with their authors, searches public posts back to 2006 with X's search operators, and hands back a next_cursor for the next page until the cursor comes back empty.

Official X API v2 The author sits beside the post data[0] id: "1849…091" text: "Shipped the new…" author_id: "1592…968" join by id includes.users[0] id: "1592…968" name: "Kai Builds" username: "kai_bu…" $0.005 post + $0.010 author Sorsa API The author sits inside the tweet tweets[0] id: "1849…091" full_text: "Shipped the…" user: { username: "kai_builds…" display_name: "Kai Builds" followers_count: 1840 + 16 more fields } Author included, no extra cost
The official X API sends the author beside the post and bills it separately; Sorsa API puts it inside the tweet.

#Match posts to authors on the official X API

To match posts to authors on the official X API, build a dictionary of includes.users keyed by user ID, then look up each post's author_id in it. X's expansions guide (opens in a new tab) notes that objects in includes carry no position, so the ID is the only link between the two lists.

Terminal
curl -G "https://api.x.com/2/tweets" \
  -H "Authorization: Bearer YOUR_BEARER_TOKEN" \
  --data-urlencode "ids=1849212213712385091,1849212213712385092" \
  --data-urlencode "expansions=author_id" \
  --data-urlencode "user.fields=description,public_metrics,profile_image_url"
Response200 OK
{
  "data": [
    {
      "id": "1849212213712385091",
      "text": "Shipped the new export queue today.",
      "author_id": "1592041833471827968",
      "edit_history_tweet_ids": ["1849212213712385091"]
    },
    {
      "id": "1849212213712385092",
      "text": "Next up: retries with backoff.",
      "author_id": "1592041833471827968",
      "edit_history_tweet_ids": ["1849212213712385092"]
    }
  ],
  "includes": {
    "users": [
      {
        "id": "1592041833471827968",
        "name": "Kai Builds",
        "username": "kai_builds_tools",
        "description": "Data tooling for small teams.",
        "public_metrics": {"followers_count": 1840, "following_count": 312}
      }
    ]
  }
}

Two posts, one author: includes.users lists each author once per response, not once per post. That is why the list is often shorter than data, and why indexing it by position, or reading includes["users"]["username"] as if it were a single object, breaks. The loop below pages through recent search results and keeps one dictionary of authors across pages.

Python
import requests

URL = "https://api.x.com/2/tweets/search/recent"
HEADERS = {"Authorization": "Bearer YOUR_BEARER_TOKEN"}
params = {
    "query": "from:kai_builds_tools",
    "max_results": 100,
    "expansions": "author_id",
    "user.fields": "description,public_metrics,profile_image_url",
}

authors, rows = {}, []
while True:
    page = requests.get(URL, headers=HEADERS, params=params).json()
    for user in page.get("includes", {}).get("users", []):
        authors[user["id"]] = user
    for post in page.get("data", []):
        author = authors.get(post["author_id"], {})
        rows.append((post["id"], author.get("username"), post["text"]))
    token = page.get("meta", {}).get("next_token")
    if not token:
        break
    params["next_token"] = token

Use .get() on the author: an account that is suspended or withheld can be missing from includes while its post is still in data, and the response then lists the gap in an errors array.

The author of a retweet or a quote

A repost or quote is a post that points to another post, and its author_id is the person who reposted or quoted, not the original author. To get the original author too, request three expansions: author_id, referenced_tweets.id and referenced_tweets.id.author_id. The original post lands in includes.tweets, its author lands in includes.users beside the reposter, and you join them through referenced_tweets[0].id, then that post's author_id. From November 22 that one repost bills two posts and two users: $0.030.

On Sorsa API, the reposted tweet comes back as retweeted_status and the quoted one as quoted_status, each a full tweet with its own user inside.

#What expansions cost from November 22, 2026

From November 22, 2026, every object an expansion adds is billed like a direct read: $0.010 per user and $0.005 per post, photo, video, poll or place. A post read with its author therefore costs up to $0.015, three times the $0.005 it cost before. Add one photo per post and the post costs up to $0.020; add the quoted post and its author as well and it reaches $0.035.

X deduplicates within a 24-hour UTC day: per its pay-per-use pricing page (opens in a new tab), a resource fetched again the same day is not charged again. A user who wrote 50 of your posts that day is billed once. So 1,000 posts from 200 different authors cost $5.00 for the posts and $2.00 for the authors, $7.00 in all; 1,000 posts from 1,000 authors cost $15.00. The deduplication resets at midnight UTC, and X calls it a soft guarantee.

On Sorsa API the price does not change with what a tweet carries. Tweets by ID cost from $0.014 per 1,000 with their authors, quoted posts and media links inside; search results cost from $0.07 per 1,000. Against $15.00 per 1,000 posts with authors on the official X API, that is about 1,000 times less.

What you read, per 1,000 postsSorsa APIOfficial X API from Nov 22
Posts onlyfrom $0.014$5.00
Posts with their authorsfrom $0.014up to $15.00
Plus one photo or video eachfrom $0.014up to $20.00
Plus the quoted post and its authorfrom $0.014up to $35.00
  • Posts only

    Sorsa API
    from $0.014
    Official X API from Nov 22
    $5.00
  • Posts with their authors

    Sorsa API
    from $0.014
    Official X API from Nov 22
    up to $15.00
  • Plus one photo or video each

    Sorsa API
    from $0.014
    Official X API from Nov 22
    up to $20.00
  • Plus the quoted post and its author

    Sorsa API
    from $0.014
    Official X API from Nov 22
    up to $35.00

The calculator prices a month of posts with the expansions you use. It counts every author as new, the top of the range; pick repeat authors to see the effect of deduplication.

Posts a month
Expansions on the official X API
Authors
Official X API from Nov 22
up to $15,000
a month: $0.005 per post + $0.010 per author
Sorsa API
from $14
a month, tweets by ID, author inside each one

Each expansion adds its own charge on the official X API; on Sorsa API the author, quoted post and media links come inside the tweet.

Good to know

X's pay-per-use plan stops at 3 million post reads a month; past that, the official X API is an Enterprise contract from about $42,000 a month. On Sorsa API the price per 1,000 tweets falls as the plan grows, and the author is part of every tweet on every plan.

#Why the author is no longer inside the tweet

The author used to be part of the tweet. In the v1.1 format, every tweet object carried a user object with the author's profile, so one tweet was self-contained, as the twarc documentation (opens in a new tab) puts it. Version 2 of the API, released in 2020, cut each object to a few default fields and moved related objects into includes, reachable only through expansions.

Until now that split cost code but not money: objects returned through expansions did not count toward usage, so an expansion simply saved a second request. The October 2026 notice changed the money part. From November 22, the author you join from includes.users is billed as if you had looked it up on its own.

API v1.1 user inside every tweet Part of the post API v2, 2020 includes.users behind expansions=author_id Not counted in usage Nov 22, 2026 Each expanded author billed as a user read $0.010 per author Sorsa API: user inside every tweet, 19 fields, no extra cost
The author left the tweet in 2020 and became a separate charge in 2026; Sorsa API keeps it inside the tweet.

Sorsa API's tweet looks like the v1.1 one in this respect: user sits inside it. Code written for v1.1 that reads tweet["user"]["screen_name"] needs only a new key name, username; the guide to migrate from the Twitter/X API maps the rest of a v2 codebase endpoint by endpoint.

#Which author fields come with each Sorsa API tweet

Each Sorsa API tweet carries 19 author fields under user, with nothing to request. The table maps the ones most code needs to the official X API names you would list in user.fields.

Sorsa API user fieldOfficial X API user.fields name
usernameusername
display_namename
descriptiondescription
followers_countpublic_metrics.followers_count
followings_countpublic_metrics.following_count
tweets_countpublic_metrics.tweet_count
profile_image_urlprofile_image_url
created_atcreated_at
locationlocation
verifiedverified
protectedprotected
pinned_tweet_idspinned_tweet_id
bio_urlsentities.description.urls
  • username

    Official X API user.fields name
    username
  • display_name

    Official X API user.fields name
    name
  • description

    Official X API user.fields name
    description
  • followers_count

    Official X API user.fields name
    public_metrics.followers_count
  • followings_count

    Official X API user.fields name
    public_metrics.following_count
  • tweets_count

    Official X API user.fields name
    public_metrics.tweet_count
  • profile_image_url

    Official X API user.fields name
    profile_image_url
  • created_at

    Official X API user.fields name
    created_at
  • location

    Official X API user.fields name
    location
  • verified

    Official X API user.fields name
    verified
  • protected

    Official X API user.fields name
    protected
  • pinned_tweet_ids

    Official X API user.fields name
    pinned_tweet_id
  • bio_urls

    Official X API user.fields name
    entities.description.urls

The other six are id, favourites_count, media_count, profile_background_image_url, can_dm and possibly_sensitive. IDs come as strings and dates in ISO 8601. The full list of what tweets, profiles and other objects hold is in the guide to what data you can get from the Twitter API.

When your starting point is a list of tweet IDs, the guide to rehydrate tweet IDs covers lists of millions, 100 IDs per request.

#Read your first tweets with their authors

On the official X API, the author of a post is an expansion: a second list to join and, from November 22, 2026, a $0.010 charge per author on top of the $0.005 post. To get tweets with their authors in one piece, use Sorsa API: POST /tweet-info-bulk returns 100 tweets per request, each with its author's full profile inside, from $0.014 per 1,000, at 20 requests per second on every plan.

Sorsa API never asks for your X login, so your own account is never at risk, and it is read-only by design: to post, pair it with the official X API. Your 100 free requests read the first 10K tweets with their authors.

#FAQ

Does the official X API return the username with each tweet?

No, not by default. The official X API v2 returns a post with only id, text and edit_history_tweet_ids. Add expansions=author_id and the response gets an includes.users list with each author's id, name and username, which you match to posts by author_id. From November 22, 2026 each of those authors is billed $0.010.

How do I get the original author of a retweet or quote tweet?

On the official X API, request expansions=referenced_tweets.id,referenced_tweets.id.author_id. The original post appears in includes.tweets and its author in includes.users; follow referenced_tweets[0].id to the post, then its author_id to the user. On Sorsa API, a reposted tweet comes back as retweeted_status and a quoted one as quoted_status, each with its own user object inside.

Are Twitter API expansions billed?

Yes, from November 22, 2026. X's notice of October 23 says objects returned through expansions are billed at the standard pay-per-use rate: $0.010 per user and $0.005 per post, media item, poll or place. Each resource is charged once per 24-hour UTC day, so a repeat author on the same day is not billed again.

Why does includes.users have fewer items than data?

The official X API lists each author once per response, however many of the posts they wrote. Ten posts by three people return three users. An author can also be missing when the account is suspended or withheld; the response then names it in an errors array. Join by ID with a dictionary, never by position.

Which API should I use to get tweets with author profiles?

Use Sorsa API. Every tweet it returns carries its author's full profile, 19 fields, with no expansion and no per-author charge: POST /tweet-info-bulk reads 100 tweets per request from $0.014 per 1,000, against up to $15.00 per 1,000 posts with authors on the official X API. One API key, 20 requests per second on every plan, and 100 free requests to start.

#How we checked

On November 2, 2026 we read X's expansions and fields guides, its pay-per-use pricing page and its October 23 notice on billing expansions, and took every official X API rate and default from them. The Sorsa API endpoints, request bodies and the 19 author fields come from the live API reference of the same day. The request and response examples follow both references, with demo accounts in place of real ones.

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
  • First published.