Skip to main content
Converting Between Twitter Usernames, User IDs, and Profile Links Every X (formerly Twitter) account has two identities: a username (handle) like @elonmusk that can be changed at any time, and a User ID like 44196397 that is permanent for the lifetime of the account. If you are building anything that stores or references X accounts - a database, a monitoring system, a CRM integration, an analytics pipeline - you need to work with User IDs, because they never change even when users rebrand their handle. Sorsa API provides three utility endpoints for instant conversion between these formats. This page documents all three, explains when and why you need them, and includes code for batch conversion.
No-code option: For one-off conversions, use the free Sorsa ID Converter web tool. Paste a username, ID, or profile URL and get the result instantly, no API key required.

Why User IDs Matter

Usernames are human-readable but unreliable as identifiers. A user can change their handle from @old_name to @new_name at any time, and someone else can then claim @old_name. If your system stores usernames as primary keys, a single handle change breaks every reference to that account. User IDs are 64-bit Snowflake integers assigned at account creation. They never change, cannot be transferred, and are unique across the entire platform. Building your data layer around User IDs means:
  • Handle changes do not break your system. Your database still points to the same account regardless of how many times they rename.
  • Database performance improves. Integer indexing is faster and more storage-efficient than variable-length strings.
  • Cross-referencing is reliable. When matching accounts across datasets, exports, or API responses from different time periods, IDs are the only stable join key.
  • Some Sorsa endpoints accept only IDs. Endpoints like /info-batch accept user_ids, and several community/list endpoints reference accounts by numeric ID. Having IDs on hand avoids extra conversion calls.

Endpoint 1: Username to User ID

GET /v3/username-to-id/{user_handle}
Converts a handle (without @) into the permanent numeric User ID.

Example

curl "https://api.sorsa.io/v3/username-to-id/elonmusk" \
  -H "ApiKey: YOUR_API_KEY"
{"id": "44196397"}

Python

import requests

API_KEY = "YOUR_API_KEY"

def username_to_id(handle):
    resp = requests.get(
        f"https://api.sorsa.io/v3/username-to-id/{handle}",
        headers={"ApiKey": API_KEY},
    )
    resp.raise_for_status()
    return resp.json()["id"]

print(username_to_id("elonmusk"))  # "44196397"

JavaScript

async function usernameToId(handle) {
  const resp = await fetch(
    `https://api.sorsa.io/v3/username-to-id/${handle}`,
    { headers: { "ApiKey": "YOUR_API_KEY" } }
  );
  return (await resp.json()).id;
}

console.log(await usernameToId("elonmusk")); // "44196397"

Endpoint 2: User ID to Username

GET /v3/id-to-username/{user_id}
Resolves a numeric User ID back to the account’s current handle. Essential for making stored IDs human-readable again, or for detecting that an account has changed its name since you last looked.

Example

curl "https://api.sorsa.io/v3/id-to-username/44196397" \
  -H "ApiKey: YOUR_API_KEY"
{"handle": "elonmusk"}

Python

def id_to_username(user_id):
    resp = requests.get(
        f"https://api.sorsa.io/v3/id-to-username/{user_id}",
        headers={"ApiKey": API_KEY},
    )
    resp.raise_for_status()
    return resp.json()["handle"]

print(id_to_username("44196397"))  # "elonmusk"

GET /v3/link-to-id?link={profile_url}
Extracts the permanent User ID from a full profile URL. Useful when you are processing links from spreadsheets, browser bookmarks, or scraped web pages and need to normalize them into IDs.

Example

curl "https://api.sorsa.io/v3/link-to-id?link=https://x.com/elonmusk" \
  -H "ApiKey: YOUR_API_KEY"
{"id": "44196397"}

Python

def link_to_id(profile_url):
    resp = requests.get(
        "https://api.sorsa.io/v3/link-to-id",
        headers={"ApiKey": API_KEY},
        params={"link": profile_url},
    )
    resp.raise_for_status()
    return resp.json()["id"]

print(link_to_id("https://x.com/elonmusk"))  # "44196397"

Batch Conversion: Usernames to IDs

When you have a list of handles (from a spreadsheet, a CRM export, or a competitor list) and need to convert them all to IDs for storage:
import requests
import time

API_KEY = "YOUR_API_KEY"

def batch_username_to_id(handles):
    """Convert a list of usernames to User IDs."""
    results = {}
    for handle in handles:
        try:
            resp = requests.get(
                f"https://api.sorsa.io/v3/username-to-id/{handle}",
                headers={"ApiKey": API_KEY},
            )
            if resp.status_code == 200:
                results[handle] = resp.json()["id"]
            else:
                results[handle] = None
                print(f"  Could not resolve @{handle} (HTTP {resp.status_code})")
        except Exception as e:
            results[handle] = None
            print(f"  Error for @{handle}: {e}")
        time.sleep(0.05)

    return results


handles = ["NASA", "SpaceX", "Tesla", "OpenAI", "stripe"]
id_map = batch_username_to_id(handles)

for handle, uid in id_map.items():
    print(f"@{handle} -> {uid}")

Batch Conversion: IDs to Current Usernames

The reverse operation - resolving a list of stored IDs back to current handles. Useful for refreshing your database after accounts may have renamed:
def batch_id_to_username(user_ids):
    """Resolve a list of User IDs to their current usernames."""
    results = {}
    for uid in user_ids:
        try:
            resp = requests.get(
                f"https://api.sorsa.io/v3/id-to-username/{uid}",
                headers={"ApiKey": API_KEY},
            )
            if resp.status_code == 200:
                results[uid] = resp.json()["handle"]
            elif resp.status_code == 404:
                results[uid] = None  # Account deleted or suspended
            else:
                results[uid] = None
        except Exception as e:
            results[uid] = None
        time.sleep(0.05)

    return results


stored_ids = ["44196397", "1934538036466810880", "11348282"]
current_handles = batch_id_to_username(stored_ids)

for uid, handle in current_handles.items():
    status = f"@{handle}" if handle else "not found (deleted/suspended)"
    print(f"ID {uid} -> {status}")

Common Patterns

Detecting Handle Changes

If you store both the User ID and the username at the time of collection, you can periodically re-resolve the IDs and detect renames:
# Your stored data
db_records = [
    {"user_id": "44196397", "stored_handle": "elonmusk"},
    {"user_id": "1234567890", "stored_handle": "old_brand_name"},
]

for record in db_records:
    current = id_to_username(record["user_id"])
    if current and current != record["stored_handle"]:
        print(f"Handle changed: @{record['stored_handle']} is now @{current}")
    time.sleep(0.05)

Normalizing Mixed Input

When users submit account references in different formats (some as handles, some as URLs, some as IDs), normalize everything to User IDs:
def normalize_to_id(input_string):
    """Accept a username, user ID, or profile URL and return the User ID."""
    input_string = input_string.strip().lstrip("@")

    # Already a numeric ID
    if input_string.isdigit():
        return input_string

    # Profile URL
    if "x.com/" in input_string or "twitter.com/" in input_string:
        return link_to_id(input_string)

    # Username
    return username_to_id(input_string)


# All of these return the same ID
print(normalize_to_id("elonmusk"))
print(normalize_to_id("@elonmusk"))
print(normalize_to_id("https://x.com/elonmusk"))
print(normalize_to_id("44196397"))

Next Steps

  • Followers and Following - many workflows start with ID conversion, then proceed to follower extraction.
  • User Profile Data - use the /info endpoint with a User ID to get the full profile.
  • Audience Geography - the /about endpoint also accepts user_id as input.
  • API Reference - full specification for /username-to-id, /id-to-username, /link-to-id, and all 38 endpoints.