Skip to main content

Audience Geography

How to retrieve the country associated with any public X account and aggregate audience geography data from a follower list.
Note: For a fuller walkthrough with worked examples and country-distribution analysis, see Twitter Audience Geography API: Analyze Followers by Country on the blog.

The /about Endpoint

Returns the country, username change count, and last username change date for any public X account.

Request

curl "https://api.sorsa.io/v3/about?username=elonmusk" \
  -H "ApiKey: YOUR_API_KEY"
import requests

resp = requests.get(
    "https://api.sorsa.io/v3/about",
    headers={"ApiKey": "YOUR_API_KEY"},
    params={"username": "elonmusk"},
)
print(resp.json())

Parameters

ParameterTypeRequiredDescription
usernamestringOne of threeHandle without @.
user_idstringOne of threeNumeric user ID.
user_linkstringOne of threeFull profile URL.

Response

{
  "country": "United States",
  "username_change_count": 1,
  "last_username_change_at": "2021-01-01T00:00:00Z"
}

Response fields

FieldTypeDescription
countrystringCountry associated with the account, derived from platform-level signals (not the bio “Location” field). Returns "Unknown" when the platform has insufficient data.
username_change_countintegerTotal number of handle changes on the account.
last_username_change_atstring (ISO 8601)Timestamp of the most recent handle change. null if the handle has never changed.

Audience Geography Workflow

To build a country distribution for any account’s audience:
  1. Extract the follower list with GET /v3/followers.
  2. Call GET /v3/about for each follower using their user_id.
  3. Aggregate the country values into a distribution.
import requests
import time
from collections import Counter

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.sorsa.io/v3"
HEADERS = {"ApiKey": API_KEY}


def get_followers(username, max_pages=10):
    followers, cursor = [], None
    for _ in range(max_pages):
        params = {"username": username}
        if cursor:
            params["next_cursor"] = cursor
        resp = requests.get(f"{BASE_URL}/followers", headers=HEADERS, params=params, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        followers.extend(data.get("users", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)
    return followers


def get_country(user_id):
    resp = requests.get(
        f"{BASE_URL}/about",
        headers=HEADERS,
        params={"user_id": user_id},
        timeout=15,
    )
    if resp.status_code == 200:
        return resp.json().get("country") or "Unknown"
    return "Unknown"


def audience_geography(username, max_follower_pages=5):
    followers = get_followers(username, max_pages=max_follower_pages)
    countries = Counter()
    for user in followers:
        countries[get_country(user["id"])] += 1
        time.sleep(0.05)  # stay within 20 req/s
    return countries, len(followers)

Cost

Each /about call counts as one request against your quota. For a 1,000-follower sample: approximately 1,005 requests (1,000 country lookups + 5 follower-list pages). See Pricing for per-plan rates.

Exporting to CSV

import csv

def export_to_csv(countries, total, path="geography.csv"):
    with open(path, "w", newline="") as f:
        w = csv.writer(f)
        w.writerow(["country", "count", "percentage"])
        for country, count in countries.most_common():
            w.writerow([country, count, round(count / total * 100, 2)])

Data Accuracy

The country field is derived from platform-level technical signals, not from the user-reported bio “Location” field. This makes it more reliable for aggregate analysis, but with two caveats:
  • A small percentage of users access X via VPN. Their assigned country reflects the VPN exit node. For sample sizes of 500 or more, this noise averages out and the distribution is accurate within a few percentage points.
  • Some accounts return "Unknown". Expect 1-5% in a healthy organic audience; a share above 15-20% in a specific cohort (e.g. recent followers after a spike) is a signal worth investigating.