サマリ
Twitter の Search APIの仕様が、1.1 → 2 でかなり変わっていました。
仕様の全体像を掴むのに少し苦労したため、メモを残しておきます。
v1.1 と v2 の違い
ざっくり言うと
- サービスレベルの変更
- ツイート取得のメソッドの変更
の2つに分けられる。2つをそれぞれ把握しないと面食らう。
v2のサービスレベル
大きく分けて2レベル。
- 直近7日間のツイートを検索できる エンドポイント 「recent」
- https://api.twitter.com/2/tweets/search/recent
- 過去のツイート全てを対象として検索できるエンドポイント 「all」
の2つに分かれる。しかし、「all」エンドポイント は、Academic Research Production のみ利用可能らしい。
v1.1 では「Standard」「Premium」など、契約によってSearchの範囲が変わったが、v2からは学術利用か、一般的な利用か、で検索可能なツイートの期間が変わるようだ。
v2エンドポイントを利用するための設定
Twitterの開発者画面から「Project」を設定し、そこに紐づく形で「App」を作成する必要がある。
下記のドキュメントに詳しく書かれている。
ツイートの取得方法
v1.1 までは、エンドポイントへのコールに対して全ての情報が含まれたJSONが返された。
v2では、基本的に「ツイート」「ツイートID」の二種類しか返ってこない。ツイートしたユーザーの詳細情報を取得するためには、オプションパラメータを設定する必要がある。
わかりづらいので、「Qiita公式アカウントのツイートを直近10件を取得」という条件で比較してみる。
v1.1 のリクエストとレスポンス
v1.1 のリクエスト
https://api.twitter.com/1.1/search/tweets.json?q=from:Qiita&count=10
v1.1のレスポンス
{
"statuses": [
{
"created_at": "Thu Apr 29 06:30:07 +0000 2021",
"id": 1387655418776899586,
"id_str": "1387655418776899586",
"text": "800LGTM! | 低レイヤーを学ぶための技術書をまとめてみる by @hareku908 https://t.co/KvnBiq6ihY",
"truncated": false,
"entities": {
"hashtags": [],
"symbols": [],
"user_mentions": [
{
"screen_name": "hareku908",
"name": "hareku",
"id": 4765763846,
"id_str": "4765763846",
"indices": [
36,
46
]
}
],
"urls": [
{
"url": "https://t.co/KvnBiq6ihY",
"expanded_url": "https://bit.ly/3gMGLJt",
"display_url": "bit.ly/3gMGLJt",
"indices": [
47,
70
]
}
]
},
"metadata": {
"iso_language_code": "ja",
"result_type": "recent"
},
"source": "<a href=\"https://buffer.com\" rel=\"nofollow\">Buffer</a>",
"in_reply_to_status_id": null,
"in_reply_to_status_id_str": null,
"in_reply_to_user_id": null,
"in_reply_to_user_id_str": null,
"in_reply_to_screen_name": null,
"user": {
"id": 341374118,
"id_str": "341374118",
"name": "Qiita (キータ) 公式",
"screen_name": "Qiita",
"location": "Tokyo, Japan",
"description": "Qiita公式アカウントです。何かありましたら support@qiita.com までご連絡ください :) / Qiita Team @QiitaTeam / Qiita Jobs @QiitaJobs / 人気の投稿 @qiitapoi / Qiita Zine https://t.co/6dzvjAhEcx /",
"url": "https://t.co/kT1Qi08l6z",
"entities": {
"url": {
"urls": [
{
"url": "https://t.co/kT1Qi08l6z",
"expanded_url": "http://qiita.com",
"display_url": "qiita.com",
"indices": [
0,
23
]
}
]
},
"description": {
"urls": [
{
"url": "https://t.co/6dzvjAhEcx",
"expanded_url": "http://zine.qiita.com",
"display_url": "zine.qiita.com",
"indices": [
134,
157
]
}
]
}
},
"protected": false,
"followers_count": 33742,
"friends_count": 4,
"listed_count": 787,
"created_at": "Sun Jul 24 07:54:14 +0000 2011",
"favourites_count": 800,
"utc_offset": null,
"time_zone": null,
"geo_enabled": false,
"verified": false,
"statuses_count": 31376,
"lang": null,
"contributors_enabled": false,
"is_translator": false,
"is_translation_enabled": false,
"profile_background_color": "FCFCFC",
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
"profile_background_tile": false,
"profile_image_url": "http://pbs.twimg.com/profile_images/1201406146822557696/ewFFvnAa_normal.jpg",
"profile_image_url_https": "https://pbs.twimg.com/profile_images/1201406146822557696/ewFFvnAa_normal.jpg",
"profile_banner_url": "https://pbs.twimg.com/profile_banners/341374118/1608287649",
"profile_link_color": "55C500",
"profile_sidebar_border_color": "BDDCAD",
"profile_sidebar_fill_color": "DDFFCC",
"profile_text_color": "272E23",
"profile_use_background_image": false,
"has_extended_profile": false,
"default_profile": false,
"default_profile_image": false,
"following": null,
"follow_request_sent": null,
"notifications": null,
"translator_type": "none",
"withheld_in_countries": []
},
"geo": null,
"coordinates": null,
"place": null,
"contributors": null,
"is_quote_status": false,
"retweet_count": 1,
"favorite_count": 8,
"favorited": false,
"retweeted": false,
"possibly_sensitive": false,
"lang": "ja"
},
{
"created_at": "Thu Apr 29 06:20:01 +0000 2021",
"id": 1387652875547987969,
"id_str": "1387652875547987969",
"text": "100LGTM! | Hasuraで既存のPostgreSQLから爆速でGraphQL APIサーバーを構築する by @KawamataRyo https://t.co/2hSLn4XpxA",
"truncated": false,
"entities": {
"hashtags": [],
"symbols": [],
"user_mentions": [
{
"screen_name": "KawamataRyo",
"name": "Kawamata Ryo",
"id": 927515451457806337,
"id_str": "927515451457806337",
"indices": [
60,
72
]
}
],
"urls": [
{
"url": "https://t.co/2hSLn4XpxA",
"expanded_url": "https://bit.ly/3nAgEqj",
"display_url": "bit.ly/3nAgEqj",
"indices": [
73,
96
]
}
]
},
"metadata": {
"iso_language_code": "ja",
"result_type": "recent"
},
"source": "<a href=\"https://buffer.com\" rel=\"nofollow\">Buffer</a>",
"in_reply_to_status_id": null,
"in_reply_to_status_id_str": null,
"in_reply_to_user_id": null,
"in_reply_to_user_id_str": null,
"in_reply_to_screen_name": null,
"user": {
"id": 341374118,
"id_str": "341374118",
"name": "Qiita (キータ) 公式",
"screen_name": "Qiita",
"location": "Tokyo, Japan",
"description": "Qiita公式アカウントです。何かありましたら support@qiita.com までご連絡ください :) / Qiita Team @QiitaTeam / Qiita Jobs @QiitaJobs / 人気の投稿 @qiitapoi / Qiita Zine https://t.co/6dzvjAhEcx /",
"url": "https://t.co/kT1Qi08l6z",
"entities": {
"url": {
"urls": [
{
"url": "https://t.co/kT1Qi08l6z",
"expanded_url": "http://qiita.com",
"display_url": "qiita.com",
"indices": [
0,
23
]
}
]
},
"description": {
"urls": [
{
"url": "https://t.co/6dzvjAhEcx",
"expanded_url": "http://zine.qiita.com",
"display_url": "zine.qiita.com",
"indices": [
134,
157
]
}
]
}
},
"protected": false,
"followers_count": 33742,
"friends_count": 4,
"listed_count": 787,
"created_at": "Sun Jul 24 07:54:14 +0000 2011",
"favourites_count": 800,
"utc_offset": null,
"time_zone": null,
"geo_enabled": false,
"verified": false,
"statuses_count": 31376,
"lang": null,
"contributors_enabled": false,
"is_translator": false,
"is_translation_enabled": false,
"profile_background_color": "FCFCFC",
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
"profile_background_tile": false,
"profile_image_url": "http://pbs.twimg.com/profile_images/1201406146822557696/ewFFvnAa_normal.jpg",
"profile_image_url_https": "https://pbs.twimg.com/profile_images/1201406146822557696/ewFFvnAa_normal.jpg",
"profile_banner_url": "https://pbs.twimg.com/profile_banners/341374118/1608287649",
"profile_link_color": "55C500",
"profile_sidebar_border_color": "BDDCAD",
"profile_sidebar_fill_color": "DDFFCC",
"profile_text_color": "272E23",
"profile_use_background_image": false,
"has_extended_profile": false,
"default_profile": false,
"default_profile_image": false,
"following": null,
"follow_request_sent": null,
"notifications": null,
"translator_type": "none",
"withheld_in_countries": []
},
"geo": null,
"coordinates": null,
"place": null,
"contributors": null,
"is_quote_status": false,
"retweet_count": 0,
"favorite_count": 4,
"favorited": false,
"retweeted": false,
"possibly_sensitive": false,
"lang": "ja"
},
/* 以下、取得件数分のデータが表示される */
],
"search_metadata": {
"completed_in": 0.07,
"max_id": 1387655418776899586,
"max_id_str": "1387655418776899586",
"next_results": "?max_id=1387547179829575679&q=from%3AQiita&count=10&include_entities=1",
"query": "from%3AQiita",
"refresh_url": "?since_id=1387655418776899586&q=from%3AQiita&include_entities=1",
"count": 10,
"since_id": 0,
"since_id_str": "0"
}
}
v2 のリクエストとレスポンス
v2のリクエスト
https://api.twitter.com/2/tweets/search/recent?max_results=10&query=from:Qiita
v2のレスポンス
{
"data": [
{
"id": "1387652875547987969",
"text": "100LGTM! | Hasuraで既存のPostgreSQLから爆速でGraphQL APIサーバーを構築する by @KawamataRyo https://t.co/2hSLn4XpxA"
},
{
"id": "1387647837983412224",
"text": "300LGTM! | 機械学習×投資の無料ツールまとめ ~Awesome Algorithmic Trading~ by @cryptrader2020 https://t.co/UO3ZA8mRkj"
},
{
"id": "1387642808182452225",
"text": "4000LGTM! | 使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 by @jnchito https://t.co/3b255fR01l"
},
{
"id": "1387637775919419393",
"text": "50LGTM! | [Pythonによる科学・技術計算] やさしい分子動力学シミュレーション, 2次元系, NVEアンサンブル,熱・統計物理学 https://t.co/ELvBHrXVnx"
},
{
"id": "1387627710198030337",
"text": "50LGTM! | 【個人開発】ポモドーロテクニックとTodoリストを組み合わせた時間管理アプリを作りました by @miii01220 https://t.co/Y5u1rgHZAf"
},
{
"id": "1387625188330704897",
"text": "50LGTM! | ペアプロ・モブプロでメンバーが驚くほど成長した話 by @kojimadev https://t.co/aq8VwBL40h"
},
{
"id": "1387607576032985094",
"text": "1100LGTM! | SREやクラウドエンジニアが読むと良さげな本まとめ by @tmknom https://t.co/eihzWpp9H9"
},
{
"id": "1387589960555974657",
"text": "200LGTM! | iOS14でのIDFA取得 https://t.co/RD6dfMlbpZ"
},
{
"id": "1387547179829575680",
"text": "50LGTM! | ポストエフェクトクエスト - 波長方向の積分マジ大事という話 - https://t.co/v2CW7HqEhH"
},
{
"id": "1387544662286733313",
"text": "1600LGTM! | Linuxディレクトリ構造 https://t.co/xHjpczl4hd"
}
],
"meta": {
"newest_id": "1387652875547987969",
"oldest_id": "1387544662286733313",
"result_count": 10,
"next_token": "b26v89c19zqg8o3fosttqjg5j32790ogxv7mnb19xcghp"
}
}
v2 のレスポンスはかなりスリムになっていることが分かる。
そして、ツイート以外のデータは含まれていない。
v2で、ユーザー情報や、そのほかのデータを取得する場合は以下のようにリクエストを行う。
https://api.twitter.com/2/tweets/search/recent?max_results=10&tweet.fields=created_at&expansions=author_id&user.fields=name,username,url,description&query=from:Qiita
レスポンスはこうなる
{
"data": [
{
"text": "800LGTM! | 低レイヤーを学ぶための技術書をまとめてみる by @hareku908 https://t.co/KvnBiq6ihY",
"author_id": "341374118",
"created_at": "2021-04-29T06:30:07.000Z",
"id": "1387655418776899586"
},
{
"text": "100LGTM! | Hasuraで既存のPostgreSQLから爆速でGraphQL APIサーバーを構築する by @KawamataRyo https://t.co/2hSLn4XpxA",
"author_id": "341374118",
"created_at": "2021-04-29T06:20:01.000Z",
"id": "1387652875547987969"
},
{
"text": "300LGTM! | 機械学習×投資の無料ツールまとめ ~Awesome Algorithmic Trading~ by @cryptrader2020 https://t.co/UO3ZA8mRkj",
"author_id": "341374118",
"created_at": "2021-04-29T06:00:00.000Z",
"id": "1387647837983412224"
},
{
"text": "4000LGTM! | 使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 by @jnchito https://t.co/3b255fR01l",
"author_id": "341374118",
"created_at": "2021-04-29T05:40:01.000Z",
"id": "1387642808182452225"
},
{
"text": "50LGTM! | [Pythonによる科学・技術計算] やさしい分子動力学シミュレーション, 2次元系, NVEアンサンブル,熱・統計物理学 https://t.co/ELvBHrXVnx",
"author_id": "341374118",
"created_at": "2021-04-29T05:20:01.000Z",
"id": "1387637775919419393"
},
{
"text": "50LGTM! | 【個人開発】ポモドーロテクニックとTodoリストを組み合わせた時間管理アプリを作りました by @miii01220 https://t.co/Y5u1rgHZAf",
"author_id": "341374118",
"created_at": "2021-04-29T04:40:01.000Z",
"id": "1387627710198030337"
},
{
"text": "50LGTM! | ペアプロ・モブプロでメンバーが驚くほど成長した話 by @kojimadev https://t.co/aq8VwBL40h",
"author_id": "341374118",
"created_at": "2021-04-29T04:30:00.000Z",
"id": "1387625188330704897"
},
{
"text": "1100LGTM! | SREやクラウドエンジニアが読むと良さげな本まとめ by @tmknom https://t.co/eihzWpp9H9",
"author_id": "341374118",
"created_at": "2021-04-29T03:20:01.000Z",
"id": "1387607576032985094"
},
{
"text": "200LGTM! | iOS14でのIDFA取得 https://t.co/RD6dfMlbpZ",
"author_id": "341374118",
"created_at": "2021-04-29T02:10:01.000Z",
"id": "1387589960555974657"
},
{
"text": "50LGTM! | ポストエフェクトクエスト - 波長方向の積分マジ大事という話 - https://t.co/v2CW7HqEhH",
"author_id": "341374118",
"created_at": "2021-04-28T23:20:01.000Z",
"id": "1387547179829575680"
}
],
"includes": {
"users": [
{
"username": "Qiita",
"id": "341374118",
"name": "Qiita (キータ) 公式",
"url": "https://t.co/kT1Qi08l6z",
"description": "Qiita公式アカウントです。何かありましたら support@qiita.com までご連絡ください :) / Qiita Team @QiitaTeam / Qiita Jobs @QiitaJobs / 人気の投稿 @qiitapoi / Qiita Zine https://t.co/6dzvjAhEcx /"
}
]
},
"meta": {
"newest_id": "1387655418776899586",
"oldest_id": "1387547179829575680",
"result_count": 10,
"next_token": "b26v89c19zqg8o3fosttqjg5nlv28d3xdrvv4rx3cqk59"
}
}
レスポンスデータの情報が厚くなっていることが分かる。
v1.1 から v2 への移行
v1.1 ユーザー向けに、移行ガイドが公開されている。
…が、普通にAPIリファレンス見ながら書き直した方が早いかも。
APIリファレンスはこちら。
そのほか
Twitter API v2 開発者のために、Postman用のコレクションが公開・頒布されていました。
説明ページ
https://developer.twitter.com/en/docs/tools-and-libraries/using-postman
Postman側のコレクション配布ページ
https://documenter.getpostman.com/view/9956214/T1LMiT5U
Postmanを使っている人は、上記のコレクションを使うのがおすすめ。パラメータの設定が楽で、v2の仕様を手っ取り早く把握できます。
以上、参考になれば幸いです。