Skip to main content
How to Analyze Twitter Audience Geography and Country Data via API On X (formerly Twitter), you can see your own account’s audience demographics through the built-in analytics dashboard - but you cannot see anyone else’s. There is no native way to find out which countries a competitor’s followers come from, whether an influencer’s audience is actually in the market they claim to reach, or how a brand’s geographic footprint compares to yours. The Sorsa API /about endpoint solves this. It returns the country associated with any public X account - metadata derived from the platform’s technical signals, not from the self-reported “Location” field in the user’s bio (which is often empty, jokey, or misleading). By combining /about with the /followers endpoint, you can build a complete geographic profile of any account’s audience: extract the follower list, then look up the country for each follower, and aggregate the results into a country breakdown. This is valuable for market expansion research, ad targeting validation, influencer vetting, bot farm detection, and academic study of how online communities distribute geographically.

The /about Endpoint

Simplest Example

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())

Response

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

Parameters (query string)

ParameterTypeRequiredDescription
usernamestringOne ofThe handle without @.
user_idstringtheseThe numeric user ID.
user_linkstringthreeFull profile URL.

What the Fields Mean

country - The country associated with the account. This is derived from platform-level signals, not from the bio “Location” field. It is more reliable than bio text but not perfect - a small percentage of users use VPNs, which can shift their detected country. For aggregate analysis (hundreds or thousands of accounts), VPN noise averages out and the overall distribution is accurate. username_change_count - How many times the account has changed its handle. Useful for detecting rebranded accounts, acquired handles, or suspicious accounts that change names frequently. last_username_change_at - Timestamp of the most recent handle change. Combined with the count, this helps you assess account stability.

The Core Workflow: Audience Geography Audit

To analyze the geographic distribution of any account’s audience, the process is two steps:
  1. Extract followers using GET /v3/followers (see Followers and Following Guide).
  2. Look up country for each follower using GET /v3/about.
  3. Aggregate the country data into a distribution.

Python: Full Audience Geography Analysis

import requests
import time
from collections import Counter

API_KEY = "YOUR_API_KEY"


def get_followers(username, max_pages=10):
    """Fetch followers of an account (simplified - see Followers guide for full version)."""
    all_users = []
    cursor = None

    for page in range(max_pages):
        params = {"username": username}
        if cursor:
            params["cursor"] = cursor

        resp = requests.get(
            "https://api.sorsa.io/v3/followers",
            headers={"ApiKey": API_KEY},
            params=params,
        )
        resp.raise_for_status()
        data = resp.json()

        all_users.extend(data.get("users", []))
        cursor = data.get("next_cursor")
        if not cursor:
            break
        time.sleep(0.1)

    return all_users


def get_country(user_id):
    """Look up the country for a single user via /about."""
    resp = requests.get(
        "https://api.sorsa.io/v3/about",
        headers={"ApiKey": API_KEY},
        params={"user_id": user_id},
    )
    if resp.status_code == 200:
        return resp.json().get("country", "Unknown")
    return "Unknown"


def analyze_audience_geography(username, max_follower_pages=5):
    """Build a country distribution for an account's followers."""
    print(f"Step 1: Extracting followers of @{username}...")
    followers = get_followers(username, max_pages=max_follower_pages)
    print(f"  Collected {len(followers)} followers.\n")

    print(f"Step 2: Looking up countries for {len(followers)} accounts...")
    countries = Counter()

    for i, user in enumerate(followers):
        country = get_country(user["id"])
        countries[country] += 1

        if (i + 1) % 50 == 0:
            print(f"  Processed {i + 1}/{len(followers)}...")
        time.sleep(0.05)  # Stay within 20 req/s

    return countries, len(followers)


# Run the analysis
country_dist, total = analyze_audience_geography("competitor_handle", max_follower_pages=5)

print(f"\n{'='*40}")
print(f"Audience Geography: @competitor_handle")
print(f"Total followers analyzed: {total}")
print(f"{'='*40}")

for country, count in country_dist.most_common(15):
    pct = count / total * 100
    bar = "#" * int(pct / 2)
    print(f"  {country:<25} {count:>5}  ({pct:>5.1f}%)  {bar}")

Sample Output

========================================
Audience Geography: @competitor_handle
Total followers analyzed: 842
========================================
  United States               287  ( 34.1%)  #################
  India                       124  ( 14.7%)  #######
  United Kingdom               68  (  8.1%)  ####
  Nigeria                      54  (  6.4%)  ###
  Canada                       41  (  4.9%)  ##
  Germany                      38  (  4.5%)  ##
  Brazil                       31  (  3.7%)  #
  France                       27  (  3.2%)  #
  Turkey                       22  (  2.6%)  #
  Indonesia                    19  (  2.3%)  #
  Unknown                      15  (  1.8%)
  ...

Comparing Audience Geography Across Competitors

The real power comes from running the same analysis on multiple accounts and comparing the results. This reveals which markets each competitor dominates and where untapped opportunities exist for your brand.
def compare_geography(handles, max_follower_pages=3):
    """Run geography analysis on multiple accounts and compare."""
    results = {}

    for handle in handles:
        print(f"\nAnalyzing @{handle}...")
        countries, total = analyze_audience_geography(handle, max_follower_pages)
        results[handle] = {"countries": countries, "total": total}

    # Print comparison for top countries
    all_countries = set()
    for r in results.values():
        all_countries.update(r["countries"].keys())

    # Find the top 10 countries across all accounts
    combined = Counter()
    for r in results.values():
        combined.update(r["countries"])
    top_countries = [c for c, _ in combined.most_common(10)]

    # Print comparison table
    header = f"{'Country':<20}" + "".join(f"@{h:<15}" for h in handles)
    print(f"\n{'='*len(header)}")
    print(header)
    print(f"{'='*len(header)}")

    for country in top_countries:
        row = f"{country:<20}"
        for handle in handles:
            total = results[handle]["total"]
            count = results[handle]["countries"].get(country, 0)
            pct = count / total * 100 if total > 0 else 0
            row += f"{pct:>6.1f}%{'':9}"
        print(row)


compare_geography(["your_brand", "competitor1", "competitor2"])
This tells you, for example, that your competitor has 34% of their audience in the US while you have only 12% - a signal that your US marketing efforts need attention, or conversely, that you have strength in markets they have not penetrated.

Practical Use Cases

Influencer Vetting

Before partnering with an influencer for a regional campaign, verify that their audience actually lives in the target market. An influencer who claims to reach “US tech professionals” but whose followers are 60% from countries outside your target market is not delivering the reach you are paying for.
# Quick check: what % of this influencer's audience is in the US?
countries, total = analyze_audience_geography("influencer_handle", max_follower_pages=10)
us_count = countries.get("United States", 0)
us_pct = us_count / total * 100 if total > 0 else 0
print(f"US audience: {us_pct:.1f}% ({us_count}/{total})")

Bot Farm Detection

If a local business or politician suddenly gains thousands of followers from countries that have no logical connection to their activity, that is a strong indicator of inorganic growth. Legitimate audience growth for a US-based brand will show a distribution weighted toward English-speaking countries. A spike of followers from a single unexpected country is a red flag.

Market Expansion Research

Before investing in localized content, ads, or partnerships in a new market, check whether your existing audience already has a meaningful presence there. If 8% of your followers are already in Germany, that is organic interest worth capitalizing on. If the number is near zero, you are starting from scratch.

Academic and Political Research

Researchers studying cross-border information flows, political influence, or diaspora communities can map how discussion participants on a topic distribute geographically. Extract users who tweet about a topic (via /search-tweets), then run their IDs through /about to build a geographic profile of the conversation.

Exporting to CSV

import csv

def export_geography_to_csv(country_dist, total, output_file="geography.csv"):
    """Export country distribution to CSV."""
    with open(output_file, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["country", "count", "percentage"])
        for country, count in country_dist.most_common():
            pct = round(count / total * 100, 2)
            writer.writerow([country, count, pct])
    print(f"Exported to {output_file}")


export_geography_to_csv(country_dist, total, "competitor_geography.csv")
This CSV is ready for Google Sheets, Excel pivot tables, or visualization tools like Tableau or Google Looker Studio to create geographic heat maps.

The Username Change Bonus

The /about endpoint also returns username_change_count and last_username_change_at. While these are not geographic data, they are useful in audience analysis contexts: Detecting rebrands and acquired handles. An account with 5+ username changes may have been bought and repurposed. If you are analyzing an influencer’s followers and many of them have high change counts, that can indicate bot or spam accounts that frequently rebrand. Account stability scoring. When qualifying leads or vetting partnership prospects from a follower list, an account that has never changed its username is more likely to be a stable, genuine account than one that changes handles every few months.
def check_account_stability(username):
    resp = requests.get(
        "https://api.sorsa.io/v3/about",
        headers={"ApiKey": API_KEY},
        params={"username": username},
    )
    resp.raise_for_status()
    data = resp.json()

    changes = data.get("username_change_count", 0)
    country = data.get("country", "Unknown")

    print(f"@{username}: {country} | {changes} username change(s)")
    if changes >= 3:
        print("  Warning: frequent handle changes")

    return data

A Note on Data Accuracy

The country data from /about is derived from platform-level technical signals, not user-reported information. This makes it significantly more reliable than parsing the “Location” field in user bios. However, it is not perfect:
  • A small percentage of users use VPNs, which can shift their detected country. For individual account lookups, keep this in mind. For aggregate analysis of hundreds or thousands of accounts, VPN noise averages out and the distribution is reliable.
  • Some accounts may return "Unknown" if the platform does not have sufficient data.
  • The country reflects the account’s current geographic association, which may change if the user relocates or switches VPN providers.
For audience-level analysis (the primary use case), these edge cases do not meaningfully affect the results. A sample of 500+ followers will give you a distribution accurate enough for market sizing, ad targeting, and competitive benchmarking decisions.

Next Steps