Salesforceの取引先をスプレッドシートへ書き出す手順
Salesforceのレコードをスプレッドシートで見たい場面があります。Apps ScriptからREST APIのクエリを呼び、2,000件を超える結果も取り切ってシートへ書き込む実装を説明します。
なぜSalesforceのレコードをシートに写すのか
Salesforceに入っている取引先のデータを、Salesforceのライセンスを持たない人と共有したい、あるいは表計算で加工してから使いたい場面があります。Apps ScriptからSalesforceのREST APIを呼び、結果をGoogleスプレッドシートへ書き込めば、閲覧用のコピーを都度作れます。
認証はApps ScriptからSalesforceを呼ぶときと同じ
Apps ScriptからSalesforceへアクセストークンを取るところは、取引先を登録する連携と同じ考え方です。ユーザー名パスワードフローは、Summer '23以降に作成された組織では既定でブロックされています。ユーザーの操作を挟まない定期連携なので、ここでもOAuth 2.0クライアントクレデンシャルフローを使います。外部クライアントアプリでクライアントクレデンシャルフローを有効化し、クライアントIDとシークレットはApps Scriptのスクリプトプロパティに保存して、ソースには書きません。
クエリ結果は1回で取り切れるとは限らない
SalesforceのREST APIでSOQLを実行すると、結果は最大2,000件ずつ返ってきます。件数が多いクエリでは、1回のレスポンスにすべてのレコードが入っていません。レスポンスの done が false のとき、nextRecordsUrl に続きを取るためのURLが入っています。この値を使って、done が true になるまで呼び続ける必要があります。
⚠️ ここをwhile文で無限に回す実装にすると、done を見ずに同じ条件で呼び続けてしまい、止まらなくなります。必ず done を終了条件にします。
Apps Scriptの実装
function getAccessToken_() { var props = PropertiesService.getScriptProperties(); var response = UrlFetchApp.fetch( 'https://xxxx.my.salesforce.com/services/oauth2/token', { method: 'post', payload: { grant_type: 'client_credentials', client_id: props.getProperty('SF_CLIENT_ID'), client_secret: props.getProperty('SF_CLIENT_SECRET') }, muteHttpExceptions: true } ); return JSON.parse(response.getContentText()); }
スクリプトプロパティのクライアントクレデンシャルで、アクセストークンを取得します。
function fetchAccounts_() { var auth = getAccessToken_(); var headers = { Authorization: 'Bearer ' + auth.access_token }; var soql = 'SELECT Id, Name, CreatedDate FROM Account ORDER BY CreatedDate DESC'; var url = auth.instance_url + '/services/data/v67.0/query/?q=' + encodeURIComponent(soql); var rows = []; while (url) { var response = UrlFetchApp.fetch(url, { headers: headers, muteHttpExceptions: true }); var result = JSON.parse(response.getContentText()); result.records.forEach(function (record) { rows.push([record.Id, record.Name, record.CreatedDate]); }); url = result.done ? null : auth.instance_url + result.nextRecordsUrl; } return rows; }
nextRecordsUrlがある間、doneがtrueになるまでクエリ結果を取り切ります。
function syncAccountsToSheet() { var rows = fetchAccounts_(); if (rows.length === 0) { return; } var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Account'); var startRow = sheet.getLastRow() + 1; sheet.getRange(startRow, 1, rows.length, rows[0].length).setValues(rows); }
取得した行を、Accountシートの末尾へ追記します。
fetchAccounts_ は done が true になるまでループし、nextRecordsUrl が返ってこなくなった時点で終わります。syncAccountsToSheet は取得した行を、既存の行の下へ追記します。同じ範囲を上書きしたい場合は、書き込み前にシートをクリアする処理を足します。
動作確認
Salesforce側にテストレコードを作る
取引先を数件作成しておきます。件数が少ないうちは、ページングの動きまでは確認できません。
スクリプトを実行する
Apps Scriptのエディタで関数「syncAccountsToSheet」を選び、実行します。
シートを確認する
Accountシートの末尾に、取引先のId・Name・CreatedDateが追記されていることを確かめます。
件数が多いときの動きを確かめる
2,000件を超える取引先がある組織では、実行ログで
fetchAccounts_が複数回リクエストを送っていることを確認します。
ここで間違えやすい
| 間違い | 何が起きるか |
|---|---|
done を見ずにループを続ける | 終了条件がなく、止まらなくなります |
nextRecordsUrl に instance_url を付け足さない | 相対パスのままリクエストしてしまい、失敗します |
SOQLの文字列をencodeURIComponentせずに送る | スペースや記号を含むクエリが正しく渡りません |
| ユーザー名パスワードフローのままにする | 新規組織では既定でブロックされ、認証エラーになります |
| 書き込み前にシートをクリアせず追記し続ける | 実行のたびに同じデータが重複して増えていきます |
確認した環境
- 2026年9月/Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、クエリ結果のページングとユーザー名パスワードフローの既定ブロックを確認しています
- コードはAPIバージョン67.0で書いています
まとめ
- SalesforceのREST APIによるクエリは、最大2,000件ずつしか返りません
doneがtrueになるまでnextRecordsUrlを追い続け、取り切ってから処理します- 認証は取引先登録の連携と同じく、クライアントクレデンシャルフローに直します
- クライアントIDとシークレットはスクリプトプロパティに保存し、ソースには書きません
- シートへの書き込みは、追記か上書きかを決めてから実装します
Salesforceの導入・運用についてご相談ください
導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。