Twitter 動画 API:X の動画の MP4 URL を取得する
Twitter 動画の MP4 URL は、投稿のメディアデータに入っています。X はアップロードされた動画を最大5種類の MP4 にエンコードし、270pから1080p以上まで、各ファイルの URL をビットレートとともに掲載します。
結論: ビットレートが最も高い MP4 を選びます。GIF は音声のない1本の MP4 です。公式 X API が画質別のファイルを一覧に出すのは media.fields=variants を指定したときだけで、2026年11月22日からは動画投稿1,000件あたり$10.00です。Sorsa API は POST /tweet-info-bulk で各投稿の動画リンクを返します。1リクエストで100投稿、1,000件あたり$0.014からで、100回分の無料リクエストで最初の10,000件を取得できます。
- エンドポイント
POST /tweet-info-bulk- 1ページの件数
- 1リクエストあたり100投稿
- 料金
- 1,000件あたり$0.014から
#X が動画を保存する仕組み:画質別ファイルとビットレート
X は、投稿者がアップロードしたファイルをそのまま配信することはありません。すべての動画を、サイズ別の MP4 ファイル群と、HLS プレイリスト(再生中にプレーヤーがサイズを切り替えるための .m3u8 形式の索引)に再エンコードします。各 MP4 は音声を含む完全なファイルなので、どれか1つだけをダウンロードして単独で再生できます。1件の投稿なら、無料の Twitter 動画ダウンローダーに貼り付けたリンクからファイルを保存できます。このガイドは、コードでファイルを取得する方法を扱います。
2026年11月2日に、2017年から2026年までの23件の公開投稿の画質別ファイル一覧を読み、X が配信した72個の MP4 ファイルを計測しました。X が各ファイルに記載するビットレートは上限値で、実際のファイルは3倍から5倍小さくなります。
| 画質 | フレームサイズ(ピクセル) | X が記載するビットレート | 実測の平均ビットレート |
|---|---|---|---|
| 270p | 480 × 270 | 256 kbps | 約91 kbps |
| 360p | 640 × 360 | 832 kbps | 約233 kbps |
| 720p | 1280 × 720 | 2,176 kbps | 約626 kbps |
| 1080p | 1920 × 1080 | 10.4 Mbps | 約2,172 kbps |
270p
- フレームサイズ(ピクセル)
- 480 × 270
- X が記載するビットレート
- 256 kbps
- 実測の平均ビットレート
- 約91 kbps
360p
- フレームサイズ(ピクセル)
- 640 × 360
- X が記載するビットレート
- 832 kbps
- 実測の平均ビットレート
- 約233 kbps
720p
- フレームサイズ(ピクセル)
- 1280 × 720
- X が記載するビットレート
- 2,176 kbps
- 実測の平均ビットレート
- 約626 kbps
1080p
- フレームサイズ(ピクセル)
- 1920 × 1080
- X が記載するビットレート
- 10.4 Mbps
- 実測の平均ビットレート
- 約2,172 kbps
計測からわかったことは次のとおりです。
- ビットレートが最も高いものが最良のファイル。 MP4 のエントリをビットレート順に並べ、一番上を選びます。種類が
application/x-mpegURLのプレイリストのエントリにはビットレートがなく、ファイルではなく索引です。 - サイズはフレームの形で変わる。 縦長の動画は320 × 568から1080 × 1920まで、正方形は320 × 320から1080 × 1080までで、1936 × 1548のアップロードには25.1 Mbps の5つ目のファイルが付きました。
- 1080pでアップロードしても1080pのファイルが付くとは限らない。 2025年と2026年の1920 × 1080のアップロード7件のうち、2件は720pで止まり、2022年より前の投稿の大半は720pが上限です。
- プレイリストは音声と映像を分けている。 映像のストリームには音声がなく、音声は32、64、128 kbps の別トラックで提供され、動画によっては字幕も付きます。
GIF はもっと単純です。X は GIF 自身のサイズで、音声のないループ再生の MP4 を1つだけ保持し、ほかの画質もプレイリストもありません。
#Sorsa API で動画の MP4 URL を取得する
Twitter(X)API 兼スクレイピングツールである Sorsa API は、各投稿にメディアを付けて返します。動画、GIF、写真はそれぞれ、投稿の entities リストのエントリになっており、type、直接の link、preview 画像を持ちます。公式 X API が求める開発者アカウントと OAuth の代わりに、ヘッダーに API キー1つで済みます。
-
キーを取得する。 サインアップして、ダッシュボードから API キーをコピーします。最初の100リクエストは無料で、カードは不要です。
-
投稿のリンクを送る。
POST /tweet-info(新しいタブで開きます) は、tweet_linkに投稿の URL または ID だけを受け付けます。ターミナル curl -X POST "https://api.sorsa.io/v3/tweet-info" \ -H "ApiKey: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"tweet_link": "https://x.com/northfield_video_lab/status/1984519203377641472"}'レスポンス200 OK { "id": "1984519203377641472", "created_at": "2026-10-31T16:04:11Z", "full_text": "First ride on the new gravel loop.", "entities": [ { "type": "video", "link": "https://video.twimg.com/amplify_video/1984519110025023488/vid/avc1/480x270/Qm3vTb8xLwK2pRfA.mp4?tag=21", "preview": "https://pbs.twimg.com/amplify_video_thumb/1984519110025023488/img/Hx2LkP9aQwErT0bn.jpg" } ], "user": { "username": "northfield_video_lab", "display_name": "Northfield Video Lab", "media_count": 214 } } -
動画のエントリを読む。
typeがvideoで、linkが video.twimg.com にあるエンティティを残します。リンクに/tweet_video/というパスがあれば、GIF です。 -
ファイルをダウンロードする。 MP4 は X のサーバー上にある普通の HTTPS ファイルです。キーも Cookie も不要で、リクエストとして数えられることもありません。
import requests API = "https://api.sorsa.io/v3/tweet-info" HEADERS = {"ApiKey": "YOUR_API_KEY"} def video_links(post): r = requests.post(API, headers=HEADERS, json={"tweet_link": post}, timeout=60) r.raise_for_status() # 403: no key, or no requests left tweet = r.json() for e in tweet.get("entities") or []: if e.get("type") == "video" and "video.twimg.com" in e["link"]: yield tweet["id"], e["link"] links = video_links("1984519203377641472") for n, (post_id, link) in enumerate(links, 1): with open(f"{post_id}_{n}.mp4", "wb") as f: f.write(requests.get(link, timeout=120).content)
link は音声付きの MP4 で、どこでも再生できます。2026年11月2日に、Sorsa API で動いている当社の無料ダウンローダーで2本の動画を試したところ、どちらのリンクも X の270pのファイルでした。82秒の動画で1.1 MB なので、プレビュー、文字起こし、フレームのサンプリング、大規模なアーカイブには十分軽量です。GIF のリンクは唯一のファイルなので、そのまま完全な形で届きます。
preview は、フルサイズのフレームとしても使えます。.jpg を外したうえで ?format=jpg&name=orig を付けると、X がアップロードされたままのサイズでフレームを返します。テスト用の投稿の1つでは、1936 × 1548ピクセルでした。写真は同じリストに type が photo として入っています。1件の投稿でもアカウント全体でも、元のサイズで取得する方法は、Twitter 画像スクレイパーのガイドで解説しています。
投稿のリストなら、最大100件のリンクまたは ID を tweet_links に入れて POST /tweet-info-bulk(新しいタブで開きます) に送ります。レスポンスは {"tweets": [...]} で、どの投稿にも同じ entities が入っており、バッチ全体が1リクエストです。料金は1,000投稿あたり$0.014からです。
#アカウントや検索結果のすべての動画をダウンロードする
動画付きの投稿は検索で見つけられます。X の検索演算子 filter:native_video は、X にアップロードされた動画付きの投稿に絞り、card_name:animated_gif は GIF に絞ります。POST /search-tweets(新しいタブで開きます) は、条件に一致する投稿を1リクエストあたり約20件返し、2006年まで遡って検索できます。
| 用途 | クエリ |
|---|---|
| 1アカウントのすべての動画 | from:northfield_video_lab filter:native_video |
| 1アカウントのすべての GIF | from:northfield_video_lab card_name:animated_gif |
| あるテーマの1か月分の動画 | "gravel racing" filter:native_video since:2026-10-01 until:2026-11-01 |
| 人気の動画だけ | #bikepacking filter:native_video min_faves:1000 |
| 動画と YouTube のリンク | trail build filter:videos |
1アカウントのすべての動画
- クエリ
from:northfield_video_lab filter:native_video
1アカウントのすべての GIF
- クエリ
from:northfield_video_lab card_name:animated_gif
あるテーマの1か月分の動画
- クエリ
"gravel racing" filter:native_video since:2026-10-01 until:2026-11-01
人気の動画だけ
- クエリ
#bikepacking filter:native_video min_faves:1000
動画と YouTube のリンク
- クエリ
trail build filter:videos
すべての演算子を例付きで解説しているのが、Twitter 検索演算子のガイドです。次のスクリプトは、1つのクエリを最初のページから最後のページまで実行します。
-
スクリプトを保存する。
next_cursor(次のページの開始位置を示すトークン)を使って検索結果を順にたどり、カーソルが空になるまで続けます。各投稿の動画と GIF を<post id>_<n>.mp4に書き出し、各ファイルをvideos.csvに記録します。x_videos.py import csv import os import subprocess import sys import time import requests API = "https://api.sorsa.io/v3/search-tweets" HEADERS = {"ApiKey": "YOUR_API_KEY"} def search(body): while True: r = requests.post(API, headers=HEADERS, json=body, timeout=60) if r.status_code == 429: # over 20 requests a second: wait time.sleep(1) continue r.raise_for_status() # 403: no key, or no requests left return r.json() def videos(tweet): """(kind, link) for every video and GIF in a post.""" for e in tweet.get("entities") or []: link = e.get("link") or "" if e.get("type") == "video" and "video.twimg.com" in link: yield ("gif" if "/tweet_video/" in link else "video"), link def save(link, path): with requests.get(link, stream=True, timeout=120) as r: r.raise_for_status() with open(path, "wb") as f: for chunk in r.iter_content(1 << 16): f.write(chunk) def save_hd(post_url, n, path): """The post's highest-bitrate MP4 through yt-dlp; False if yt-dlp fails.""" cmd = ["yt-dlp", "-f", "best[protocol=https]", "--playlist-items", str(n), "-o", path, post_url] done = subprocess.run(cmd, capture_output=True).returncode == 0 return done and os.path.exists(path) def run(query, folder, hd=False): os.makedirs(folder, exist_ok=True) state = os.path.join(folder, "cursor.txt") body = {"query": query, "order": "latest"} if os.path.exists(state): # resume a stopped run body["next_cursor"] = open(state).read().strip() log_path = os.path.join(folder, "videos.csv") with open(log_path, "a", newline="", encoding="utf-8") as f: log = csv.writer(f) while True: page = search(body) for t in page.get("tweets", []): for n, (kind, link) in enumerate(videos(t), 1): path = os.path.join(folder, f"{t['id']}_{n}.mp4") if os.path.exists(path): continue url = f"https://x.com/{t['user']['username']}/status/{t['id']}" if not (hd and kind == "video" and save_hd(url, n, path)): save(link, path) log.writerow([t["id"], t["created_at"], kind, os.path.getsize(path), path]) cursor = page.get("next_cursor") if not cursor: break body["next_cursor"] = cursor with open(state, "w") as s: s.write(cursor) if os.path.exists(state): os.remove(state) if __name__ == "__main__": run(sys.argv[1], sys.argv[2], hd="--hd" in sys.argv[3:]) -
クエリとフォルダを指定して実行する。
python x_videos.py "from:northfield_video_lab filter:native_video" northfieldを実行すると、そのアカウントの動画がフォルダnorthfieldに保存されます。403 はキーがない、またはリクエスト残量がないこと、429 は1秒に20リクエストを超えたことを示し、スクリプトは1秒待って再試行します。 -
いつでも再実行できる。 すでにディスクにあるファイルはスキップされ、カーソルはページごとに保存されます。止まった実行は続きから再開し、再実行すれば新しい動画だけが追加されます。
アカウント全体の動画でも、リクエストは少なく済みます。検索が返すのは動画付きの投稿だけなので、1ページ約20投稿なら、動画300本のアカウントは約15リクエストです。テキストやそのほかの投稿フィールドをスプレッドシートに入れたい場合は、ユーザーの全ツイートをダウンロードをご覧ください。
#投稿の720pや1080pのファイルを取得する
投稿の720pや1080pのファイルが必要なら、そのリンクを、オープンソースのコマンドラインダウンローダーである yt-dlp に渡します。yt-dlp は X にログインせずに公開投稿を読み取れます。次のフォーマット指定で、ビットレートが最も高い MP4 が選ばれます。
yt-dlp -f "best[protocol=https]" -o "%(id)s.%(ext)s" "https://x.com/northfield_video_lab/status/1984519203377641472"
上のスクリプトに --hd を付けると、すべての動画でこの処理を行い、yt-dlp が失敗したときは API のリンクで保存します。82秒のテスト動画では、yt-dlp 2026.10.26 が1920 × 1080のファイルを選びました。-F で表示したフォーマット一覧では、このファイルは約100 MiB とされていました。一覧は記載されたビットレートに長さを掛けて算出するためで、実際にダウンロードしたファイルは22.2 MB でした。yt-dlp は自分の IP アドレスから1投稿ずつ取得するので、まとめて探すのは Sorsa API に任せ、yt-dlp には必要な HD ファイルだけを取得させます。
知っておきたいこと
公式 X API も、指定すれば画質別ファイルの一覧全体を1回のレスポンスで返します。投稿の取得に
expansions=attachments.media_keysとmedia.fields=variantsを付けると、各動画がincludes.mediaに、bit_rate、content_type、urlを持つvariants配列付きで返ります。開発者アカウントと OAuth が必要で、2026年11月22日からは、このレスポンスに含まれるメディアオブジェクト1件ごとに、投稿読み取りの$0.005に加えて$0.005が課金されます。
#API で扱う Twitter の GIF
X に投稿された GIF は、MP4 として返ります。X は投稿時に、すべての GIF を短い、音声のない、ループ再生の動画に変換するからです。リンクは video.twimg.com/tweet_video/ 上にあり、X は他のサイズを保持しないので、その1ファイルが存在する最良のコピーです。X 自身のデータでは種類が animated_gif、ビットレートが0です。Sorsa API の entities では、video として届き、リンクに /tweet_video/ というパスが含まれます。
.gif ファイルに戻すには、色をきれいに保つために、パレット生成のパスを付けて ffmpeg を実行します。
ffmpeg -i 1211797914437259264_1.mp4 \
-vf "fps=15,split[a][b];[a]palettegen[p];[b][p]paletteuse" \
-loop 0 1211797914437259264.gif
ファイルはかなり大きくなります。テストでは、83 KB の MP4 が、同じ498 × 272ピクセルのまま1.76 MB の GIF になり、21倍の大きさでした。保存とプレーヤーには MP4 を残し、ページやチャットで必要な場面にだけ GIF を作ってください。コードなしで GIF を1つ保存するなら、無料の Twitter GIF ダウンローダーが、投稿のリンクから保存します。
#リクエスト数と保存容量で見る費用
Sorsa API では、投稿の費用は、どんなメディアが付いていても同じです。ID で取得する投稿は1,000件あたり$0.014から(1リクエスト100件)、検索で見つける投稿は1,000件あたり$0.07から(1リクエスト約20件)です。動画ファイルは X のサーバーからダウンロードされ、リクエストとして数えられません。公式 X API では、すべての動画がメディア拡張で届き、2026年11月22日からはX が拡張オブジェクトに(新しいタブで開きます)標準レートで課金します。動画投稿1件は、投稿の$0.005にメディアの$0.005を加えた額で、1,000件あたり$10.00です。
| 動画リンクの取得方法 | 必要なもの | 動画投稿1,000件あたりの料金 |
|---|---|---|
| Sorsa API、ID 指定の投稿 | API キー1つ | $0.014から |
| Sorsa API、検索 | API キー1つ | $0.07から |
| 公式 X API | 開発者アカウントと OAuth | 2026年11月22日から$10.00 |
Sorsa API、ID 指定の投稿
- 必要なもの
- API キー1つ
- 動画投稿1,000件あたりの料金
- $0.014から
Sorsa API、検索
- 必要なもの
- API キー1つ
- 動画投稿1,000件あたりの料金
- $0.07から
公式 X API
- 必要なもの
- 開発者アカウントと OAuth
- 動画投稿1,000件あたりの料金
- 2026年11月22日から$10.00
公式 X API
$10,000
動画投稿100万件の場合
動画投稿1件 $0.010、11月22日から
Sorsa API
$14から
動画投稿100万件の場合
1,000件あたり$0.014から
コストは714分の1
公式 X API の従量課金の料金(新しいタブで開きます)は、請求サイクルあたり300万件の投稿読み取りでも止まり、それを超えると X が販売するのは Enterprise 契約だけです。Sorsa API は全プランで毎秒20リクエストなので、100投稿のバッチなら毎秒2,000件の動画投稿を取得でき、失敗したリクエストは課金されません。
動画のアーカイブでは、保存容量のほうが大きな費用になり、件数よりも画質で大きく変わります。計測した実際の平均ビットレートでは、30秒の動画1,000本が、270pで約0.3 GB、720pで2.3 GB、1080pで8.1 GB です。長さと本数を入力してみてください。
| 画質 | 動画1本あたり | 全動画 | 上限値 |
|---|---|---|---|
| 270p | 0.34 MB | 341 MB | 0.96 MB |
| 360p | 0.87 MB | 874 MB | 3.1 MB |
| 720p | 2.3 MB | 2.3 GB | 8.2 MB |
| 1080p | 8.1 MB | 8.1 GB | 38.9 MB |
動画1本あたり: 画質ごとの実測ビットレートの中央値(91、233、626、2,172 kbps)。上限値: Xが公開しているビットレート(256、832、2,176 kbps、10.4 Mbps)に長さを掛けた値です。
動画1本あたりのサイズは、2026年11月2日にXのサーバー上で測定した63個のMP4ファイルの、実測の平均ビットレートに基づきます。上限値には、Xが公開しているビットレートを使っています。
#コードで X の動画を取得し始める
コードで X の動画や GIF の MP4 URL を取得するなら、Sorsa API を使ってください。1リクエストで最大100投稿を、各動画のリンク付きで、1,000件あたり$0.014からで取得できます。検索では、2006年まで遡って、アカウントやテーマのすべての動画が見つかり、自分の X アカウントが関わることはありません。スクリプトをコピーしてキーを入れれば、100回分の無料リクエストで、最初の10,000件の動画投稿のリンクが手に入ります。
#よくある質問
公式 X API は Twitter の動画 URL を返しますか?
はい。投稿の取得または検索に expansions=attachments.media_keys と media.fields=variants を追加すると、includes.media の各動画が、MP4 の画質別ファイルを bit_rate と url 付きで一覧に出すので、ビットレートが最も高いものを選びます。この2つのパラメータがないと、プレビュー画像しか返りません。開発者アカウントと OAuth が必要で、2026年11月22日からは、メディアオブジェクト1件ごとに$0.005が、投稿読み取りの$0.005に加算されます。
Twitter の動画 URL を取得するには、どの API を使えばよいですか?
Sorsa API を使ってください。POST /tweet-info-bulk は、1リクエストで最大100投稿を、各動画の MP4 リンクとプレビュー付きで、1,000投稿あたり$0.014からで返します。公式 X API は、2026年11月22日から動画投稿1,000件あたり$10.00です。Sorsa API に必要なのは、開発者アカウントと OAuth ではなく API キー1つで、検索なら2006年まで遡って動画を見つけられ、全プランで毎秒20リクエストに対応します。
ダウンロードした Twitter の動画がぼやけるのはなぜですか?
おそらく、小さい画質のファイルを保存したためです。X は各動画を最大5つの MP4(270pから1080p以上)として保持しており、ツールや API によっては、最初の、あるいは最も軽いものを渡します。一覧でビットレートが最も高い MP4 を選んでください。最良のファイルでも720pなら、そのアップロードには1080pのファイルが付いていません。2025年と2026年の1080pのアップロード7件のうち、2件が720pで止まっていました。
非公開アカウントや削除された投稿の動画をダウンロードできますか?
できません。非公開アカウントの投稿は、承認したフォロワーにしか見えないため、Sorsa API を含め、どの API でも動画を取得できず、そのリクエストが課金されることもありません。削除された投稿は X の検索や取得から消えるので、どの API もそれを一覧にできません。削除前に保存したコピーだけが、動画を残しています。
X の動画をダウンロードするのは合法ですか?
はい。研究、モデレーション、アーカイブのために公開動画を保存することは合法で、広く行われています。著作権は各動画を作った人に残るため、ファイルを公開したり再販したりする前に、所有者の許可を得てください。Sorsa API は独自の API を通じて公開投稿を読み取るため、自分の X アカウントは関わりません。
#検証方法
2026年11月2日に、2017年から2026年までの23件の公開動画・GIF 投稿について X が公開している画質別ファイルの一覧を読み、video.twimg.com が配信した75個の MP4 ファイルのうち72個を計測しました。1本の動画については、HLS プレイリストも読み、ログアウト状態の yt-dlp 2026.10.26 でフォーマット一覧も確認しました。うち3件の投稿を、Sorsa API で動いている当社の無料ダウンローダーに通し、返されるリンクを確認しました。スクリプトは、実際のリンクを返す API の代役に対して実行し、ffmpeg のコマンドは実際の GIF ファイルで実行しました。エンドポイントとフィールドは公開 Sorsa API リファレンスに、X のメディアフィールドと料金は、同日に読んだ docs.x.com と2026年10月23日付の X のお知らせに基づいています。
出典
- Sorsa API reference: Tweet Data(新しいタブで開きます)
- Sorsa API reference: Tweet Data (Batch)(新しいタブで開きます)
- Sorsa API reference: Search Tweets(新しいタブで開きます)
- X API data dictionary: media object(新しいタブで開きます)
- X API pricing(新しいタブで開きます)
- X API PPU: Expansions Update, October 23, 2026(新しいタブで開きます)
- yt-dlp on GitHub(新しいタブで開きます)