著者:Sorsa Editorial

更新:2026年7月。100回分の無料リクエストの開始オファーを、始め方と料金のガイダンスに追加し、公式 X API のコスト比較を現行の従量課金の料金に刷新しました。

要点: Twitter(X)は、API を通じて5つのユーザーアクションを検証させます。フォロー、リツイート、引用ツイート、コメント、そしてコミュニティのメンバーシップです。各チェックは単一の true/false の結果を返します。いいねは2024年6月に非公開になり、もはやどのツールでも検証できません。アカウントの所有権は、ユーザーにユニークなコードを投稿させることで証明します。

X(旧Twitter)でアクションに報いる懸賞、アンバサダープログラム、またはクエストキャンペーンを動かすと、偽のハンドル、半分終わったタスク、そしてボットがフォームを埋め始めた瞬間に崩れます。参加者300人までには、手動のレビューは絶望的で、名誉制度のチェックボックスは無価値です。実際に必要なのは、すべての参加者について「この人は本当に主張したことをしたか?」に答える API です。代替の Twitter/X API プロバイダーである Sorsa API は、各検証を1つの yes/no のエンドポイントとして公開します。ハンドルとアクションを渡し、ブール値を返してもらいます。OAuth のハンドシェイクも開発者アカウントの承認もなく、ただ単一の ApiKey ヘッダーだけです。レート制限はすべてのプランで一律の毎秒20リクエストです。すべてのアカウントは100回分の無料リクエスト(1回限り、クレジットカード不要、40個のエンドポイントすべてで有効、有効期限なし)で始まり、コミットする前にフルテストのキャンペーンを検証するのに十分です。そしてフラットなリクエスト単位の料金(エントリープランで$0.0049から、Pro でおよそ$0.002に下がる)で、公式 X API で同じチェックを再構築する何分の1かのコストです。公式 X API は完全なリストにわたってリソース単位で課金します。Sorsa は読み取り専用なので、公開データを検証して読みますが、投稿、フォロー、DM はしません。書き込みアクションには、依然として公式 API を使います。

これらのエンドポイントは Sorsa がビルドして運用しており、主にクリエイターマーケティングのエージェンシーと懸賞ツールのために、数十のキャンペーンにわたってこのパターンを実行し監査してきました。エンドポイントはシンプルです。落とし穴(非公開アカウント、なりすましのハンドル、ボットファーム、いいねをチェックする失われた能力)はそうではありません。本ガイドは、動作する Python ですべてのチェックを案内し、それからそれらを完全なキャンペーンのパイプラインに縫い合わせます。まず1人の参加者、それから数万人のバルク検証です。コードを書かずにフロー全体を実行したいなら、懸賞検証 のソリューションが、これと同じチェックを UI の背後で包みます。


目次

  1. X で検証できることとできないこと
  2. 公式 X API 対 Sorsa での検証の仕組み
  3. チェック1:ユーザーはアカウントをフォローしたか?
  4. チェック2:ユーザーはツイートをリツイートしたか?
  5. チェック3:ユーザーはツイートを引用したか?
  6. チェック4:ユーザーはツイートにコメントしたか?
  7. チェック5:ユーザーはコミュニティのメンバーか?
  8. 完全なキャンペーン検証のパイプラインを作る
  9. 参加者をバルクで検証する
  10. アカウントの所有権を検証する
  11. 不正防止:アカウント品質のチェック
  12. 影響力で重み付けした報酬の採点
  13. 実践:14日で47,000人の参加者
  14. 参加者あたりのコスト
  15. 始め方
  16. よくある質問

X で検証できることとできないこと {#what-you-can-and-cannot-verify-on-x}

X 上の5つのユーザーアクションが、今日 API を通じて検証可能です。フォロー、リツイート、引用ツイート、コメント、そしてコミュニティへの参加です。それぞれが1つのエンドポイントに対応し、ブール値を返します。いいねはもう、公開でもサードパーティでも、どのツールでも検証できません。X が2024年6月にそれらを非公開にしたからです。表示も検証できません。

アクションエンドポイントメソッド返すもの検証可能?
ユーザーがアカウントをフォロー/check-followPOST{follow: true/false}はい
ユーザーがツイートをリツイート/check-retweetPOST{retweet: true/false}はい
ユーザーがツイートを引用/check-quotedPOST{status: "quoted" / "retweet" / "not_found"}はい
ユーザーがツイートにコメント/check-commentGET{commented: true/false, tweet: {...}}はい
ユーザーが X コミュニティに参加/check-community-memberPOST{is_member: true/false}はい
ユーザーがツイートに いいね(なし)(なし)(なし)いいえ、2024年6月以降非公開
ユーザーがツイートを表示/インプレッション(なし)(なし)(なし)いいえ

いいねの制限が最もよくある驚きです。2024年6月、X はすべての人のいいねを非公開にしました。投稿の作者だけが誰がいいねしたかを見られ、(公式 X API を含む)どの API も「ユーザー A がツイート B にいいねしたか」にもう答えられません。「この投稿にいいね」を含む古いキャンペーンのテンプレートがあるなら、そのタスクをリツイートかコメントに置き換えます。両方とも完全に検証可能なままで、どのみちより強いエンゲージメントのシグナルを生みます。

上の表の他のすべては単一の API 呼び出しです。認証は単一の ApiKey ヘッダー(OAuth フローなし、アプリ承認なし)で、レスポンスは素の JSON です。本ガイドの残りは実用的なハウツーです。


公式 X API 対 Sorsa での検証の仕組み {#how-verification-works-on-the-official-x-api-vs-sorsa}

公式 X API には、特定のユーザーがフォロー、リツイート、引用、またはコメントしたかを直接答えるエンドポイントはありません。フォロワー、リツイート者、または返信者の完全なリストを取得してそれらを検索することで、各答えを再構築し、取得したリソース単位で課金されます。目的に作られた検証 API は代わりに、チェックあたり1つのブール値を1回のリクエストで返します。

これが、チームが専用の検証層に手を伸ばす最大の単一の理由で、並べて見る価値があります。以下の数字は、公式 X API の現行の従量課金レートと、Sorsa のフラットなリクエスト単位の料金です。

タスク公式 X API(従量課金)Sorsa API
ユーザーがアカウントをフォローしたか直接のエンドポイントなし。アカウントの完全なフォロワーまたはフォロー中のリストをページネーションして検索します。返される各プロフィールがユーザー読み取り(各$0.010)として課金されます。1回の /check-follow リクエストが follow: true/false を返します。
ユーザーがツイートをリツイートしたか直接のエンドポイントなし。完全なリツイート者のリストを取得してハンドルを検索します。1回の /check-retweet リクエストが retweet: true/false を、非常に大きなリストにはページネーションとともに返します。
ユーザーがツイートを引用したか直接のエンドポイントなし。引用ツイートを取得して作者を一致させます。1回の /check-quoted リクエストが quotedretweet、または not_found を返します。
ユーザーがコメントしたか直接のエンドポイントなし。返信のリストを取得してハンドルを検索します。1回の /check-comment リクエストが commented: true/false と返信を返します。
ユーザーがコミュニティのメンバーか同等の公開エンドポイントなし。1回の /check-community-member リクエストが is_member: true/false を返します。
認証ベアラートークンと承認済み開発者アプリを持つ OAuth 2.0。単一の ApiKey ヘッダー。審査待ちなし。
課金取得したリソース単位:投稿あたり$0.005、ユーザープロフィールあたり$0.010、月2,000,000投稿読み取りの上限付き。フラットなリクエスト単位:1呼び出し = 1リクエスト、$0.0049(Starter)からおよそ$0.002(Pro)まで、すべてのエンドポイント込み。
レート制限エンドポイントにより変動、固定の窓で。すべてのプランで一律の毎秒20リクエスト。

サンプルではなく完全なオーディエンスに対してチェックすることに、2つ目の、より静かな利点があります。公開の X インターフェースとほとんどの無料の懸賞ピッカーは、オーディエンスの最も最近のひと切れ、しばしば直近100人前後のリツイート者や返信者しか表面化しないため、そこから引かれた当選者は、より早く参加した全員を静かに除外します。完全なリストに対する検証はそれを避けます。Sorsa の /check-retweet はリスト全体を一度に100エントリーずつページネーションし、ユーザー単位の直接のチェックは、参加者がオーディエンスのどこに座っていようと、特定の参加者について答えます。

目的が yes/no の答えではなく根底のエンゲージメントデータ(返信者、引用者、リツイート者の完全なリストとその指標)なら、それは別の仕事で、Twitter エンゲージメント API ガイド で扱っています。


チェック1:ユーザーはアカウントをフォローしたか? {#check-1-did-the-user-follow-an-account}

最も一般的なキャンペーンのタスク(「参加するには @YourBrand をフォロー」)。/check-follow エンドポイントがこれに直接答えます。エンドポイントのロジックは「user_2 は user_1 をフォローするか?」なので、user_1 がブランドで user_2 が参加者です。

エンドポイント: POST https://api.sorsa.io/v3/check-follow

パラメータ

ブランド(フォローされるアカウント)に1つの識別子を、参加者に1つを提供します。

パラメータ必須説明
username_1stringいずれかブランドのハンドル。
user_link_1string1つまたはブランドのプロフィール URL。
user_id_1stringまたはブランドの数値のユーザー ID。
username_2stringいずれか参加者のハンドル。
user_link_2string1つまたは参加者のプロフィール URL。
user_id_2stringまたは参加者の数値のユーザー ID。

Python

python
import requests

API_KEY = "YOUR_API_KEY"
BASE = "https://api.sorsa.io/v3"
HEADERS = {"ApiKey": API_KEY, "Content-Type": "application/json"}

def check_follow(brand_handle: str, participant_handle: str) -> dict:
    resp = requests.post(
        f"{BASE}/check-follow",
        headers=HEADERS,
        json={"username_1": brand_handle, "username_2": participant_handle},
        timeout=15,
    )
    resp.raise_for_status()
    return resp.json()


result = check_follow("YourBrand", "participant123")
if result["follow"]:
    print("Follow verified.")
elif result.get("user_protected"):
    print("Account is private; follow cannot be confirmed.")
else:
    print("Not following.")

レスポンス

json
{
  "follow": true,
  "user_protected": false
}

知っておくべきエッジケース

user_protectedtrue なら、参加者のアカウントは非公開で、そのフォローグラフはどのサードパーティにも見えません。3つの選択肢があります。エントリーを拒否する、参加者に検証のためアカウントを公開にするよう頼む、またはアカウント所有権の検証(下で扱います)を使って本人がハンドルを所有することを確認し、それから手動のオーバーライドでエントリーを受け入れる、です。これまでの経験では、懸賞の参加者の1%未満が非公開アカウントを持つため、明確なメッセージ付きのハードな拒否がたいてい問題ありません。

このチェックは、キャンペーンのためにフォロー関係の一方向に答えます。キャンペーンの文脈の外で単一のフォローや相互フォローをチェックするには、あるアカウントが別のアカウントをフォローしているかチェックする方法 のガイドをご覧いただくか、ノーコードの フォローチェッカーツール でブラウザで単発のチェックを実行します。


チェック2:ユーザーはツイートをリツイートしたか? {#check-2-did-the-user-retweet-a-tweet}

「参加するにはこの投稿をリツイート」。到達を高める標準的な仕組みです。/check-retweet エンドポイントはブール値を返し、大きなリストにはページネーションします。

エンドポイント: POST https://api.sorsa.io/v3/check-retweet

パラメータ

パラメータ必須説明
tweet_linkstringはい検証するツイートの URL。
usernamestringいずれか参加者のハンドル。
user_linkstring1つまたはプロフィール URL。
user_idstringまたは数値のユーザー ID。
next_cursorstringいいえ100を超えるリツイートのツイートのためのページネーション。

Python

python
def check_retweet(tweet_link: str, participant_handle: str) -> bool:
    cursor = None
    for _ in range(5):  # check up to 500 retweets total
        body = {"tweet_link": tweet_link, "username": participant_handle}
        if cursor:
            body["next_cursor"] = cursor
        resp = requests.post(f"{BASE}/check-retweet", headers=HEADERS, json=body, timeout=15)
        resp.raise_for_status()
        data = resp.json()
        if data["retweet"]:
            return True
        cursor = data.get("next_cursor")
        if not cursor:
            return False
    return False

ページネーションの仕組み

各呼び出しは最も最近の100リツイートをスキャンします。ツイートが数千のリツイートを持ち、ユーザーが早くリツイートしたなら、そのアクションはリストのより深くにある可能性があり、ページネーションを必要とします。上の例は、検証を速く保つために5ページ(最も最近の500リツイート)に制限します。ほとんどのキャンペーンには、参加者がプロンプトを見てから数時間以内にリツイートする傾向があるため、これで十分すぎます。そのアクションはリストの上部に座ります。

これが、ネイティブの X インターフェースや無料のピッカーから当選者を引くのとの実用的な違いで、それらは通常最も最近のバッチしか見ません。/check-retweet は完全なリストを一度に100ずつ歩くため、早いリツイート者は遅いものと同じように確実に見つかります。公式 X API は同等の直接のチェックを提供しません。完全なリツイート者のリストを取得して自分で検索し、OAuth のセットアップ、窓ごとのレート制限の会計、そしてその上に自分のページネーションのロジックが必要です。


チェック3:ユーザーはツイートを引用したか? {#check-3-did-the-user-quote-a-tweet}

「ひとことを添えてこれを引用」。これはプレーンなリツイートより価値があります。引用ツイートが参加者自身のコメントを加え、パーソナライズされたテキストでキャンペーンを増幅するからです。

エンドポイント: POST https://api.sorsa.io/v3/check-quoted

/check-quoted エンドポイントは、引用をプレーンなリツイートと区別することに賢く、3つのステータスのうち1つを返します。

Python

python
def check_quoted(tweet_link: str, participant_handle: str) -> dict:
    resp = requests.post(
        f"{BASE}/check-quoted",
        headers=HEADERS,
        json={"tweet_link": tweet_link, "username": participant_handle},
        timeout=15,
    )
    resp.raise_for_status()
    return resp.json()


data = check_quoted("https://x.com/YourBrand/status/1234567890", "participant123")

if data["status"] == "quoted":
    print(f"Quote verified on {data['date']}: {data['text']}")
elif data["status"] == "retweet":
    print("Retweeted without commentary; does not satisfy quote requirement.")
else:
    print("No quote or retweet found.")

なぜ引用のテキストが重要か

レスポンスは完全な引用のテキストと日付を含み、エントリーを承認する前に品質チェックにパイプできます。「新製品について、ひとことを添えて引用」を必要とするキャンペーンは、「いいね」のような一語の引用より多くに値します。これらのキャンペーンを動かすほとんどのチームは、最小文字数のルール(通常30〜50文字)、冒とく的な言葉のチェック、そしてキャンペーンが使うなら必須のハッシュタグを適用します。

python
def quote_is_acceptable(quote_text: str, min_length: int = 30, required_hashtag: str = None) -> bool:
    if len(quote_text.strip()) < min_length:
        return False
    if required_hashtag and required_hashtag.lower() not in quote_text.lower():
        return False
    return True

チェック4:ユーザーはツイートにコメントしたか? {#check-4-did-the-user-comment-on-a-tweet}

「この投稿の下にコメントを落とす」。POST の代わりに GET を使う唯一の検証エンドポイントです。

エンドポイント: GET https://api.sorsa.io/v3/check-comment

パラメータ(クエリ文字列)

パラメータ必須説明
tweet_linkstringはいツイートの URL。
usernamestringいずれか参加者のハンドル。
user_linkstring1つまたはプロフィール URL。
user_idstringまたは数値のユーザー ID。

Python

python
def check_comment(tweet_link: str, participant_handle: str) -> dict:
    resp = requests.get(
        f"{BASE}/check-comment",
        headers={"ApiKey": API_KEY},
        params={"tweet_link": tweet_link, "username": participant_handle},
        timeout=15,
    )
    resp.raise_for_status()
    return resp.json()


data = check_comment("https://x.com/YourBrand/status/1234567890", "participant123")

if data["commented"]:
    text = data["tweet"]["full_text"]
    print(f"Comment verified: {text[:120]}")
else:
    print("No comment found.")

レスポンスとコメントの品質

commentedtrue のとき、/check-comment からのレスポンスは、コメント自体の完全なツイートオブジェクトを含みます。テキスト、エンゲージメント指標、言語検出、タイムスタンプです。これを使って、最小の長さ、必須のキーワードを強制するか、絵文字だけのスパムの返信を拒否します。コメントがエンゲージメントのタスク全体であるキャンペーンでは、品質の基準は単一の絵文字より高くあるべきです。

python
def comment_is_acceptable(comment: dict, min_length: int = 20, required_keyword: str = None) -> bool:
    text = comment.get("full_text", "").strip()
    if len(text) < min_length:
        return False
    if required_keyword and required_keyword.lower() not in text.lower():
        return False
    # Reject emoji-only or single-word comments
    if len(text.split()) < 3:
        return False
    return True

チェック5:ユーザーはコミュニティのメンバーか? {#check-5-is-the-user-a-community-member}

「参加するには自社の X コミュニティに参加」。参加者を、一発限りのリツイート者ではなく、コミュニティの持続的な一部にしたいときに有用です。

エンドポイント: POST https://api.sorsa.io/v3/check-community-member

python
def check_community_member(community_id: str, participant_handle: str) -> bool:
    resp = requests.post(
        f"{BASE}/check-community-member",
        headers=HEADERS,
        json={"community_id": community_id, "username": participant_handle},
        timeout=15,
    )
    resp.raise_for_status()
    return resp.json().get("is_member", False)


is_member = check_community_member("1966045657589813686", "participant123")
print("Member" if is_member else "Not a member")

コミュニティ ID は、コミュニティの URL(x.com/i/communities/<id>)の中の長い数値の文字列です。/check-community-member エンドポイントはきれいなブール値を返します。コミュニティに参加することはエンゲージし続ける意図を示すため、コミュニティはしばしば一度限りのリツイートより耐久性のあるシグナルです。


完全なキャンペーン検証のパイプラインを作る {#building-a-full-campaign-verification-pipeline}

実際のキャンペーンでは、参加者はいくつかのタスクを完了します。以下は、1人の参加者について5つのチェックすべてを実行し、構造化された結果を返し、コメントと引用に品質のルールを適用するパターンです。

python
from dataclasses import dataclass, field

@dataclass
class CampaignConfig:
    brand_handle: str
    tweet_to_retweet: str
    tweet_to_quote: str
    tweet_to_comment: str
    community_id: str
    required_hashtag: str = ""
    min_quote_length: int = 30
    min_comment_length: int = 20

@dataclass
class ParticipantResult:
    username: str
    follow: bool = False
    retweet: bool = False
    quote: bool = False
    quote_text: str = ""
    comment: bool = False
    comment_text: str = ""
    community: bool = False
    completed: int = field(init=False, default=0)

    def total(self) -> int:
        return sum([self.follow, self.retweet, self.quote, self.comment, self.community])


def verify_participant(username: str, cfg: CampaignConfig) -> ParticipantResult:
    r = ParticipantResult(username=username)

    # Follow
    r.follow = check_follow(cfg.brand_handle, username)["follow"]

    # Retweet
    r.retweet = bool(check_retweet(cfg.tweet_to_retweet, username))

    # Quote tweet (with quality check)
    quote_data = check_quoted(cfg.tweet_to_quote, username)
    if quote_data["status"] == "quoted":
        r.quote_text = quote_data.get("text", "")
        r.quote = quote_is_acceptable(r.quote_text, cfg.min_quote_length, cfg.required_hashtag)

    # Comment (with quality check)
    comment_data = check_comment(cfg.tweet_to_comment, username)
    if comment_data.get("commented"):
        r.comment_text = comment_data["tweet"].get("full_text", "")
        r.comment = comment_is_acceptable(comment_data["tweet"], cfg.min_comment_length)

    # Community
    r.community = check_community_member(cfg.community_id, username)

    r.completed = r.total()
    return r


cfg = CampaignConfig(
    brand_handle="YourBrand",
    tweet_to_retweet="https://x.com/YourBrand/status/111111111",
    tweet_to_quote="https://x.com/YourBrand/status/222222222",
    tweet_to_comment="https://x.com/YourBrand/status/333333333",
    community_id="1966045657589813686",
    required_hashtag="#YourLaunch",
)

result = verify_participant("participant123", cfg)
print(f"@{result.username}: {result.completed}/5 tasks done")

1人の参加者は5つの API リクエスト(タスクごとに1つ)かかります。Sorsa の一律の 毎秒20リクエスト のレート制限では、1つのワーカースレッドがシーケンシャルにおよそ毎秒4人の参加者を検証できます。提出ごとに1回検証を実行するほとんどのキャンペーンには、それで十分すぎる余裕です。


参加者をバルクで検証する {#verifying-participants-in-bulk}

キャンペーンが数千の参加者を持ち、それらをバッチで検証したい(たとえば当選者を発表する前に)とき、パターンはこう見えます。レート制限の処理、CSV の出力、そして再開可能な設計(各参加者の直後に行を書くので、クラッシュが進捗を失わない)に注目してください。

python
import csv
import time
from pathlib import Path

def verify_campaign_batch(usernames: list[str], cfg: CampaignConfig, output_file: str) -> None:
    fields = ["username", "follow", "retweet", "quote", "comment", "community",
              "completed", "quote_text", "comment_text"]

    already_done = set()
    out_path = Path(output_file)
    if out_path.exists():
        with out_path.open() as f:
            already_done = {row["username"] for row in csv.DictReader(f)}

    mode = "a" if out_path.exists() else "w"
    with out_path.open(mode, newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        if mode == "w":
            writer.writeheader()

        for i, username in enumerate(usernames):
            if username in already_done:
                continue
            try:
                r = verify_participant(username, cfg)
                writer.writerow({
                    "username": r.username,
                    "follow": r.follow,
                    "retweet": r.retweet,
                    "quote": r.quote,
                    "comment": r.comment,
                    "community": r.community,
                    "completed": r.completed,
                    "quote_text": r.quote_text,
                    "comment_text": r.comment_text,
                })
                f.flush()
                print(f"[{i+1}/{len(usernames)}] @{username}: {r.completed}/5")
            except requests.HTTPError as e:
                if e.response.status_code == 429:
                    print("Rate limit hit, sleeping 5s and retrying...")
                    time.sleep(5)
                    continue
                print(f"[{i+1}] @{username}: ERROR {e}")

            time.sleep(0.25)  # stay safely under 20 req/s with 5 reqs per participant


participants = open("entries.txt").read().splitlines()
verify_campaign_batch(participants, cfg, "campaign_results.csv")

このパターンは、シングルスレッドでおよそ毎時14,000人の参加者を検証します。2つか3つのワーカーにまたがって並列化すれば(依然としてグローバルな毎秒20リクエストの天井を尊重して)、毎時30,000人に届けます。10万エントリー未満のほとんどのキャンペーンには、シングルスレッドのシーケンシャルな検証が一晩で終わります。


アカウントの所有権を検証する {#verifying-account-ownership}

参加者が何かに当選できる前に、参加者が提出した X ハンドルを実際に所有することを証明したいかもしれません。このステップなしでは、誰でも有名なハンドルをフォームに貼り付けて報酬を主張できます。標準のパターン:ユニークなコードを生成し、参加者にそれを含むツイートを投稿するよう頼み、それから本人の最近のタイムラインでそのコードをチェックします。

python
import secrets

def generate_verification_code(prefix: str = "VERIFY") -> str:
    return f"{prefix}-{secrets.token_hex(4)}"


def verify_account_ownership(username: str, expected_code: str) -> bool:
    """Check if the user posted a tweet containing the verification code."""
    resp = requests.post(
        f"{BASE}/user-tweets",
        headers=HEADERS,
        json={"username": username},
        timeout=15,
    )
    resp.raise_for_status()
    tweets = resp.json().get("tweets", [])
    for tweet in tweets:
        if expected_code in tweet.get("full_text", ""):
            return True
    return False


# Workflow
code = generate_verification_code()
print(f"Ask the user to post a tweet containing: {code}")
# ... user posts the tweet ...
if verify_account_ownership("participant123", code):
    print("Account ownership confirmed.")
else:
    print("Code not found in recent tweets.")

これは、ほとんどの真剣な懸賞とアンバサダーのプラットフォームが使うのと同じ仕組みです。参加者は、投稿を一度確認するだけでよいため、望むなら検証の後にツイートを削除できます。


不正防止:アカウント品質のチェック {#anti-fraud-account-quality-checks}

自動化されたキャンペーンはボットを引き寄せ、その結果、大規模なクエストの仕組みを動かすプラットフォームはシビル防止に多く投資します。いくつかの API レベルのチェックが、完全なシビル検出システムを必要とせずに明らかな違反者を排除します。それぞれが /info への1つの追加の呼び出しです。

python
from datetime import datetime, timezone

def is_legitimate_account(
    username: str,
    min_age_days: int = 30,
    min_tweets: int = 10,
    min_followers: int = 5,
) -> tuple[bool, dict]:
    resp = requests.get(
        f"{BASE}/info",
        headers={"ApiKey": API_KEY},
        params={"username": username},
        timeout=15,
    )
    resp.raise_for_status()
    profile = resp.json()

    created = datetime.fromisoformat(profile["created_at"].replace("Z", "+00:00"))
    age_days = (datetime.now(timezone.utc) - created).days

    checks = {
        "account_age_ok": age_days >= min_age_days,
        "has_tweets": profile.get("tweets_count", 0) >= min_tweets,
        "has_followers": profile.get("followers_count", 0) >= min_followers,
        "not_protected": not profile.get("protected", False),
    }
    return all(checks.values()), checks

本番でこれを実行した3つの観察:

  1. 最小30日の年齢がほとんどの新鮮なボットアカウントを捕まえます。 ボットファームは通常アカウントをバッチで登録し、数日以内に使います。30日の下限が大多数を打ち落とします。キャンペーンが高価値なら90日に押し上げます。
  2. ゼロツイートのアカウントはほぼつねに偽物です。 最小5〜10の既存のツイートは、本物の人間の利用の強いシグナルです。
  3. フォロー対フォロワーの比率は思うより重要ではありません。 50フォロワーと800フォロー中の本物の人はよくあります(受動的な消費者)。比率を主要なフィルターとして使わないでください。

これらのしきい値は明らかなボットを安く排除します。さらに進んで参加者自身のフォロワーベースのどれだけが偽物または非アクティブに見えるかを採点したい高価値のキャンペーンには、偽フォロワーの監査 のガイドがそのより深いパスを案内します。

このチェックを、5つの検証チェックのいずれかを実行する前に適用します。is_legitimate_accountFalse を返すなら、どのみち拒否したであろう参加者に5つの検証リクエストを節約します。


影響力で重み付けした報酬の採点 {#influence-weighted-reward-scoring}

すべての参加者が同じ到達を持つわけではありません。50,000フォロワーのクリエイターからのリツイートは、50のアカウントからのものより、ブランドのキャンペーンにとって価値があります。まっすぐな修正は、各タスクのポイント値を参加者のフォロワー数の対数関数で重み付けすることです。

python
import math

BASE_POINTS = {"follow": 10, "retweet": 15, "quote": 25, "comment": 20, "community": 10}

def get_follower_count(username: str) -> int:
    resp = requests.get(
        f"{BASE}/info",
        headers={"ApiKey": API_KEY},
        params={"username": username},
        timeout=15,
    )
    resp.raise_for_status()
    return resp.json().get("followers_count", 0)


def calculate_weighted_points(result: ParticipantResult) -> dict:
    followers = get_follower_count(result.username)
    # log scaling: 100 followers -> 2x, 10K -> 4x, 1M -> 6x
    multiplier = max(1.0, math.log10(followers + 1))
    total = 0
    breakdown = {}
    for task, base in BASE_POINTS.items():
        if getattr(result, task):
            points = round(base * multiplier)
            breakdown[task] = points
            total += points
    return {"followers": followers, "multiplier": round(multiplier, 2),
            "breakdown": breakdown, "total": total}

結果:5つのタスクすべてを完了する50フォロワーのアカウントは約80ポイントを稼ぎます。同じタスクを完了する50,000フォロワーのアカウントは約380ポイントを稼ぎます。キャンペーンは、到達ゼロのアカウントと同じだけマイクロセレブに支払うことなく、到達に比例して報います。

暗号資産風味のキャンペーンには、フォロワー数の乗数を Sorsa Score に置き換えられます。それは、暗号資産の KOL、プロジェクト、VC の間でのアカウントの認知度を測ります。2つのアカウントは似たフォロワー数を持ちながら、一方が暗号資産ネイティブの声で他方が一般向けのアカウントなら、非常に異なる Sorsa Score を持ち得ます。


実践:14日で47,000人の参加者 {#in-practice-47000-participants-in-14-days}

協力したクリエイターマーケティングのエージェンシーは、消費者直販の家庭用品ブランドのために14日間の懸賞を立ち上げました。キャンペーンの仕組みは標準でした。ブランドをフォロー、ローンチのツイートをリツイート、ブランドのハッシュタグとともに引用、2つ目のツイートにコメント、そしてブランドの X コミュニティに参加、です。3人の当選者がそれぞれ、およそ$4,500相当の家具付きの部屋の模様替えを受け取ります。

提出はキャンペーンのランディングページ経由で来ました。14日目までに47,000のエントリーがありました。

名誉制度のチェックボックスで動かされたエージェンシーの以前のキャンペーンは、通常50〜60%の偽物または部分的な完了を見て、当選者を発表する前に数日の手動レビューを必要としました。今回は上の API 検証のパイプラインを使いました。実行からの数字:

  • 総提出47,000
  • 235,000の検証リクエスト(参加者あたり5つ)
  • 47,000の追加の /info 呼び出し、不正防止と影響力重み付けのステップのため
  • 総 API 使用量:約282,000リクエスト、キャンペーンの窓にわたって
  • 使用したプラン:Enterprise(月500Kリクエストで$899)
  • 31,200人の参加者 が5つのタスクすべてを通過
  • 8,400人の参加者 が3〜4のタスクを通過(部分賞の層に適格)
  • 7,400人の参加者 が完全に拒否(アカウント品質のチェックに失敗、または0〜2のタスクを完了)
  • 節約された手動モデレーションの時間:約120時間(同様の規模の過去のキャンペーンに基づくエージェンシーの推定)

この規模で検証を実行するコスト(Enterpriseプランの1か月)は、モデレーターの時間の1日未満でした。エージェンシーは今、動かすすべてのブランドのキャンペーンに同じパイプラインをテンプレートとして使います。

開示: Sorsa API は当社の製品で、上の数字は、1つの名指しされた契約ではなく、匿名化された複合的なクライアントの展開を記述しています。技術的な主張は正確で、コストの利点はフラットなリクエスト単位の料金の実際の性質です。自社のキャンペーンでは、ワークフローにコミットする前に小さなパイロットを実行してください。


参加者あたりのコスト {#cost-per-participant}

不正防止と影響力の採点を重ねた完全な5タスクの検証は、参加者あたり7つの API リクエストかかります。

  • /info に1リクエスト(不正防止に採点用のフォロワー数)
  • 5つの検証チェックに5リクエスト
  • アカウント所有権の検証に任意で1リクエスト(実装されていれば)

Sorsa はフラットレートの料金を使います。1 API 呼び出し = エンドポイントにかかわらず月次の枠からの1リクエストです。Proプラン(月額$199、100,000リクエスト)では、それは月におよそ14,000人の完全に検証された参加者です。Enterprise(月額$899、500,000リクエスト)では、約71,000人です。その天井を超えるカスタムプランが利用可能です。

キャンペーン規模必要なリクエスト推奨プランプラン価格
最大1,400人の参加者約10,000Starter月額$49
最大14,000人の参加者約100,000Pro月額$199
最大71,000人の参加者約500,000Enterprise月額$899
71,000人以上の参加者カスタム営業に連絡カスタム

Proプランのレートでは、完全な7リクエストの検証は参加者あたりおよそ $0.014 かかります。10,000人の参加者のキャンペーンには、それは API リクエストでおよそ$140です。

公式 X API との対比は、単一の見出しの価格ではなく課金モデルから来ます。X は取得したリソース単位で、投稿あたり$0.005、ユーザープロフィールあたり$0.010で課金し、直接のチェックエンドポイントを持たないため、アクションの検証は、リツイート者とフォロワーのリスト全体を取得してその中のすべての項目に支払うことを意味し、月200万投稿読み取りの上限と OAuth 2.0 の下でです。Sorsa は、根底のオーディエンスがどれだけ大きくても、/check-* 呼び出しあたり1つのフラットなリクエストを課金します。両方のプロバイダーのプラン別・エンドポイント別の完全な内訳は、Twitter API 料金ガイド にあり、まだ選択肢を比較検討しているなら、Twitter API の代替 の概要が、より広い分野をコストと機能で比較します。


始め方 {#getting-started}

動く検証チェックを数分で走らせられます。待つべき開発者アカウントの承認も、つなぐべき OAuth フローもありません。キーを作り、ApiKey ヘッダーに入れ、/check-follow を呼びます。すべてのアカウントは100回分の無料リクエスト、つまりクレジットカード不要・有効期限なしで40個のエンドポイントすべてをカバーする1回限りの枠で始まるため、何かを支払う前に小さなテストバッチを検証できます。その後、エントリープランは10,000リクエストで$49、すべての層ですべてのエンドポイントが含まれ、レート制限は一律の毎秒20リクエストです。

  • コードなしで API playground でエンドポイントを試します。
  • クイックスタート を読んで最初の認証済みリクエストを行います。
  • ドキュメントのエンドツーエンドのパイプラインには、キャンペーン検証の案内 に従います。
  • 月500,000リクエストを超える、またはより高いレート制限のキャンペーンには、営業に相談 してください。

検証エンドポイントとレスポンスの形の完全なメニューは、検証エンドポイントのリファレンス をご覧ください。


よくある質問 {#faq}

API で Twitter のいいねを検証できる?

いいえ。X は2024年6月にいいねを非公開にし、2026年時点で、公開でもサードパーティでもどの API も、あるユーザーが別のユーザーのツイートにいいねしたかに答えられません。これは Sorsa の制限ではなく、プラットフォームレベルの変更です。キャンペーンのテンプレートがまだいいねを求めるなら、そのタスクをリツイートかコメントに置き換えます。両方とも完全に検証可能なままで、より強いエンゲージメントのシグナルを運びます。

Twitter のアクションを検証するのに OAuth や開発者アカウントの承認は必要?

Sorsa API では不要です。認証は単一の ApiKey ヘッダーで、OAuth のハンドシェイクも、コールバック URL も、アプリ審査や審査待ちの行列もありません。公式 X API は OAuth 2.0 と承認済み開発者アプリを必要とし、取得したリソース単位で課金するため、規模での検証はフラットなリクエスト単位のモデルよりセットアップが遅く高価です。

数千のリツイートを持つツイートを誰かがリツイートしたかチェックできる?

はい。/check-retweet エンドポイントは呼び出しあたり100リツイートをスキャンし、ページネーションする next_cursor を返します。本ガイドの例のコードは5ページ(500リツイート)に制限し、参加者は通常プロンプトを受けてから数時間以内にリツイートするため、たいていそれで十分です。より深くスキャンする必要があるツイートには、ページの上限を上げれば、チェックは完全なリストを歩き続けます。

誰かが入力した Twitter のハンドルを実際に所有しているかどう検証する?

ユニークな短いコードを生成し、参加者にそれを含むツイートを投稿するよう頼み、それから user-tweets エンドポイントを使って本人の最近のタイムラインでそのコードをスキャンします。動く例が本ガイドのアカウント所有権のセクションにあります。これは、真剣な懸賞とアンバサダーのプラットフォームが、人々が管理しないハンドルを提出するのを防ぐために使う標準のパターンです。

参加者が非公開(保護された)アカウントを持つ場合はどうなる?

check-follow と check-quoted のレスポンスは user_protected フラグを含みます。それが true のとき、アカウントのフォローグラフは公開されず、フォローや引用をプログラムで確認できません。選択肢は、参加者に検証のためプロフィールを公開にするよう頼む、明確なメッセージでエントリーを拒否する、またはアカウント所有権の検証を実行して手動のオーバーライドで受け入れる、です。典型的なキャンペーンでは、参加者の1%未満が非公開アカウントを持ちます。

ボットが懸賞を悪用するのをどう防ぐ?

3つの API レベルの防御の層を使います。最小の年齢、ツイート数、フォロワーのための /info エンドポイント経由のアカウント品質のチェック、最小の長さと必須のハッシュタグのためのコメントと引用の品質のチェック、そしてどの報酬が出る前のアカウント所有権の検証です。これらのどれも完全なシビル検出システムではありませんが、一緒にほとんどのキャンペーンを枯渇させる楽勝のケースを取り除き、各拒否されたアカウントは、それに費やしたであろう検証リクエストも節約します。

エンゲージメントの検証は公式 X API を使うより安い?

検証には特に、はい。公式 X API は直接のチェックエンドポイントを持たないため、リツイート者やフォロワーのリスト全体を取得してリソース単位、投稿あたり$0.005、プロフィールあたり$0.010で、月200万投稿読み取りの上限の下で支払うことで、各答えを再構築します。Sorsa は、オーディエンスのサイズにかかわらずチェックあたり1つのフラットなリクエストを、$0.0049からリクエストあたり約$0.002まで課金するため、完全なキャンペーンの検証はコストの小さな何分の1かで動きます。

これらのチェックを非マーケティングのユースケースに使える?

はい。よくある非マーケティングの用途には、ロールを付与する前にユーザーがブランドをフォローすることを検証して非公開の Discord チャンネルへのアクセスをゲートすること、従業員やパートナーのソーシャル増幅のコンプライアンスの追跡、そしてアフィリエイトプログラムでのユーザー提出のアトリビューションの主張の検証があります。それぞれが同じ単一のブール値のチェックで、ただ懸賞の文脈の外で適用されるだけです。


監修:Keksich(Sorsa創業者、マーケター兼X APIリサーチャー)

本ガイドのまとめ方:Sorsa の検証エンドポイントをビルドして運用する Sorsa 自身の実地の作業、API 自体に対するライブの呼び出し、そしてエンドポイントとレスポンスの詳細のための Sorsa API のドキュメント に基づいています。コストの比較は、レート、200万投稿読み取りの上限、そして OAuth の要件のリファレンスとして、公式 X API の現行の従量課金の料金と方針のページを使います。2026年6月確認。