作者:Sorsa 编辑部
更新于 2026 年 7 月 11 日:对照当前按量付费模型重新核实了官方 X API 的地理限制(旧的分层请求上限对新开发者不再适用,速率限制现在按接口计),把 /about 响应更新到当前字段集(除 country 外,现在还返回连接来源、Premium 状态和关联账号),补上 100 次免费请求这条起步路径,让整套流程在付费前可测,并对照线上 Sorsa 参考重新核查了每个接口和价格。
核心要点。 Twitter 受众地理,就是一个 X 账号的粉丝按国家的分布。做法是拉取粉丝列表,查出 X 给每个粉丝分配的国家(公开的“账号所在地”标签背后的那个值,不是用户自己填的简介),再做聚合。这套方法对你自己的账号和任意公开账号都有效。
X(原 Twitter)曾经在原生分析里向你展示自己账号的受众国家数据,但从不让你看别人的。没有哪个公开按钮会告诉你某个网红的粉丝住在哪些国家,或者竞品的受众在各市场怎么分布。官方 X API 只在推文级暴露地理信息,而带地理标记的推文非常少;另一条路是 Enterprise 层级的 Profile Geo 增补,多数团队负担不起。
Sorsa API 是 Twitter/X API 的替代服务商,用 /about 这一个接口填上了这个缺口:按请求计费,价格固定,返回 X 与任意公开账号关联的国家,没有 OAuth,也没有审批队列。整套流程可以用 100 次免费请求先测一遍,注册即送,无需绑卡,永不过期,覆盖全部 40 个接口。把它和 /followers 接口配合,就能拿到任意账号(你自己的或竞品的)受众的完整国家拆解。本指南讲清四件事:这个国家值到底从哪来、为什么它比解析简介里的“位置”字段靠谱、四套可用于生产环境的 Python 方案,以及要花多少钱。
从 v1.1 时代起,我们就在给增长和研究团队跑受众地理的数据管道,2023 年定价变动后又把其中数十条从官方 API 迁了出来。这是我们收到最多的需求之一,也是最容易做错的之一。
目录
- X 实际把地理信息存在哪里
- 为什么受众地理不好拿
- 四个真实场景
- 国家数据有多准
- /about 接口参考
- Twitter 受众地理的四套 Python 方案
- 导出与可视化
- 这要花多少钱
- 实战
- 常见问题
- 如何开始
X 实际把地理信息存在哪里 {#where-x-actually-stores-geography}
X 为一个账号保存了三种不同的地理信号,彼此不能互换。多数讲这个话题的文章把它们混为一谈。
1. 简介里的“位置”字段。 用户自己填进资料的自由文本。没有标准化,经常是玩笑(“互联网”“某处的地下室”),经常为空,而且从不校验。老一批社媒聆听工具抓的就是这个字段,这也是它们的地理图表从来不准的根源。约 30% 到 40% 的账号把它留空,或者填的根本不是地名。
2. 推文级 Place 数据。 用户给某条推文打地理标记时,载荷里会带一个含国家代码的 Place 对象。据 X 自己关于按位置过滤推文的开发者文档,这在推文级是选择性开启的,而精确位置功能已在 2019 年从 iOS 和 Android 应用中移除。带地理元数据的推文不到 3%,做不了受众级分析。
3. 账号所属的国家,也就是“账号所在地”。 X 根据平台级信号(IP 历史、设备数据、支付方式、应用商店)为每个账号关联一个国家,与用户自己填的内容无关。2025 年底 X 把这个值放进了各资料页的“关于此账号”面板,公开显示为“账号所在地”,那些显示国家标徽的浏览器扩展读的也是同一个信号。这个面板里每个字段的完整拆解,见我们对 X“账号所在地”标签的解释文章。
Sorsa 的 /about 接口把第三种信号作为 country 字段返回。做聚合分析时,三者里这个最可靠:绝大多数活跃账号都有值,由平台推导而非用户自述,也不依赖用户开启推文级地理标记。
为什么受众地理不好拿 {#why-audience-geography-is-hard-to-get}
数据既然存在,为什么不是每个分析工具都能显示?有三个原因。
第一,官方 X API 没有把这个国家值做成一个能对任意账号查询的简单字段。你可以靠解析公开的“关于”面板逐个读单个资料,但没有对应的 v2 接口文档。Profile Geo 增补是单独的 Enterprise 附加项,带 profile_country 这类仅企业可用的操作符,用来按作者国家过滤推文。但那是五位数的年度合同,而且工作在推文流级,不是受众级。
第二是体量。想知道竞品的受众住在哪里,你得查每一个粉丝的国家,而不只是账号本身。一个 10 万粉丝的账号,就是 10 万次查询加几百页粉丝列表。官方 v2 接口按 15 分钟窗口限速,各接口不同,在当前按量付费模型下通常是几百次请求。这个规模的单次竞品审计要跑上好几个小时,往往是好几天的限流调用,这还没算按资源计费的成本。
第三,消费级工具只能查你自己的账号。Circleboom、TweepsMap、Fedica、Audiense 这类基于登录的分析工具通过 OAuth 以你的身份认证,所以只能绘制你自己的粉丝,绘制不了竞品的。它们读的还是用户自述的简介“位置”字段,并把留空的粉丝直接排除,样本因此偏向那些恰好填了这一栏的人。
| 方式 | 谁的受众 | 数据源 | 规模 | 说明 |
|---|---|---|---|---|
| 消费级登录工具 | 只能是自己的账号(OAuth) | 自述简介位置,空白被排除 | 点选操作,无批量也无代码 | 有可视化地图,但拿不到竞品受众 |
| 官方 X API | 任意账号,但没有对应字段 | 推文级地理,或 Enterprise Profile Geo | Profile Geo 需 Enterprise 合同 | 五位数,推文流级 |
Sorsa /about | 任意公开账号 | 平台国家(“账号所在地”) | 每账号一次请求,20 次/秒 | 按请求计费,JSON,只读 |
Sorsa 把这三道约束都绕开了:/about 直接返回国家,走固定月费套餐而不是按资源计费,对任意公开资料都有效,也不需要用户侧的 OAuth 授权。完整的成本情况,见我们的 2026 年 Twitter API 价格详解。
四个真实场景 {#four-real-use-cases}
下面四个场景里,受众地理直接决定了一个真实决策。本指南后面的方案与它们一一对应。
区域活动前的网红核查
我们合作过的一个消费科技品牌,准备把五位数中段的预算投进一次美国上市活动,请了六位创作者。每位都声称能触达“美国科技从业者”。审计结果显示,其中几位真正位于美国的粉丝远不到一半,粉丝量最大的那位甚至明显低于其粉丝数给人的印象。于是在合同签署之前,预算就转向了美国集中度更高的小体量创作者。做付费网红投放的人,都该在签约之前而不是之后核查受众地理。
市场扩张研究
在投入本地化内容、付费广告预算或区域招聘之前,先看清受众现在的样子。某个 B2B SaaS 团队做完一次审计发现,相当一部分自然粉丝位于一个他们从没用内容或广告定向过的市场,这足以支撑做一个本地化落地页和一场小型本地活动。反过来的情况同样有用:某个地区粉丝接近于零,说明你在那里是从零开始,预算应该投给认知而不是转化。这是受众研究的地理这一半;兴趣和粉丝交集那一半,见我们的目标受众实战手册。
非自然增长检测
如果一个中小账号突然从某个与其活动毫无关联的国家涌入大批粉丝,这就是很强的非自然增长信号。我们建议过的一个研究团队,会标记那些粉丝国家分布在 30 天内变动超过约 15 个百分点的账号,多数被标记的账号最后被查出买了粉丝,或者是某场放大活动的目标。对品牌来说,同样的逻辑能抓出买粉抽奖和竞品的水军。地理只是众多信号之一,完整的账号级方法见我们的审计虚假粉丝指南。
跨境对话绘制
做学术和政策研究时,可以任取一个话题,用 /search-tweets 或 /mentions 拉近期推文,再把去重后的作者过一遍 /about,画出这场对话在地理上的分布。我们见过它被用来研究侨民社区、错误信息传播和跨境政策辩论。“谁在真正讨论某个话题”的国家分布,按互动加权之后,很能说明这场对话到底属于谁。
国家数据有多准 {#how-accurate-is-the-country-data}
/about 返回的国家由平台推导,比解析简介可靠得多,但并不完美,局限值得说清楚。
有一小部分账号长期使用 VPN,会被标成 VPN 出口国家,而不是用户实际居住地。单次查询时这一点很要紧;但做 500 个粉丝以上的聚合分析时,VPN 噪音会被平均掉,整体分布在几个百分点内保持稳定。
有些账号返回不出国家。这说明 X 缺少足够信号来有把握地判定,通常出现在非常新或者活跃度极低的账号上。下面的方案会把这类账号归进“未知”桶。在一个健康的 1,000 粉丝样本里,这一比例大约是 1% 到 5%。而近期新增粉丝里“未知”占比明显偏高,本身就是有用的信号,与买粉商高度相关。
国家反映的是账号当前的地理关联,用户真的换了国家或切了 VPN,这个值就会变。所以要把长期群体的数据当作一个快照,不是固定身份。
和简介里的“位置”字段一比,反差很明显。占据这块市场的消费级工具读的是用户自述的简介文本,还会丢掉留空的粉丝;而 /about 的国家由 X 从连接信号推断,多数活跃账号都有值。做市场估算、广告定向、网红核查和竞品对标,这个准确度绰绰有余。但要做法证级别的单账号身份认定,任何单次查询都只能当作一个假设。
/about 接口参考 {#the-about-endpoint-reference}
认证只需一个请求头:ApiKey: YOUR_API_KEY。完整细节见 Sorsa 认证文档。
curl "https://api.sorsa.io/v3/about?username=elonmusk" \
-H "ApiKey: YOUR_API_KEY"
响应:
{
"country": "United States",
"username_change_count": 1,
"last_username_change_at": "2021-01-01T00:00:00Z",
"premium_start_at": "2025-06-18T16:28:44Z",
"is_blue_verified": true,
"source": "United States App Store",
"affiliate_username": "X"
}
你要的地理字段是 country。标识账号有三个参数可选:username(不带 @ 的用户名)、user_id(数字型稳定 ID)或 user_link(完整资料 URL)。聚合类工作流建议用 user_id,因为账号改用户名这个 ID 也不变。响应里还有几个字段:username_change_count 和 last_username_change_at 可用于稳定性评分;source 是“连接方式”的值,可能是网页或某个应用商店地区,可以当作一个粗略的第二地理信号;is_blue_verified 和 premium_start_at 表示 Premium 状态及起始时间,不过非 Premium 账号的时间戳是占位符;affiliate_username 是关联资料指向的母账号。上面链接的“账号所在地”解释文章把这些字段完整拆开讲了。任意套餐上一次请求就是扣一次额度,没有按接口的倍率。
Twitter 受众地理的四套 Python 方案 {#four-python-pipelines-for-twitter-audience-geography}
四套方案都建在两个辅助函数上:一个用 GET /followers 拉粉丝列表(每页最多 200 份用户资料,见粉丝与关注),一个用 GET /about 解析单个粉丝的国家。
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):
"""翻页调用 GET /followers(每页最多 200 份用户资料)。"""
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):
"""返回某个 user_id 的国家;X 没有值时返回 'Unknown'。"""
try:
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"
except requests.RequestException:
pass
return "Unknown"
方案 1:单账号查询
最简单的情况,既是后面的构建块,也适合临时验证。不想写代码只想看一眼的话,账号所在地查询可以直接在浏览器里跑同样的查询。
def get_account_geo(identifier):
"""返回一个账号的国家和稳定性元数据(用户名、user_id 或 URL)。"""
if identifier.startswith("http"):
params = {"user_link": identifier}
elif identifier.isdigit():
params = {"user_id": identifier}
else:
params = {"username": identifier.lstrip("@")}
resp = requests.get(f"{BASE_URL}/about", headers=HEADERS, params=params, timeout=15)
resp.raise_for_status()
return resp.json()
data = get_account_geo("elonmusk")
print(data["country"], data["username_change_count"])
方案 2:完整受众地理
核心工作流:拉取任意公开账号的粉丝列表,逐个解析国家,再聚合。
def audience_geography(username, max_pages=10):
followers = get_followers(username, max_pages=max_pages)
counts = Counter(get_country(f["id"]) for f in followers)
return counts, len(followers)
counts, total = audience_geography("nasa", max_pages=5)
for country, n in counts.most_common(10):
print(f"{country}: {n} ({n / total * 100:.1f}%)")
在 20 次/秒的限制下,1,000 个粉丝的样本顺序解析约需一分钟。审计规模更大时,把 get_country 放进 15 到 18 个工作线程的线程池,既能待在速率限制之内,又能大幅缩短实际耗时。
方案 3:竞品对比
对一小批账号跑同样的审计,再对比它们在目标市场的占比。受众地理是更完整的 Twitter 竞品分析流程的一项输入。
def compare_accounts(usernames, market="United States"):
for u in usernames:
counts, total = audience_geography(u, max_pages=5)
share = round(counts.get(market, 0) / total * 100, 1) if total else 0
print(f"{u}: {total} sampled, {market} {share}%, top {counts.most_common(3)}")
compare_accounts(["competitor_a", "competitor_b", "competitor_c"])
方案 4:按国家模式检测买粉
买来的粉丝群体有一个很典型的国家签名:“未知”占比偏高,加上意外的单一国家集中。
def inorganic_signal(counts, total):
unknown = counts.get("Unknown", 0) / total * 100 if total else 0
known = [(c, n) for c, n in counts.items() if c != "Unknown"]
top_country, top_n = max(known, key=lambda x: x[1], default=("none", 0))
top_share = top_n / total * 100 if total else 0
flags = []
if unknown > 20:
flags.append(f"high Unknown share ({unknown:.0f}%)")
if top_share > 60:
flags.append(f"single-country concentration ({top_country} {top_share:.0f}%)")
return flags
这是启发式判断,不是证据。成熟的养号场能掩盖国家,但这个模式能抓住大部分廉价的非自然增长。
导出与可视化 {#exporting-and-visualising}
拿到 counts 对象之后,可以导出做报表,也可以直接画成地图。想把拆解结果放进一张共享表给非技术同事看,见如何把 X 数据导出到 Google Sheets。
import csv
def export_to_csv(counts, total, path="audience_geography.csv"):
with open(path, "w", newline="") as f:
w = csv.writer(f)
w.writerow(["country", "count", "percentage"])
for country, count in counts.most_common():
w.writerow([country, count, round(count / total * 100, 2)])
/about 返回的国家名是标准英文名,可以直接映射进 Plotly 的分级统计地图(choropleth),不用再查 ISO 代码。这样就能得到消费级工具拿来卖钱的那种地图,数据是你自己的,输出是一个独立的 HTML 文件。
import plotly.express as px
def plot_choropleth(counts, path="audience_map.html"):
rows = [{"country": c, "followers": n} for c, n in counts.items() if c != "Unknown"]
fig = px.choropleth(rows, locations="country", locationmode="country names",
color="followers", color_continuous_scale="Blues")
fig.write_html(path)
这要花多少钱 {#how-much-does-this-cost}
受众地理属于高用量场景,因为每个粉丝都要调一次 /about。所有套餐都按请求计费,每次请求 $0.0018 起;100 次免费请求足够你在投入前先把成本测出来。以 Sorsa Pro 套餐($199/月,含 10 万次请求,每次请求 $0.00199)为例:
| 负载 | 请求数 | Pro 上的成本 |
|---|---|---|
| 单账号查询 | 1 | $0.002 |
| 1,000 粉丝抽样审计 | 约 1,005 | 约 $2.00 |
| 1 万粉丝完整审计 | 约 1.005 万 | 约 $20.00 |
| 三个竞品对比,每个 1,000 | 约 3,015 | 约 $6.00 |
| 每月 20 个账号、每个抽样 1,000 | 约 2.01 万 | Pro 已覆盖 |
单个账号的一次性审计,Starter 套餐($49/月,含 1 万次请求)就够;机构或分析团队的常规工作通常落在 Pro 上。完整档位见定价页面。活动核验有一个优化点:你不需要查每一个粉丝,只需要查完成了活动动作的账号,这个数量通常比完整粉丝列表小两个数量级。
实战 {#in-practice}
我们合作的一家分析机构,在交易阶段为客户审计创作者受众。对一小批创作者跑完整的受众流程,每人抽样几百到几千个粉丝,在 Pro 套餐上花几美元、几分钟就能完成;同样的任务放在官方 API 上,要么签 Enterprise 合同,要么忍受好几天的限流 v2 调用。如果把这项工作当作持续项目而不是一次性任务来跑,地理数据会嵌进更完整的竞品追踪方案。他们一再依赖这套数据的原因在于来源:国家来自 X 自己的“账号所在地”信号,不是登录类工具读的简介字段,所以客户质疑数字时,这份分布经得起追问。
常见问题 {#faq}
不用 API 能看到一个 Twitter 账号位于哪个国家吗?
单个账号可以,规模化不行。X 在每个资料的公开“关于此账号”面板上显示国家,我们对“账号所在地”标签的解释文章走了一遍手动查看的方法。但要拿到几千个粉丝的聚合分布,就需要 API,比如 Sorsa 的 /about:按请求计费,能在 REST 规模上返回同样的国家值。
简介里的“位置”字段为什么对受众地理不可靠?
简介“位置”字段是用户自己填的自由文本,不校验、不标准化,还经常留空或写成段子。约 30% 到 40% 的账号把它留空,或者填的不是地名。就算填了,“NYC”“New York City”和“Big Apple”在朴素脚本眼里是三个不同的地方。/about 返回的国家值由平台推导,这些失效模式一个都不沾。
官方 X API 提供粉丝国家数据吗?
在 v2 付费档位上,不针对任意账号在账号级提供。Profile Geo 增补基于作者资料给推文附加国家元数据,属于仅企业可用的附加项,五位数定价。标准 v2 用户查询接口不返回“账号所在地”国家。Sorsa 的 /about 填的就是这个缺口:同一个底层信号,用一次 REST 调用就能拿到。
一次受众地理审计要多久?
以 1,000 个粉丝的样本、Sorsa 20 次/秒的限制计算,顺序执行的脚本约一分钟跑完。1 万粉丝的审计顺序执行需要 8 到 12 分钟,用 15 到 18 个并发工作线程的线程池则是 1 到 2 分钟。粉丝列表翻页只占总量很小一部分,主要开销在逐个粉丝的 /about 调用上。
/about 返回不出国家时会怎样?
/about 返回不出国家,说明 X 没有足够信号来有把握地判定,通常出现在非常新或活跃度极低的账号上。本指南的方案会把这类账号归进“未知”桶。在一个健康的 1,000 粉丝样本里,这个比例大约 1% 到 5%。近期新增粉丝里“未知”占比明显偏高,往往与非自然增长模式和买粉商相关。
能用受众地理检测买粉吗?
能,但有前提。买粉商通常从少数几个监管薄弱的国家采购,这些账号往往活跃度很低。于是产出一个很典型的群体签名:“未知”占比高、意外的单一国家集中,以及偏高的用户名更换频率。本指南的方案 4 实现了这个启发式判断。这套判断并不完美,成熟的养号场能掩盖国家,但大部分廉价的非自然增长都能抓到。
哪些 Twitter API 替代方案支持规模化的受众地理查询?
Sorsa API 是少数几个通过有文档的 REST 接口(/about)提供“账号所在地”国家的替代方案之一,还能与每次请求最多 200 份用户资料的批量粉丝抽取配合,并且走固定月费套餐而不是按调用计费。更全面的横向对比,见我们对 Twitter API 替代方案的分析。
如何开始 {#getting-started}
最快的试法:到 Sorsa 控制台拿一个密钥,创建账号,领取 100 次免费请求(无需绑卡,永不过期,覆盖全部 40 个接口),先拿一部分额度对任意用户名跑一遍单账号代码片段,再逐步放量。/about 参考在接口文档里;在线试用工具 Playground 让你在写代码之前就能从浏览器调用 /about。所有套餐都按请求计费,没有审批环节,所以一次性的单账号审计落在 Starter 内,机构的常规工作通常落在 Pro 上。如果你要为审计抽取粉丝,我们的拉取粉丝列表指南讲了翻页模式和各种边缘情况。
审校:Keksich(Sorsa 创始人,X API 研究者)
本文是怎么写成的:内容建立在我们运营 Twitter/X API 替代方案的实际工作之上,包括支撑这些方案的 /about 和 /followers 接口,文中每个接口和价格都在 2026 年 7 月对照线上 Sorsa v3 参考和定价重新核实过。关于 X 自身的地理行为,我们参考了 X 关于推文位置过滤和 Enterprise Profile Geo 操作符的开发者文档;公开的“账号所在地”标签是什么、什么时候铺开的,见第一节链接的解释文章。成本数字按当前 Pro 每请求费率计算得出,不是估算。