如何批量修改已授权 public platform 账号资料

作者:TwexAPI 团队
ZH
其他语言:
中文
English日本語
TwexAPI
public platform 账号资料
已授权账号管理
批量修改
Python
Node.js
API 教程

用 TwexAPI 批量更新已授权 public platform 账号的名称、简介、地区、网站、头像和 banner。先验证单个账号,再用 Python 或 Node.js 小并发执行。

Quick Answer

TwexAPI 的 更新 public platform 账号资料 端点(/twitter/profile)用于更新您拥有或获授权管理的 public platform 账号资料字段。先用一个低风险账号验证,再以小并发执行;保护账号 cookie,并保存逐账号结果日志。。在 api.twexapi.io 使用 Bearer Token 认证。典型读取约 5 Credits(按公开换算约 $0.05/千次),符合条件的付费方案标示 20+ QPS。新账号可获得 20,000 起始 Credits。字段说明见本文及 https://docs.twexapi.io。

FAQ

更新 public platform 账号资料 端点返回什么?

用于更新您拥有或获授权管理的 public platform 账号资料字段。先用一个低风险账号验证,再以小并发执行;保护账号 cookie,并保存逐账号结果日志。

为什么在此场景使用 TwexAPI 而不是官方 public social data API?

更新 public platform 账号资料 场景可使用 https://docs.twexapi.io 中记录的 TwexAPI Bearer 认证流程。典型内容或资料读取约消耗 5 Credits(按公开换算约 $0.05/千次),符合条件的付费方案标示 20+ QPS。新账号可获得 20,000 起始 Credits。截至 2026-08-20,官方 Post 与 User 读取分别为 $5 和 $10/千次资源,限速因端点和访问级别而异。

在 TwexAPI 上运行此流程大概花多少?

典型读取约消耗 5 Credits。按公开换算,1,000 次此类读取约消耗 5,000 Credits,即约 $0.05;1 万次约消耗 50,000 Credits。实际成本因端点而异,请在 https://twexapi.io/pricing 确认端点价格与当前方案。

如果你只维护一个 X 账号,手动改账号资料通常更省事。真正麻烦的是一组账号一起改:品牌主号、地区支持号、产品线账号、活动账号,名称和简介要统一,头像和 banner 也不能漏。手动点十几次页面不难,但很容易改错账号、漏掉地区字段,或者把旧活动 banner 留在线上。

TwexAPI 的修改用户资料接口适合处理这种重复任务。比较稳的做法不是一上来就跑完整批次,而是先用一个账号验证字段和图片 URL,再把同一套请求包装成小并发脚本,最后保留每个账号的执行结果。

只为你拥有或已获授权管理的 X 账号使用此流程。账号 Cookie 应当像密码一样保管。

批量执行思路

Answer: 批量执行思路指在本案例中通过 api.twexapi.io 的 TwexAPI Bearer 接口完成该任务。典型读取约 5 Credits(约 $0.05/千次),符合条件的付费方案标示 20+ QPS。截至 2026-08-20,官方 Post 与 User 读取分别为 $5 和 $10/千次,限速因端点而异。

把账号资料批量修改当成一次受控的维护任务,而不是把账号列表丢进循环里直接跑。接口调用本身很简单:用 TwexAPI Bearer Token 调用 POST https://api.twexapi.io/twitter/profile,传入目标账号的 cookie,并且只传这次允许修改的字段。

一个稳妥的批量任务通常有四条边界:

  • 先更新一个低风险账号,并在 X 页面上确认展示结果。
  • 每个账号保留一条本地配置,updates 里只放这次要改的字段。
  • 用 2 到 3 个 worker 小并发执行,让失败原因更容易追踪。
  • 为每个账号保存成功状态、失败原因、重试次数和执行时间。

账号资料修改没有单独的预览端点。实际的 dry run 是先人工复核配置,再只更新一个低风险账号,最后到 X 页面确认展示结果。

API 端点

Answer: API 端点通过本文档中的 TwexAPI 端点以 Bearer Token 调用实现;批量或分页请求在 20+ QPS 下通常约 5 Credits/次。

POST https://api.twexapi.io/twitter/profile

Authorization: Bearer <your_twexapi_token>
Content-Type: application/json

请求体支持以下字段:

字段是否必填用途
cookie是待修改 X 账号的已登录 Cookie 字符串
name否展示名称
website否账号资料中的网站 URL
description否账号简介
location否账号地区
profile_image否头像图片 URL
profile_banner否Banner 图片 URL
proxy见说明本次请求使用的代理 URL

接口 schema 中 proxy 是可空字段,但字段说明要求使用住宅代理。批量任务里不要把它当成无关紧要的参数:除非 TwexAPI 支持人员确认你的集成可以省略,否则建议每个账号都配置稳定代理。这样即使某个账号失败,也更容易判断是凭据、代理、图片 URL 还是请求字段的问题。

成功修改后,接口会返回:

{
  "code": 200,
  "msg": "success",
  "data": true
}

先修改一个 X 账号

Answer: 先修改一个 public platform 账号指在本案例中通过 api.twexapi.io 的 TwexAPI Bearer 接口完成该任务。典型读取约 5 Credits(约 $0.05/千次),符合条件的付费方案标示 20+ QPS。截至 2026-08-20,官方 Post 与 User 读取分别为 $5 和 $10/千次,限速因端点而异。

先用一个低风险账号走完整流程。把 TwexAPI Token 放到环境变量中,不要写死在代码里:

export TWEXAPI_TOKEN="your_twexapi_bearer_token"

然后只传入这次要修改的字段:

1curl --request POST \
2  --url https://api.twexapi.io/twitter/profile \
3  --header "Authorization: Bearer $TWEXAPI_TOKEN" \
4  --header "Content-Type: application/json" \
5  --data '{
6    "cookie": "auth_token=...; ct0=...; twid=...",
7    "proxy": "http://username:[email protected]:8000",
8    "name": "Acme Support",
9    "description": "Product help, release notes, and service updates.",
10    "location": "New York",
11    "website": "https://example.com/support",
12    "profile_image": "https://example.com/assets/support-avatar.png",
13    "profile_banner": "https://example.com/assets/support-banner.png"
14  }'

这一步不是为了省几行代码,而是为了确认三件事:Cookie 仍然有效,代理能正常请求,图片 URL 可以被公开访问。只有单账号请求成功后,再进入批量脚本。

只传你确实想改的值。例如只更新文字资料时,不要传 profile_image 和 profile_banner。批量任务最怕“顺手带上旧字段”,因为脚本会忠实地把旧值写回去。

准备批量账号配置

Answer: 准备批量账号配置指在本案例中通过 api.twexapi.io 的 TwexAPI Bearer 接口完成该任务。典型读取约 5 Credits(约 $0.05/千次),符合条件的付费方案标示 20+ QPS。截至 2026-08-20,官方 Post 与 User 读取分别为 $5 和 $10/千次,限速因端点而异。

创建一个本地 profiles.json 文件。每个对象代表一个账号,updates 里只放这次要改的字段。account_id 只是写入报告的本地标签,不会发送给 API。

1[
2  {
3    "account_id": "acme-support-us",
4    "cookie": "auth_token=...; ct0=...; twid=...",
5    "proxy": "http://username:[email protected]:8000",
6    "updates": {
7      "name": "Acme Support US",
8      "description": "Product help and service updates for US customers.",
9      "location": "United States",
10      "website": "https://example.com/us/support",
11      "profile_image": "https://example.com/assets/us-avatar.png",
12      "profile_banner": "https://example.com/assets/us-banner.png"
13    }
14  },
15  {
16    "account_id": "acme-support-jp",
17    "cookie": "auth_token=...; ct0=...; twid=...",
18    "proxy": "http://username:[email protected]:8000",
19    "updates": {
20      "name": "Acme Support JP",
21      "description": "Product help and service updates for customers in Japan.",
22      "location": "Japan",
23      "website": "https://example.com/jp/support"
24    }
25  }
26]

profiles.json 包含账号凭据,不要把它提交到版本库。如果它必须放在本地项目目录中,请将其加入 .gitignore,并限制文件权限:

chmod 600 profiles.json

下面的批量脚本只会发送 updates 中明确存在的键。省略字段,就不会修改现有账号资料。只有当你确实要清空字段,并且已经用单账号验证过行为时,才使用显式 null。

Python 批量修改脚本

Answer: Python 批量修改脚本指在本案例中通过 api.twexapi.io 的 TwexAPI Bearer 接口完成该任务。典型读取约 5 Credits(约 $0.05/千次),符合条件的付费方案标示 20+ QPS。截至 2026-08-20,官方 Post 与 User 读取分别为 $5 和 $10/千次,限速因端点而异。

下面这个脚本做四件事:校验每个账号是否有 cookie 和 updates,过滤不支持的字段,对临时网络错误做最多 3 次重试,并把每个账号的结果写入 profile-update-results.json。如果环境里还没有 requests,先安装依赖:

python -m pip install requests

将下面的脚本保存为 batch-update-profiles.py:

1import json
2import os
3import time
4from concurrent.futures import ThreadPoolExecutor, as_completed
5
6import requests
7
8API_URL = "https://api.twexapi.io/twitter/profile"
9ALLOWED_FIELDS = {
10    "name",
11    "website",
12    "description",
13    "location",
14    "profile_image",
15    "profile_banner",
16}
17MAX_ATTEMPTS = 3
18MAX_WORKERS = 3
19
20token = os.environ.get("TWEXAPI_TOKEN")
21if not token:
22    raise RuntimeError("Missing TWEXAPI_TOKEN environment variable.")
23
24def build_body(account):
25    account_id = account.get("account_id", "unnamed-account")
26    cookie = account.get("cookie")
27    updates = account.get("updates")
28
29    if not cookie:
30        raise ValueError(f"{account_id}: missing cookie")
31    if not isinstance(updates, dict) or not updates:
32        raise ValueError(f"{account_id}: updates must be a non-empty object")
33
34    unknown_fields = sorted(set(updates) - ALLOWED_FIELDS)
35    if unknown_fields:
36        raise ValueError(f"{account_id}: unsupported fields: {unknown_fields}")
37
38    body = {"cookie": cookie, **updates}
39    if account.get("proxy"):
40        body["proxy"] = account["proxy"]
41    return body
42
43def update_profile(account):
44    account_id = account.get("account_id", "unnamed-account")
45
46    try:
47        body = build_body(account)
48    except ValueError as error:
49        return {"account_id": account_id, "ok": False, "error": str(error)}
50
51    for attempt in range(1, MAX_ATTEMPTS + 1):
52        try:
53            response = requests.post(
54                API_URL,
55                headers={"Authorization": f"Bearer {token}"},
56                json=body,
57                timeout=45,
58            )
59            payload = response.json()
60
61            if response.ok and payload.get("code") == 200 and payload.get("data") is True:
62                return {"account_id": account_id, "ok": True, "attempts": attempt}
63
64            message = str(payload.get("msg", f"HTTP {response.status_code}"))
65            retryable = response.status_code == 429 or response.status_code >= 500
66            if not retryable:
67                return {"account_id": account_id, "ok": False, "error": message}
68        except (requests.RequestException, ValueError) as error:
69            message = f"{type(error).__name__}: request failed"
70
71        if attempt < MAX_ATTEMPTS:
72            time.sleep(attempt * 2)
73
74    return {
75        "account_id": account_id,
76        "ok": False,
77        "error": message,
78        "attempts": MAX_ATTEMPTS,
79    }
80
81with open("profiles.json", encoding="utf-8") as source:
82    accounts = json.load(source)
83
84if not isinstance(accounts, list) or not accounts:
85    raise RuntimeError("profiles.json must contain a non-empty array.")
86
87results = []
88with ThreadPoolExecutor(max_workers=min(MAX_WORKERS, len(accounts))) as executor:
89    futures = [executor.submit(update_profile, account) for account in accounts]
90    for future in as_completed(futures):
91        result = future.result()
92        results.append(result)
93        print(f"{result['account_id']}: {'updated' if result['ok'] else 'failed'}")
94
95with open("profile-update-results.json", "w", encoding="utf-8") as output:
96    json.dump(results, output, ensure_ascii=False, indent=2)
97
98success_count = sum(1 for result in results if result["ok"])
99print(f"Finished: {success_count}/{len(results)} profiles updated.")

运行脚本:

TWEXAPI_TOKEN="your_twexapi_bearer_token" python batch-update-profiles.py

第一次运行时,建议只在 profiles.json 里保留 1 到 2 个账号。确认结果文件、X 页面展示和图片加载都正常后,再放回完整列表。

Node.js 批量修改脚本

Answer: Node.js 批量修改脚本指在本案例中通过 api.twexapi.io 的 TwexAPI Bearer 接口完成该任务。典型读取约 5 Credits(约 $0.05/千次),符合条件的付费方案标示 20+ QPS。截至 2026-08-20,官方 Post 与 User 读取分别为 $5 和 $10/千次,限速因端点而异。

如果你的团队更常用 Node.js,可以用下面这个版本。Node.js 18 及以上版本已经内置 fetch,无需额外安装依赖。将脚本保存为 batch-update-profiles.mjs:

1import { readFile, writeFile } from "node:fs/promises";
2
3const API_URL = "https://api.twexapi.io/twitter/profile";
4const ALLOWED_FIELDS = new Set([
5  "name",
6  "website",
7  "description",
8  "location",
9  "profile_image",
10  "profile_banner",
11]);
12const MAX_ATTEMPTS = 3;
13const MAX_WORKERS = 3;
14const token = process.env.TWEXAPI_TOKEN;
15
16if (!token) {
17  throw new Error("Missing TWEXAPI_TOKEN environment variable.");
18}
19
20const sleep = (milliseconds) =>
21  new Promise((resolve) => setTimeout(resolve, milliseconds));
22
23function buildBody(account) {
24  const accountId = account.account_id || "unnamed-account";
25  const updates = account.updates;
26
27  if (!account.cookie) {
28    throw new Error(`${accountId}: missing cookie`);
29  }
30  if (!updates || typeof updates !== "object" || Array.isArray(updates)) {
31    throw new Error(`${accountId}: updates must be a non-empty object`);
32  }
33
34  const updateKeys = Object.keys(updates);
35  if (updateKeys.length === 0) {
36    throw new Error(`${accountId}: updates must be a non-empty object`);
37  }
38
39  const unknownFields = updateKeys.filter((key) => !ALLOWED_FIELDS.has(key));
40  if (unknownFields.length > 0) {
41    throw new Error(`${accountId}: unsupported fields: ${unknownFields.join(", ")}`);
42  }
43
44  return {
45    cookie: account.cookie,
46    ...(account.proxy ? { proxy: account.proxy } : {}),
47    ...updates,
48  };
49}
50
51async function updateProfile(account) {
52  const accountId = account.account_id || "unnamed-account";
53  let body;
54
55  try {
56    body = buildBody(account);
57  } catch (error) {
58    return { account_id: accountId, ok: false, error: error.message };
59  }
60
61  let message = "request failed";
62  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
63    try {
64      const response = await fetch(API_URL, {
65        method: "POST",
66        headers: {
67          Authorization: `Bearer ${token}`,
68          "Content-Type": "application/json",
69        },
70        body: JSON.stringify(body),
71        signal: AbortSignal.timeout(45_000),
72      });
73      const payload = await response.json().catch(() => ({}));
74
75      if (response.ok && payload.code === 200 && payload.data === true) {
76        return { account_id: accountId, ok: true, attempts: attempt };
77      }
78
79      message = String(payload.msg || `HTTP ${response.status}`);
80      const retryable = response.status === 429 || response.status >= 500;
81      if (!retryable) {
82        return { account_id: accountId, ok: false, error: message };
83      }
84    } catch (error) {
85      message = `${error.name}: request failed`;
86    }
87
88    if (attempt < MAX_ATTEMPTS) {
89      await sleep(attempt * 2_000);
90    }
91  }
92
93  return { account_id: accountId, ok: false, error: message, attempts: MAX_ATTEMPTS };
94}
95
96async function runQueue(accounts, workerCount) {
97  const results = new Array(accounts.length);
98  let cursor = 0;
99
100  async function worker() {
101    while (cursor < accounts.length) {
102      const index = cursor;
103      cursor += 1;
104      results[index] = await updateProfile(accounts[index]);
105      console.log(`${results[index].account_id}: ${results[index].ok ? "updated" : "failed"}`);
106    }
107  }
108
109  await Promise.all(
110    Array.from({ length: Math.min(workerCount, accounts.length) }, () => worker()),
111  );
112  return results;
113}
114
115const accounts = JSON.parse(await readFile("profiles.json", "utf8"));
116if (!Array.isArray(accounts) || accounts.length === 0) {
117  throw new Error("profiles.json must contain a non-empty array.");
118}
119
120const results = await runQueue(accounts, MAX_WORKERS);
121await writeFile("profile-update-results.json", JSON.stringify(results, null, 2));
122
123const successCount = results.filter((result) => result.ok).length;
124console.log(`Finished: ${successCount}/${results.length} profiles updated.`);

运行脚本:

TWEXAPI_TOKEN="your_twexapi_bearer_token" node batch-update-profiles.mjs

上线前检查清单

Answer: 上线前检查清单指在本案例中通过 api.twexapi.io 的 TwexAPI Bearer 接口完成该任务。典型读取约 5 Credits(约 $0.05/千次),符合条件的付费方案标示 20+ QPS。截至 2026-08-20,官方 Post 与 User 读取分别为 $5 和 $10/千次,限速因端点而异。

  • 确认这些账号都由你拥有,或已经获得明确管理授权。
  • 把 TWEXAPI_TOKEN 放在环境变量或密钥管理系统中,不要写入脚本。
  • 把 X Cookie 和带认证信息的代理 URL 放在版本库之外。
  • 不要在日志、报错信息或结果报告中输出 Cookie 与代理凭据。
  • 先跑 1 个账号,再跑一个小批次,确认无误后才扩大规模。
  • 保持较小并发。一次增加太多 worker,会让失败原因更难排查。
  • 只对网络异常、HTTP 429 和 HTTP 5xx 做重试;字段错误、凭据错误要先修正再重跑。
  • 每次执行后检查 profile-update-results.json,并保留它作为审计记录。
  • 为 profile_image 和 profile_banner 使用稳定、可公开访问的图片 URL。

相关资源

如何批量修改已授权 public platform 账号资料