· HARUBOUYA · dataflow · 15 min read
SalesforceのデータをAPIでGCPに持ってきてレポートにする
標準レポートでは他のデータと突き合わせられません。接続アプリケーションの作り方から、SOQLでの取得、ページング、BigQueryへの取り込み、上限の避け方までを順に書きます。

Salesforceには標準のレポート機能があります。それでも外に出したくなる理由は1つで、他のデータと突き合わせられないからです。受注、在庫、Web行動、広告の数字と並べたい瞬間に、Salesforceの中だけでは完結しません。
ここでは、APIでデータを取り出し、BigQueryに入れて、ダッシュボードにするまでを順に書きます。
全体像
Cloud Scheduler(毎日決まった時刻に起動)
↓
Cloud Run(Salesforce の REST API を呼ぶ)
↓
Cloud Storage(取得した生データを日付つきで置く)
↓
BigQuery(読み込む。日付で分割して保持)
↓
Looker Studio(ダッシュボードとして表示)一度に作らないでください。まず1オブジェクトだけを手で取ってBigQueryに入れるところまで通します。そこから自動化します。
なお、BigQuery Data Transfer Service にもSalesforceからの取り込み機能があります。要件が単純なら自前で作らない選択肢もあるため、着手前に公式ドキュメントで対応範囲を確認します。ここでは自前で組む場合の話を書きます。
1. 接続アプリケーションを作る
APIでつなぐには、Salesforce側に「このプログラムからのアクセスを許可する」という登録が要ります。これを接続アプリケーションと呼びます。
手順
- Salesforceの右上の歯車から「設定」を開きます
- 左メニューで「アプリケーション」→「アプリケーションマネージャ」を開きます
- 右上の「新規接続アプリケーション」を押します
- 必須項目を入力します
| 項目 | 入れるもの |
|---|---|
| 接続アプリケーション名 | 用途が分かる名前(例:BigQuery連携) |
| API参照名 | 名前を入れると自動で入ります。そのままでよいです |
| 取引先責任者メール | 管理者のメールアドレス |
- 「OAuth設定の有効化」にチェックを入れます
- コールバックURLを入れます。
https://で始まるURLが必要 - 「許容するOAuthの範囲」で必要な権限を選ぶ(データへのアクセスと管理、など)
- 保存します。「変更が有効になるまで最大10分かかる場合があります」と出るので待ちます
- 作成した接続アプリケーションの管理画面で、コンシューマ鍵とコンシューマの秘密を控えます
この2つがプログラム側で使う資格情報です。パスワードと同じ扱いにして、コードには書かずSecret Managerに保存します。
認証の方式
ユーザー名とパスワードを毎回送る方式でもトークンは取れます。ただし、サーバー間の定期処理ではログイン情報を送らなくて済むOAuthのフローを使います。どのフローを選ぶかはSalesforceのヘルプに一覧があるため、そこから要件に合うものを選びます。
認証に成功すると、次の2つが返ります。
| 返るもの | 使い道 |
|---|---|
| アクセストークン | APIを呼ぶときのヘッダーに付けます |
| インスタンスURL | APIを呼ぶ先のホスト。組織ごとに異なります |
インスタンスURLを固定値で持たないでください。組織の移行や設定変更で変わることがあるため、認証時に返る値を使います。
2. データを取る
エンドポイント
GET <インスタンスURL>/services/data/vXX.X/query?q=<SOQL>ヘッダーに認証を付けます。
Authorization: Bearer <アクセストークン>vXX.X はAPIのバージョンです。組織が対応しているバージョンは /services/data/ を叩けば一覧で返ります。最新を常に指すのではなく、使うバージョンを固定します。上げるときは検証してから上げます。
SOQLの書き方
SQLに似ていますが別物です。URLに載せるため、クエリ中のスペースは + か %20 に置き換えます。
SELECT+Id,Name,Amount,CloseDate,LastModifiedDate+FROM+Opportunity構文が不正な場合は MALFORMED_QUERY が返ります。
レスポンスの形
{
"totalSize": 3222,
"done": false,
"nextRecordsUrl": "/services/data/vXX.X/query/01gRO0000016PIAYA2-500",
"records": [ ... ]
}| 項目 | 意味 |
|---|---|
totalSize | 条件に合う総件数 |
done | これ以上続きが無ければ true |
nextRecordsUrl | 続きを取るためのパス |
records | 取得したレコードの配列 |
ページングが必須
同期のクエリで一度に返るのは最大2,000件です。レコードの大きさによってはそれより少なくなります。
done が false の間、
nextRecordsUrl をインスタンスURLに連結して GET を繰り返すここを実装し忘れると、2,000件で静かに切れたデータが毎日入り続けます。エラーにならないため気づきにくいです。
3. 件数が多い場合
2,000件以上を返すクエリは、Bulk API 2.0 が用意されています。非同期で、ジョブを作って結果を後から取りに行く方式です。
| 方式 | 向く場面 |
|---|---|
| 同期のクエリ+ページング | 数千件までの日次差分 |
| Bulk API 2.0 | 初回の全件取得、数万件以上の抽出 |
初回に全件を取り、以降は差分だけを同期のクエリで取る、という組み合わせが現実的です。
4. 差分と削除の扱い
毎日全件を取ると、API呼び出しの上限に当たります。差分だけを取ります。
更新分を取る
LastModifiedDate で絞ります。
SELECT+Id,Name,Amount+FROM+Opportunity
+WHERE+LastModifiedDate+>+2026-01-01T00:00:00Z前回いつまで取ったかを、BigQuery側かファイル側に記録しておき、次回はそこから取ります。
削除分を取る
差分取得の弱点は、削除されたレコードが永遠に残ることです。更新日時で絞る限り、消えたものは引っかかりません。
REST APIには次が用意されています。
| リソース | 用途 |
|---|---|
| QueryAll | 削除済み・アーカイブ済みを含めて取得します |
| sObject Get Deleted | 期間を指定して、削除されたレコードのIDを取得します |
| sObject Get Updated | 期間を指定して、更新されたレコードのIDを取得します |
削除を反映しないなら、その旨をダッシュボードに明記します。黙って古い件数を出し続けるのが一番まずいです。
5. API呼び出しの上限
Salesforceには24時間あたりの呼び出し回数の上限があります。組織単位の集計で、ユーザーごとではありません。
| エディション | 24時間あたりの上限 |
|---|---|
| Developer Edition | 15,000 |
| Enterprise / Professional(API有効) | 100,000 +(ライセンス数 × ライセンス種別ごとの数)+ 購入分 |
| Unlimited / Performance | 100,000 +(ライセンス数 × ライセンス種別ごとの数)+ 購入分 |
| Full Sandbox | 5,000,000 |
Enterprise Edition で Salesforce ライセンスが15個なら、100,000 + 15 × 1,000 = 115,000件です。REST API も Bulk API もこの枠に入ります。
その他の制限は次のとおりです。
| 制限 | 値 |
|---|---|
| 20秒以上かかる同時リクエスト数 | 本番・Sandboxは25、Developer Editionは5 |
| タイムアウト | クエリ以外は10分 |
| URIとヘッダーの合計サイズ | 16,384バイト |
長いSOQLをURLに載せると、最後の制限に当たります。取得する項目を絞るだけで回避できることが多いです。
使用量の見方
| 方法 | 手順 |
|---|---|
| 応答ヘッダー | REST APIの応答に付く Sforce-Limit-Info を記録しておきます |
| 専用のリソース | /services/data/vXX.X/limits を叩きます |
| 管理画面 | 設定 → システムの概要 → 「API 使用状況」 |
| 通知 | 「API 使用状況通知」を設定すると、一定割合を超えたときにメールが届きます |
通知は最初に設定します。上限に当たってから気づくと、その日はもう取れません。
6. BigQueryに入れる
取得したデータは、いったんCloud Storageに置いてから読み込みます。
gs://<バケット名>/salesforce/opportunity/dt=2026-01-15/data.json日付でフォルダを分けると、後から特定の日だけ作り直せます。直接BigQueryに書き込むより、この一段を挟むほうが復旧が楽になります。
テーブルの持ち方
| 決めること | 選択肢 | 目安 |
|---|---|---|
| 更新方法 | 全件洗い替え/差分の追記+重複排除 | 件数が数万までなら洗い替えが単純で安全 |
| 分割 | 取り込み日/レコードの日付 | クエリで絞る列に合わせます |
| 型 | 日時はTIMESTAMP、金額はNUMERIC | 文字列のまま入れません |
洗い替えにする場合、取得が途中で失敗したときに前日分まで消えないようにします。新しいテーブルに書いてから差し替える形にします。
日時の扱い
SalesforceのAPIが返す日時はUTCです。日本時間で見たいなら、BigQuery側で変換します。
SELECT
DATETIME(close_date, 'Asia/Tokyo') AS close_date_jst
FROM
`<データセット>.opportunity`保存はUTCのまま、表示のときだけ変換します。保存時に変換すると、後から取り直したデータと混ざります。
7. レポートにする
Looker Studio でBigQueryのテーブルをつなぎます。
- Looker Studio を開き、「作成」→「レポート」を押します
- データに接続する画面で「BigQuery」を選びます
- プロジェクト → データセット → テーブルの順に選びます
- 「追加」を押します
閲覧されるたびにBigQueryへのクエリが走ります。見る人が増えるほど費用が増えるため、集計済みのテーブルを別に作り、ダッシュボードはそれだけを見る形にします。
失敗例
| 失敗 | 何が起きるか | 代わりにやること |
|---|---|---|
| ページングを実装しません | 2,000件で切れたデータが毎日入ります。エラーは出ません | done が true になるまで繰り返します |
| 毎日全件を取ります | API呼び出しの上限に当たり、その日は取れなくなります | LastModifiedDate で差分だけ取ります |
| 削除を取り込みません | 消えたレコードが残り、件数が合いません | QueryAll か Get Deleted を併用します。対応しないなら明記します |
| SOQLをURLエンコードしません | クエリが途中で切れる、または不正になります | スペースを + か %20 に置換します |
| インスタンスURLを固定値で持ちます | 組織の変更で一斉に動かなくなります | 認証時に返る値を使います |
| APIバージョンを最新に追従させます | 予告なく挙動が変わります | 使うバージョンを固定し、上げるときは検証します |
| 資格情報をコードに書きます | リポジトリの履歴に残り、消せません | Secret Managerに置きます |
| 上限の通知を設定しません | 当たってから気づきます | 「API 使用状況通知」を先に設定します |
まとめ
- 外に出す理由は、他のデータと突き合わせるためです。それ以外なら標準レポートで足ります
- 接続アプリケーションを作り、コンシューマ鍵と秘密を取得します。資格情報はSecret Managerへ
- 同期のクエリは一度に最大2,000件です。ページングは必須で、忘れると静かにデータが欠けます
- 初回の全件はBulk API 2.0、以降は
LastModifiedDateの差分取得 - 削除は差分では取れません。QueryAll か Get Deleted を併用します
- API呼び出しの上限は組織単位で24時間集計です。使用量の通知を最初に設定します
- 日時はUTCのまま保存し、表示のときだけ日本時間に変換します
最初にやることは設計ではなく、1オブジェクトを手で1回取ってみることです。件数・項目・日時の形を見てからでないと、設計の前提が決まりません。
参考になったら、いいねを押してください。





