色々自動化したい。
そんなお年頃のわたくし。
今回はGoogleスプレッドシートとGAS(Google Apps Script)、それにTwitter API(X API)を使って、X(旧Twitter)へ自動ツイートする仕組みを作ってみたというお話。
Xを再開することにしたのでブログ過去記事を自動ツイートしたい
TwitterがXになって久しいですが。
イーロン・マスク氏の雰囲気そのままに、Xはゴタゴタしていたので、なんだかもう使いたくなくなってBlueskyに移行。
そのままBlueskyでブログの更新通知などやったりもしていたものの、結局イマイチだなーとXに戻ることに。
XにはTwitter時代のアカウントがあるわけですが、なんとなく、心機一転新規アカウントでやろう、と。
そこで、1000記事以上あるブログの記事を、自動で日々ツイートしたくなった、というのが経緯。
XになってからAPI周りも色々変わった。
そんなこんなで、お勉強も兼ねて、構築してみたわけです。
Xへの自動投稿:GAS × スプレッドシート × Twitter API
まずは全体像から。
• 投稿文の管理はGoogleスプレッドシート
• GASでランダムに投稿文を抽出してツイート
• Twitter(X)APIのOAuth 1.0aで投稿
• 投稿は朝10時と夜7時の1日2回、時間トリガーで実行
Twitter APIの認証まわりはOAuth 1.0aで対応
最近のX APIでは、OAuth 2.0のApp-onlyではツイートできず。
そのため、古き良きOAuth 1.0a(アクセストークンによるユーザー認証)で実装。
ま、これもまた時期に使えなくなる気がしてる。
※2025年10月現在、動作確認はできております。
X(旧Twitter)へ自動投稿するには、APIの認証が必須。
今回は OAuth 1.0a(ユーザーコンテキスト) を使って、投稿権限付きのトークンを取得する。
1. X Developer Portal にアクセスする
まずは開発者ポータルに自動ツイートをするXアカウントでログイン。

2. アプリを作成する

1. 「Projects & Apps」→「Overview」に移動
2. 「+ Create App」または「+ Add App」ボタンをクリック
3. アプリ名(例:AutoBlogPoster)を入力して作成
自分の場合課金なしのfreeなのでプロジェクトはすでに存在。
課金しているユーザはプロジェクト自体も作れるのかも。
3. 認証設定(Authentication Settings)を変更する

OAuth 1.0aを使って投稿するには、アプリの権限設定が必要です。
1. 「Projects & Apps」から作ったアプリを選択し、User authentication settingsをEdit
2. 以下のように設定する
・App permissions → Read and Write
・Type of App → Web App, Automated App or Bot
・Callback URI / Redirect URL → https://example.com/callback(ダミーでOK)
・Website URL → 自分のブログなど(https://yourblog.comなど)
3. 「Save」ボタンで保存
4. Access Token / Access Token Secret を取得する

User Auth 設定が完了したら、アクセストークンを発行する。
1. 「Projects & Apps」から作ったアプリを選択し、上部タブの Keys and tokens を開く
2. 「API Key and Secret」「Access Token and Secret」で「Generate」もしくは「Regenerate」ボタンを押して、情報を取得する
後ほどGASで使うので、情報はメモしておく
補足:トークン再発行のタイミング
App Permissions(Read Only → Read and Writeなど)を変更した場合は、Access Token / Secret を再発行する必要がある。
以前のトークンはそのままでは権限が不足していてエラーでハマる原因に。
スプレッドシートにツイート内容をまとめる
投稿する内容は、スプレッドシートの1列目に書くだけ。

ブログの過去記事です!
タイトルタイトルタイトルタイトルタイトル
https://aaa.com/xxx/
#タグ1 #タグ2
こんな感じで内容は自由に。
また、シート名は「tweets」に設定。
GAS(Google Apps Script)で自動ツイート実行

スプレッドシートの拡張機能からApps Scriptへ。
// == 概要 ==
// スプレッドシートのランダムな行から投稿文を選び、
// 毎日2回(朝10時/夜7時)自動でX(旧Twitter)へ投稿
// == 設定項目 ==
const consumerKey = "YOUR_API_KEY"; // Twitter API Key
const consumerSecret = "YOUR_API_SECRET"; // Twitter API Secret
const accessToken = "YOUR_ACCESS_TOKEN"; // Twitter Access Token
const accessSecret = "YOUR_ACCESS_SECRET"; // Twitter Access Token Secret
const SPREADSHEET_ID = "YOUR_SPREADSHEET_ID"; // スプレッドシートID
const SHEET_NAME = "tweets"; // シート名(例:"tweets")
// == CryptoJSの読み込み(OAuth署名用) ==
eval(UrlFetchApp.fetch("https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js").getContentText());
// == メイン関数(ランダム投稿) ==
function postRandomTweet() {
const sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(SHEET_NAME);
const data = sheet.getDataRange().getValues().flat().filter(row => row && row !== "");
if (data.length === 0) {
Logger.log("📭 投稿できるデータがありません。");
return;
}
const randomIndex = Math.floor(Math.random() * data.length);
const tweetText = data[randomIndex];
postToTwitter(tweetText);
}
// == X(旧Twitter)へ投稿 ==
function postToTwitter(tweetText) {
const apiUrl = "https://api.twitter.com/2/tweets";
const method = "POST";
const nonce = Utilities.getUuid().replace(/-/g, "");
const timestamp = Math.floor(Date.now() / 1000).toString();
const oauthParams = {
oauth_consumer_key: consumerKey,
oauth_nonce: nonce,
oauth_signature_method: "HMAC-SHA1",
oauth_timestamp: timestamp,
oauth_token: accessToken,
oauth_version: "1.0",
};
const baseParams = Object.keys(oauthParams)
.sort()
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(oauthParams[key])}`)
.join("&");
const baseString = [
method,
encodeURIComponent(apiUrl),
encodeURIComponent(baseParams)
].join("&");
const signingKey = `${encodeURIComponent(consumerSecret)}&${encodeURIComponent(accessSecret)}`;
const signature = CryptoJS.HmacSHA1(baseString, signingKey).toString(CryptoJS.enc.Base64);
oauthParams.oauth_signature = signature;
const authHeader = "OAuth " + Object.keys(oauthParams)
.sort()
.map(key => `${encodeURIComponent(key)}="${encodeURIComponent(oauthParams[key])}"`)
.join(", ");
const payload = JSON.stringify({ text: tweetText });
const res = UrlFetchApp.fetch(apiUrl, {
method: "POST",
contentType: "application/json",
payload: payload,
muteHttpExceptions: true,
headers: {
Authorization: authHeader,
},
});
const code = res.getResponseCode();
const body = res.getContentText();
if (code === 201 || code === 200) {
Logger.log("✅ 投稿成功: " + tweetText);
} else {
Logger.log("❌ 投稿失敗 (" + code + "): " + body);
}
}
// == テスト投稿用 ==
function testTweet() {
postToTwitter("テスト投稿!🧪 #自動投稿テスト");
}
こちらがスクリプト。
const consumerKey = "YOUR_API_KEY";
const consumerSecret = "YOUR_API_SECRET";
const accessToken = "YOUR_ACCESS_TOKEN";
const accessSecret = "YOUR_ACCESS_SECRET";
ここにはX Developer Portalで作ったアプリから取得した情報を入れる。
const SPREADSHEET_ID = "YOUR_SPREADSHEET_ID";
const SHEET_NAME = "tweets";
ここにはスプレッドシートの情報を入れる。
https://docs.google.com/spreadsheets/d/ここがSPREADSHEET_ID/edit?gid=0#gid=0
SPREADSHEET_IDは、スプレッドシートのURLの上記箇所。
スプレッドシートの任意の行の1列目からランダムにツイート文を取得し、Xへ投稿する。
OAuth 1.0aによる署名処理にはCryptoJSをCDNから読み込み、API認証と投稿処理をGASだけで完結。
スプレッドシートに投稿文を1行ずつ並べておくだけでOK。
トリガー設定:朝10時と夜7時に自動実行

GASの「時間主導型トリガー」を使えば、定時投稿も簡単。
• 10:00 → 朝のツイート
• 19:00 → 夜のツイート
それぞれ postRandomTweet() 関数を実行するようトリガーを設定しておくだけ。
自分の場合は上記設定としているが、こちらも好みの設定でOK。
まとめ
「ブログを自動でXに投稿したい」
そんな思いから始めた今回の仕組み作りですが、GASとX APIを使えば、意外と簡単に構築できます。

GASは無料でもかなり使えるし、Twitter APIも書き込み上限は500件/月なので大量に投稿するでもしなければ無料枠でいけちゃいます。
スプレッドシート × GAS × X API。
なかなか便利だな、と。