Skip to main content

Migration Guide: Twitter/X API to Sorsa API

Moving from the official Twitter/X API to Sorsa API? This guide covers everything you need to know - from authentication and endpoint mapping to response format changes and code examples.

Why Migrate?

The official Twitter/X API (v2) comes with complex OAuth flows, strict rate limits tied to expensive access tiers, and frequent policy changes. Sorsa API provides a simpler alternative:
  • Simple API key auth instead of OAuth 1.0a/2.0 flows
  • Flat, predictable pricing based on request count
  • Richer default responses - no need to specify expansions or tweet.fields
  • Extra features not available in the official API (Sorsa Score, follower category stats, community tools, verification checks)

Quick Comparison

FeatureTwitter/X API v2Sorsa API v3
Base URLhttps://api.x.com/2https://api.sorsa.io/v3
AuthOAuth 1.0a / OAuth 2.0 BearerAPI key via ApiKey header
Rate limitsPer-endpoint, per-15-min windowRequest-based quota
Response formatNested data + includes + metaFlat objects, direct fields
Field selectionRequires tweet.fields, user.fields, expansionsAll fields returned by default
Paginationpagination_token in query paramsnext_cursor in request body or query
Pricing tiersFree / Basic / Pro / EnterprisePay-per-request packages

Step 1: Authentication

Before (Twitter/X API)

Twitter requires OAuth 2.0 Bearer tokens or OAuth 1.0a for user-context requests. Typical setup:
# OAuth 2.0 App-Only
curl -X GET "https://api.x.com/2/users/by/username/elonmusk" \
  -H "Authorization: Bearer AAAA...your_bearer_token"
For user-context endpoints you need a full OAuth 1.0a dance with consumer keys, access tokens, and signature generation.

After (Sorsa API)

A single API key in the ApiKey header. That’s it.
curl -X GET "https://api.sorsa.io/v3/info?username=elonmusk" \
  -H "ApiKey: your_api_key"
No token refresh, no OAuth flows, no signature computation. Get your API key: Log in at api.sorsa.io/overview/keys to create and manage keys.

Step 2: Endpoint Mapping

Users

ActionTwitter/X API v2Sorsa API v3
Get user profileGET /2/users/by/username/:usernameGET /info?username=:username
Get user by IDGET /2/users/:idGET /info?user_id=:id
Get multiple usersGET /2/users?ids=...GET /info-batch?user_ids=...
Get followersGET /2/users/:id/followersGET /followers?user_id=:id
Get followingGET /2/users/:id/followingGET /follows?user_id=:id
Account metadataNot availableGET /about?username=:username

Tweets

ActionTwitter/X API v2Sorsa API v3
Get tweet by IDGET /2/tweets/:idPOST /tweet-info { "tweet_link": ":id" }
Get multiple tweetsGET /2/tweets?ids=...POST /tweet-info-bulk { "tweet_links": [...] }
User timelineGET /2/users/:id/tweetsPOST /user-tweets { "user_id": ":id" }
Quote tweetsGET /2/tweets/:id/quote_tweetsPOST /quotes { "tweet_link": "..." }
RetweetersGET /2/tweets/:id/retweeted_byPOST /retweets { "tweet_link": "..." }
Replies/commentsNot a dedicated endpoint (use search)POST /comments { "tweet_link": "..." }
Article (long-form)Not availablePOST /article { "tweet_link": "..." }
ActionTwitter/X API v2Sorsa API v3
Search tweetsGET /2/tweets/search/recent?query=...POST /search-tweets { "query": "..." }
Search usersNot available in v2 BasicPOST /search-users { "query": "..." }
Search mentionsUse query=@username in searchPOST /mentions { "query": "username" }

Lists

ActionTwitter/X API v2Sorsa API v3
List membersGET /2/lists/:id/membersGET /list-members?list_id=:id
List followersGET /2/lists/:id/followersGET /list-followers?list_link=:id
List tweetsGET /2/lists/:id/tweetsGET /list-tweets?list_id=:id

ID Conversion (Sorsa-only utilities)

ActionSorsa API v3
Username to IDGET /username-to-id/:handle
ID to usernameGET /id-to-username/:id
Profile link to IDGET /link-to-id?link=...
Check API quotaGET /key-usage-info

Verification (Sorsa-only)

These endpoints have no equivalent in the Twitter/X API - you would need to fetch full lists and check manually.
ActionSorsa API v3
Check if user follows accountPOST /check-follow
Check if user commented on tweetGET /check-comment
Check if user quoted/retweetedPOST /check-quoted
Check if user retweetedPOST /check-retweet
Check community membershipPOST /check-community-member

Sorsa Score & Analytics (Sorsa-only)

ActionSorsa API v3
Account influence scoreGET /score?username=...
Score changes (7d / 30d)GET /score-changes?username=...
Follower breakdown by categoryGET /followers-stats?username=...
Top 20 followers by scoreGET /top-followers?username=...
Top 20 following by scoreGET /top-following?username=...
New followers (last 7 days)GET /new-followers-7d?username=...
New following (last 7 days)GET /new-following-7d?username=...

Step 3: Response Format Changes

This is the biggest change you will need to handle in your code. Twitter/X API v2 splits data across data, includes, and meta objects. Sorsa returns everything in a flat structure.

User Profile

Twitter/X API v2:
{
  "data": {
    "id": "44196397",
    "name": "Elon Musk",
    "username": "elonmusk",
    "public_metrics": {
      "followers_count": 100000000,
      "following_count": 500,
      "tweet_count": 30000,
      "listed_count": 12000
    },
    "verified": false,
    "profile_image_url": "https://pbs.twimg.com/..."
  }
}
Sorsa API:
{
  "id": "44196397",
  "display_name": "Elon Musk",
  "username": "elonmusk",
  "description": "...",
  "location": "...",
  "followers_count": 100000000,
  "followings_count": 500,
  "tweets_count": 30000,
  "favourites_count": 50000,
  "media_count": 1200,
  "verified": false,
  "protected": false,
  "can_dm": true,
  "profile_image_url": "https://pbs.twimg.com/...",
  "profile_background_image_url": "...",
  "bio_urls": ["https://example.com"],
  "pinned_tweet_ids": ["17823..."],
  "possibly_sensitive": false,
  "created_at": "2009-06-02T00:00:00Z"
}
Key differences:
  • name becomes display_name
  • No nested public_metrics object - counts are top-level fields
  • following_count becomes followings_count
  • tweet_count becomes tweets_count
  • Additional fields: can_dm, favourites_count, media_count, bio_urls, pinned_tweet_ids

Tweet Data

Twitter/X API v2 (with expansions):
{
  "data": {
    "id": "1234567890",
    "text": "Hello world",
    "created_at": "2024-01-15T12:00:00.000Z",
    "public_metrics": {
      "retweet_count": 100,
      "reply_count": 50,
      "like_count": 500,
      "quote_count": 25,
      "bookmark_count": 10,
      "impression_count": 50000
    },
    "author_id": "44196397",
    "conversation_id": "1234567890",
    "in_reply_to_user_id": "...",
    "lang": "en"
  },
  "includes": {
    "users": [
      { "id": "44196397", "name": "Elon Musk", "username": "elonmusk" }
    ]
  }
}
Sorsa API:
{
  "id": "1234567890",
  "full_text": "Hello world",
  "created_at": "2024-01-15T12:00:00.000Z",
  "likes_count": 500,
  "retweet_count": 100,
  "reply_count": 50,
  "quote_count": 25,
  "bookmark_count": 10,
  "view_count": 50000,
  "lang": "en",
  "is_reply": false,
  "is_quote_status": false,
  "is_replies_limited": false,
  "conversation_id_str": "1234567890",
  "in_reply_to_tweet_id": null,
  "in_reply_to_username": null,
  "user": {
    "id": "44196397",
    "display_name": "Elon Musk",
    "username": "elonmusk",
    "followers_count": 100000000
  },
  "entities": [],
  "quoted_status": null,
  "retweeted_status": null
}
Key differences:
  • text becomes full_text
  • No nested public_metrics - counts are top-level
  • like_count becomes likes_count
  • impression_count becomes view_count
  • Author data is embedded in user (no separate includes block)
  • Quoted and retweeted tweets are nested directly in quoted_status / retweeted_status
  • Boolean flags: is_reply, is_quote_status, is_replies_limited
  • in_reply_to_username instead of in_reply_to_user_id

Step 4: Pagination

Before (Twitter/X API)

GET /2/users/44196397/followers?max_results=100&pagination_token=abc123
Token is in the meta object of the response:
{
  "data": [...],
  "meta": {
    "next_token": "abc123",
    "result_count": 100
  }
}

After (Sorsa API)

For GET endpoints, pass cursor as a query parameter:
GET /followers?username=elonmusk&cursor=abc123
For POST endpoints, include next_cursor in the request body:
{
  "query": "from:elonmusk",
  "next_cursor": "abc123"
}
The cursor is returned at the top level of the response:
{
  "tweets": [...],
  "next_cursor": "xyz789"
}
When next_cursor is empty or absent, you have reached the last page.

Step 5: Code Migration Examples

Python - Get User Profile

Before:
import requests

BEARER_TOKEN = "AAAA...your_bearer_token"

url = "https://api.x.com/2/users/by/username/elonmusk"
params = {
    "user.fields": "description,public_metrics,profile_image_url,verified,created_at"
}
headers = {"Authorization": f"Bearer {BEARER_TOKEN}"}

response = requests.get(url, headers=headers, params=params)
data = response.json()

user = data["data"]
followers = user["public_metrics"]["followers_count"]
name = user["name"]
After:
import requests

API_KEY = "your_api_key"

url = "https://api.sorsa.io/v3/info"
params = {"username": "elonmusk"}
headers = {"ApiKey": API_KEY}

response = requests.get(url, headers=headers, params=params)
user = response.json()

followers = user["followers_count"]
name = user["display_name"]

Python - Search Tweets

Before:
url = "https://api.x.com/2/tweets/search/recent"
params = {
    "query": "from:elonmusk since:2024-01-01",
    "tweet.fields": "created_at,public_metrics,lang",
    "expansions": "author_id",
    "user.fields": "username,name",
    "max_results": 10
}
headers = {"Authorization": f"Bearer {BEARER_TOKEN}"}

response = requests.get(url, headers=headers, params=params)
data = response.json()

tweets = data["data"]
users = {u["id"]: u for u in data.get("includes", {}).get("users", [])}
next_token = data.get("meta", {}).get("next_token")
After:
url = "https://api.sorsa.io/v3/search-tweets"
payload = {
    "query": "from:elonmusk since:2024-01-01"
}
headers = {"ApiKey": API_KEY}

response = requests.post(url, headers=headers, json=payload)
data = response.json()

tweets = data["tweets"]        # each tweet already includes user data
next_cursor = data.get("next_cursor")

JavaScript - Get Tweet Info

Before:
const tweetId = "1234567890";

const response = await fetch(
  `https://api.x.com/2/tweets/${tweetId}?tweet.fields=public_metrics,created_at&expansions=author_id&user.fields=username,name`,
  { headers: { Authorization: `Bearer ${BEARER_TOKEN}` } }
);
const { data, includes } = await response.json();

const likes = data.public_metrics.like_count;
const author = includes.users[0].name;
After:
const response = await fetch("https://api.sorsa.io/v3/tweet-info", {
  method: "POST",
  headers: {
    "ApiKey": API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    tweet_link: "https://x.com/elonmusk/status/1234567890",
  }),
});
const tweet = await response.json();

const likes = tweet.likes_count;
const author = tweet.user.display_name;

JavaScript - Paginate Through Followers

Before:
let paginationToken = null;
let allFollowers = [];

do {
  const params = new URLSearchParams({ max_results: "100" });
  if (paginationToken) params.set("pagination_token", paginationToken);

  const res = await fetch(
    `https://api.x.com/2/users/44196397/followers?${params}`,
    { headers: { Authorization: `Bearer ${BEARER_TOKEN}` } }
  );
  const json = await res.json();

  allFollowers.push(...(json.data || []));
  paginationToken = json.meta?.next_token || null;
} while (paginationToken);
After:
let cursor = null;
let allFollowers = [];

do {
  const params = new URLSearchParams({ user_id: "44196397" });
  if (cursor) params.set("cursor", cursor);

  const res = await fetch(
    `https://api.sorsa.io/v3/followers?${params}`,
    { headers: { ApiKey: API_KEY } }
  );
  const json = await res.json();

  allFollowers.push(...(json.users || []));
  cursor = json.next_cursor || null;
} while (cursor);

Step 6: Field Name Mapping Reference

Use this table to update your data models and parsers.

User Fields

Twitter/X API v2Sorsa APINotes
ididSame
usernameusernameSame
namedisplay_nameRenamed
descriptiondescriptionSame
locationlocationSame
public_metrics.followers_countfollowers_countFlattened
public_metrics.following_countfollowings_countFlattened + renamed
public_metrics.tweet_counttweets_countFlattened + renamed
public_metrics.listed_count-Not available
-favourites_countSorsa-only
-media_countSorsa-only
verifiedverifiedSame
protectedprotectedSame
profile_image_urlprofile_image_urlSame
-profile_background_image_urlSorsa-only
-can_dmSorsa-only
-bio_urlsSorsa-only
-pinned_tweet_idsSorsa-only
-possibly_sensitiveSorsa-only
created_atcreated_atSame

Tweet Fields

Twitter/X API v2Sorsa APINotes
ididSame
textfull_textRenamed
created_atcreated_atSame
public_metrics.like_countlikes_countFlattened + renamed
public_metrics.retweet_countretweet_countFlattened
public_metrics.reply_countreply_countFlattened
public_metrics.quote_countquote_countFlattened
public_metrics.bookmark_countbookmark_countFlattened
public_metrics.impression_countview_countFlattened + renamed
langlangSame
conversation_idconversation_id_strRenamed
in_reply_to_user_idin_reply_to_usernameReturns handle instead of ID
-in_reply_to_tweet_idSorsa-only
-is_replySorsa-only boolean flag
-is_quote_statusSorsa-only boolean flag
-is_replies_limitedSorsa-only boolean flag
author_id (+ includes.users)user (embedded object)Full user object inline
Referenced tweets via includesquoted_status, retweeted_statusNested directly
entities (urls, mentions, etc.)entities (type, link, preview)Different structure

Step 7: HTTP Method Changes

Some endpoints that are GET in the Twitter/X API are POST in Sorsa. Update your HTTP clients accordingly.
EndpointTwitter/X APISorsa API
Get tweetGETPOST
Search tweetsGETPOST
User timelineGETPOST
Quote tweetsGETPOST
RetweetersGETPOST
GET endpoints in Sorsa use query parameters. POST endpoints use JSON request body.

Step 8: Search Query Syntax

Sorsa supports the same Advanced Search syntax as Twitter/X. Your existing search queries will work as-is.
OperatorExampleWorks in Sorsa?
from:from:elonmuskYes
to:to:elonmuskYes
since: / until:since:2024-01-01 until:2024-02-01Yes
Hashtags#bitcoinYes
Exact phrase"hello world"Yes
ORbitcoin OR ethereumYes
Exclude-retweetsYes
For the full list of supported operators, see Search Operators. The Mentions endpoint (POST /mentions) also supports additional filters not available in the official API: min_likes, min_replies, min_retweets, and date range via since_date / until_date.

Step 9: Error Handling

Both APIs return JSON error messages, but the format is different. Twitter/X API v2:
{
  "errors": [
    {
      "message": "Not Found",
      "type": "https://api.x.com/2/problems/resource-not-found",
      "title": "Not Found Error",
      "detail": "Could not find tweet with id: [123].",
      "status": 404
    }
  ]
}
Sorsa API:
{
  "message": "Error description"
}
Sorsa uses a simpler format. Error codes are the same across all endpoints: 400 (bad request), 403 (forbidden / invalid API key), 404 (not found), 500 (server error).

Migration Checklist

  • [ ] Replace OAuth Bearer / OAuth 1.0a with ApiKey header
  • [ ] Update base URL from https://api.x.com/2 to https://api.sorsa.io/v3
  • [ ] Change endpoint paths (see mapping tables above)
  • [ ] Switch GET to POST where required (tweets, search, communities)
  • [ ] Remove tweet.fields, user.fields, and expansions params (Sorsa returns all fields)
  • [ ] Update response parsing: remove data / includes / meta unwrapping
  • [ ] Rename fields in your data models (name to display_name, text to full_text, etc.)
  • [ ] Flatten metric access (remove public_metrics nesting)
  • [ ] Update pagination: replace pagination_token / next_token with cursor / next_cursor
  • [ ] Update error handling for the simplified error format
  • [ ] Test with the API Playground before deploying
  • [ ] Monitor your quota with GET /key-usage-info

Features Only Available in Sorsa

These have no equivalent in the official Twitter/X API:
FeatureEndpointDescription
Sorsa ScoreGET /scoreInfluence score based on who follows the account
Score TrendsGET /score-changesWeekly and monthly score deltas
Follower CategoriesGET /followers-statsBreakdown by influencers, projects, VCs
Top FollowersGET /top-followersTop 20 followers ranked by influence
New Followers (7d)GET /new-followers-7dRecent follower gains
Verification ChecksPOST /check-follow, etc.Instant follow/comment/retweet verification
Community ToolsPOST /community-tweets, etc.Community feeds, members, search
Verified FollowersGET /verified-followersFilter for verified followers only
Account AboutGET /aboutCountry, username change history
Article DataPOST /articleFull text of long-form X articles

Resources