HubSpot APIは、コンタクト・会社・取引などのCRMデータを外部のシステムから読み書きするためのREST APIです。2026年3月30日からURLのバージョン表記が日付形式に変わり、2026年10月時点の最新は/crm/objects/2026-09/のようなパスになりました。この記事では、トークンの発行方式の選び方、curlでの最初のリクエスト、顧客コードをキーにしたバッチupsert、Webhookの署名検証、レート制限の見積もりを公式ドキュメントの数値とコードで示し、個別開発すべき条件とiPaaSで足りる場面を受託開発の立場から整理します。
まとめ:HubSpot API連携は静的トークン・日付版パス・一意キーのupsertで組む
1つのHubSpotアカウントと自社システムをつなぐだけなら、トークンはレガシーの非公開アプリ(Private App)か、新しい開発者プラットフォームの静的認証アプリで発行します。複数の顧客アカウントに配る製品を作る場合だけOAuthが必要です。新しく書くコードは日付版のパスを使い、半年ごとの新版に合わせて更新計画を立てておきます。
データの同期は、基幹システムの顧客コードを一意プロパティとしてHubSpotに作り、それをキーに100件ずつupsertする形が基本です。HubSpotからの変更通知はWebhookで受け、署名を検証してから5秒以内に応答し、重い処理はキューに回します。上限はProfessionalで10秒あたり190回、1日62.5万回で、検索APIだけは毎秒5回と別枠で厳しいため、ここから逆算してバッチの大きさと実行間隔を決めます。
HubSpot APIの全体像:2026-03から始まった日付版パスとサポート期間の数え方
日本語の解説の多くは/crm/v3/のパスで書かれていますが、2026年から公式ドキュメントの表記は変わりました。
v3からYYYY-MM形式へ移った日付版パスと旧パスが使える期間の扱い
HubSpotの開発者プラットフォームとAPIのバージョン方針によると、REST APIは2026年3月30日から日付版に移行しました。最新版はURLに/2026-03/のような年月が入り、ベータ版は/2026-09-beta/のように次の公開月に-betaが付きます。v1からv4までの旧パスは、従来のURLのまま引き続き使えます。
新版は毎年3月と9月の年2回公開されます。公開から6か月は「Current」、その後は重大な不具合だけを直す「Supported」になり、公開から18か月で「Unsupported」として修正が止まります。2026-03版なら、計算上は2027年9月ごろが更新の期限です。新しく書くコードは最新の日付版にし、年1回の版上げを保守計画に入れておきます。
CRMのオブジェクトとプロパティ、関連付けを表すAPIの構成と呼び方
HubSpotのCRMは、オブジェクト(コンタクト、会社、取引など)、レコード、プロパティ(項目)、関連付け(レコード同士のつながり)で構成され、APIのURLもこの単位に分かれています。レコードの操作は/crm/objects/2026-09/{オブジェクト}、項目の定義は/crm/properties/2026-09/{オブジェクト}、関連付けの種類は/crm/associations/2026-09/の下にあります。
オブジェクトはcontactsのような名前のほか、コンタクトは0-1、会社は0-2、取引は0-3という数値IDでも指定できます。カスタムオブジェクトには2-で始まるIDが付きます。エディションごとに使える機能はHubSpotの料金|7つのHubの構造とシート・コンタクト二層課金の決まり方で整理しているので、設計の前に契約を確かめてください。
認証方式の選び方:レガシー非公開アプリ・静的認証アプリ・OAuthの使い分け
旧来のAPIキーは使えないため、どの方式でもトークンをAuthorization: Bearerヘッダで送ります。違いは、トークンを誰がどこで発行し、何アカウントに入れられるかです。
1アカウントだけをつなぐ社内連携で選ぶ非公開アプリと静的認証の違い
レガシー非公開アプリの公式ガイドでは、非公開アプリはレガシー扱いになったものの、サポートは継続すると説明されています。作成できるのはスーパー管理者で、上限は1アカウント20個です。画面でスコープを選ぶだけでトークンが手に入るため、数本のAPIを呼ぶ社内連携ならこれで足ります。
新しい開発者プラットフォームでは、同じ用途を静的認証アプリが受け持ちます。開発者プラットフォームの認証の概要によれば、静的認証は1アカウントだけに入れるアプリ向け、OAuthは複数アカウントに配るアプリ向けです。
| 方式 | 入れられる数 | 向く用途 |
|---|---|---|
| レガシー非公開アプリ | 自社の1アカウント | 数本のAPIを呼ぶ社内連携 |
| 静的認証アプリ | 1アカウントずつ | CLIで構成管理する社内連携 |
| OAuth(private配布) | 最大10アカウント | グループ会社や特定の顧客 |
| OAuth(マーケットプレイス) | 不特定のアカウント | 広く配る連携製品 |
新規に作るなら、構成をファイルで管理できる静的認証アプリを第一候補にします。ただしCLIでのビルドとデプロイが前提になり、2025.2以降の版はNode.js v22以上を求めます。CLIを入れる体制が無いなら、非公開アプリで始めても問題はありません。
app-hsmeta.jsonで静的認証とスコープを宣言する設定ファイルの書き方
静的認証アプリは、プロジェクトのappフォルダに置くapp-hsmeta.jsonで認証方式を宣言します。アプリ設定の公式リファレンスでは、distributionをprivate、auth.typeをstaticにする組み合わせが示されています。
{
"uid": "erp_sync_app",
"type": "app",
"config": {
"name": "ERP連携",
"description": "基幹システムの顧客マスタをHubSpotの会社へ同期する",
"distribution": "private",
"auth": {
"type": "static",
"requiredScopes": [
"crm.objects.companies.read",
"crm.objects.companies.write",
"crm.schemas.companies.write"
]
}
}
}
静的認証で宣言できるのはrequiredScopesだけです。プラットフォームの版はプロジェクト直下のhsproject.jsonのplatformVersionで指定し、2026年10月時点の最新は2026年9月8日に出た2026.09です。2025.1は2026年8月1日に提供を終えたので、古いプロジェクトは移行が要ります。
トークンの保管場所と6か月ごとのローテーションを運用に組み込む手順
非公開アプリのトークンは、ローテーションしない限り同じ文字列のまま使えます。公式ガイドは6か月ごとのローテーションを推奨しており、180日以上替えていないとスーパー管理者に通知メールが届きます。替え方は「すぐ失効」と「7日後に失効」の2つです。
- トークンをコードに書かず、クラウドのシークレット管理か環境変数に置く
- 「7日後に失効」でローテーションし、新しいトークンを発行する
- シークレットを新しい値に差し替え、連携を再起動する
- ログで401が出ていないことを確かめてから、旧トークンを即時失効させる
Gitに誤ってコミットした場合は「すぐ失効」を選びます。作成した担当者をHubSpotから削除すると一部のAPIがUSER_DOES_NOT_HAVE_PERMISSIONSで失敗するため、退職時もローテーションの対象です。
curlで最初のリクエストを送り、コンタクト取得とレート制限ヘッダを確かめる
コードを書く前にcurlで1回呼び、認証とスコープ、残りの呼び出し回数を確かめておきます。
2026-09版のパスでコンタクトを10件取得するリクエストと応答の読み方
トークンを環境変数HUBSPOT_TOKENに入れてから、次のコマンドを実行します。propertiesで返す項目を指定しないと、既定の数項目しか返りません。
export HUBSPOT_TOKEN="pat-xxx-xxxxxxxx"
curl --request GET \
--header "Authorization: Bearer ${HUBSPOT_TOKEN}" \
--url "https://api.hubapi.com/crm/objects/2026-09/contacts?limit=10&properties=email,firstname,lastname,company"
成功するとresultsにレコードの配列が入り、続きがある場合はpaging.next.afterに次のページの位置が返ります。この値をafterパラメータに渡せば次の10件を取れます。401はトークンの誤りか失効、403はスコープ不足が第一の疑いどころです。
応答ヘッダのX-HubSpot-RateLimit系5項目で残り回数を確かめる方法
同じリクエストに-D -を付けると応答ヘッダが表示されます。API利用ガイドラインと上限の公式ページが定めるヘッダは次の5つです。
curl -s -o /dev/null -D - \
--header "Authorization: Bearer ${HUBSPOT_TOKEN}" \
"https://api.hubapi.com/crm/objects/2026-09/contacts?limit=1" \
| grep -i "x-hubspot-ratelimit"
# X-HubSpot-RateLimit-Daily: 1日の上限
# X-HubSpot-RateLimit-Daily-Remaining: 今日の残り回数
# X-HubSpot-RateLimit-Interval-Milliseconds: 短期上限の時間幅(10000なら10秒)
# X-HubSpot-RateLimit-Max: その時間幅で許される回数
# X-HubSpot-RateLimit-Remaining: その時間幅での残り回数
1日の上限を示す2つのヘッダは、OAuthで認可した要求には付きません。検索APIの応答には5つとも付かないので、残り回数をヘッダで見張る作りは検索APIに使えません。
基幹システムの顧客コードでupsertする、一意プロパティとバッチ更新の実装
連携で一番多い要件は、基幹システムの顧客マスタをHubSpotの会社に写すことです。HubSpotのIDを基幹側に持たせると対応表の保守が増えるので、基幹側のコードをHubSpotの項目に持たせてキーにします。
hasUniqueValueで顧客コード用の一意プロパティを作るリクエスト
プロパティAPIの公式ガイドでは、作成時にgroupName、name、label、type、fieldTypeの5つが必須です。hasUniqueValueをtrueにすると、同じ値を2件のレコードに入れられない一意プロパティになります。
curl --request POST \
--header "Authorization: Bearer ${HUBSPOT_TOKEN}" \
--header "Content-Type: application/json" \
--url "https://api.hubapi.com/crm/properties/2026-09/companies" \
--data '{
"groupName": "companyinformation",
"name": "erp_customer_code",
"label": "基幹システム顧客コード",
"type": "string",
"fieldType": "text",
"hasUniqueValue": true
}'
一意プロパティは1オブジェクトにつき10個までです。作った後は/crm/objects/2026-09/companies/C0001?idProperty=erp_customer_codeのように、レコードIDの代わりに顧客コードで1件を取り出せます。顧客コードを数値型にすると先頭の0が消えるので、型は文字列にしてください。
100件ずつのバッチupsertと429・423で待って再送するPythonコード
オブジェクトAPIの公式ガイドによると、バッチ系のエンドポイントは1リクエスト100件までで、upsertはidPropertyで指定した項目が一致すれば更新、無ければ作成します。次のコードはpip install requestsだけで動きます。
import os
import time
import requests
TOKEN = os.environ["HUBSPOT_TOKEN"]
URL = "https://api.hubapi.com/crm/objects/2026-09/companies/batch/upsert"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
RETRY_STATUS = {423, 429, 502, 503, 504}
def upsert_companies(rows):
"""基幹システムの顧客マスタを100件ずつHubSpotの会社へupsertする"""
for i in range(0, len(rows), 100): # バッチは1リクエスト100件まで
inputs = [
{
"idProperty": "erp_customer_code",
"id": r["code"],
"properties": {"name": r["name"], "phone": r["phone"]},
}
for r in rows[i:i + 100]
]
for attempt in range(5):
res = requests.post(URL, headers=HEADERS, json={"inputs": inputs}, timeout=30)
if res.status_code in RETRY_STATUS:
time.sleep(2 ** (attempt + 1)) # 2・4・8・16・32秒と間隔を延ばして再送
continue
res.raise_for_status()
break
else:
raise RuntimeError(f"{i}件目からのバッチが5回失敗しました")
if __name__ == "__main__":
upsert_companies([
{"code": "C0001", "name": "株式会社サンプル", "phone": "03-0000-0000"},
])
423 Lockedは短時間に大量の更新を送ると返り、公式ガイドは要求の間を2秒以上空けるよう求めています。最初の待ち時間を2秒にしたのはそのためです。コンタクトはemailをキーにもできますが、その場合は一部の項目だけを更新する部分upsertが使えません。REST APIの呼び方に不慣れなら、先にAPI連携の実装方法:REST・認証・エラー処理の手順をcurlとPythonのコード例で解説で基本を押さえてください。
関連付けの作成とレコード削除で事前に決めておく扱いと復元の期限
会社とコンタクトのつながりは関連付けAPIで作ります。PUT /crm/objects/2026-09/{元オブジェクト}/{元ID}/associations/{先オブジェクト}/{先ID}が基本形で、関連付けの種類IDは/crm/associations/2026-09/0-1/0-2/labelsのように取得します。関連付けAPIは一般の上限とは別の、より厳しい上限を持つ点に注意が必要です。
削除したレコードはごみ箱に入り、90日以内なら画面から復元できます。取引が終わった顧客を消すか、ステータス項目を「取引終了」にするだけかは、営業が過去の履歴を見たいかで決めてください。迷うなら削除はせず、ステータスで絞り込むほうが事故は少なく済みます。
Webhookの受け口:署名v3の検証と5秒以内の応答・再送に備える受信処理
HubSpotで起きた変更を自社システムへ流す方法は、定期的にAPIで取りに行くか、Webhookで通知を受けるかの2つです。変更の件数が多いなら、取りに行く呼び出しが要らないWebhookが有利です。
署名v3をHMAC SHA-256で検証するPython(Flask)の受信コード
HubSpotからの要求を検証する公式ガイドでは、メソッド、URI、本文、タイムスタンプをこの順につないだ文字列を、アプリのクライアントシークレットでHMAC SHA-256にかけ、Base64にした値が署名だと定めています。タイムスタンプが5分より古い要求は拒否します。
import base64
import hashlib
import hmac
import os
import time
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["HUBSPOT_CLIENT_SECRET"].encode()
PUBLIC_URL = "https://example.com/hubspot/webhook" # HubSpotに登録した受信URL
def enqueue(events):
# 実運用ではSQSやRedisなどのキューに積み、別プロセスで処理する
print(f"{len(events)}件の通知を受け付けました")
@app.post("/hubspot/webhook")
def receive():
ts = request.headers.get("X-HubSpot-Request-Timestamp", "")
sig = request.headers.get("X-HubSpot-Signature-v3", "")
if not ts.isdigit() or time.time() * 1000 - int(ts) > 300_000:
abort(401) # 5分より古い要求は拒否する
raw = "POST" + PUBLIC_URL + request.get_data(as_text=True) + ts
expected = base64.b64encode(
hmac.new(SECRET, raw.encode("utf-8"), hashlib.sha256).digest()
).decode()
if not hmac.compare_digest(expected, sig): # 定数時間で比較する
abort(401)
enqueue(request.get_json())
return "", 204
本文はJSONとして読み直さず、受け取ったままの文字列で署名を計算します。読み直して整形すると空白や順序が変わり、署名は一致しません。URIにクエリ文字列が付くなら、公式ガイドの表にある%3Aなどを元の文字に戻してから計算してください。
5秒のタイムアウトと最大10回の再送を前提にした重複処理の止め方
エラー処理の公式ページによると、HubSpotは接続に失敗したとき、5秒以内に応答が無いとき、4xxか5xxが返ったときに通知を再送します。再送は最大10回で、24時間の中で間隔をずらして行われます。
受け口の設計はここから2つに決まります。受け取ったらすぐ2xxを返し、基幹システムへの書き込みはキューの先で行うこと。そして、同じ通知が2回届いても結果が変わらない作りにすることです。基幹側が遅い日に5秒を超えると、処理は成功しているのに再送が届きます。2回目を捨てる方式は冪等キーとは?Idempotency-Keyの発行・保存・再生の実装と二重課金の止め方を解説、受信側の作り全般はWebhookの作り方と実装方法|受信側の署名検証・冪等性と送信側の再送をNode.js・Pythonで解説で扱っています。
レート制限の設計:10秒・1日の上限と検索API毎秒5回から同期間隔を決める
上限は、連携を入れたアカウントの契約で決まります。設計の段階で1日の呼び出し回数を見積もっておきます。
契約エディション別に見る10秒あたりと1日あたりの呼び出し上限
非公開アプリと、private配布の開発者プラットフォームのアプリには、次の上限がかかります。10秒あたりの上限はアプリごと、1日の上限はアカウント内の全アプリで共有です。
| 契約 | 10秒あたり | 1日あたり |
|---|---|---|
| Free・Starter | 100回/アプリ | 25万回/アカウント |
| Professional | 190回/アプリ | 62.5万回/アカウント |
| Enterprise | 190回/アプリ | 100万回/アカウント |
| 上限追加を購入 | 250回/アプリ | 1つごとに100万回を追加 |
上限追加は2つまで買えます。マーケットプレイスで配るOAuthアプリは、導入先1アカウントあたり10秒110回で、上限追加の対象外です。1日の上限は、アカウントの時間帯設定で午前0時にリセットされます。
検索APIの毎秒5回・1万件・200件の上限で差分取得を組む手順
差分の取得には、更新日時で絞り込む検索APIが便利です。ただしCRM検索APIの公式ガイドの制限は一般の上限より厳しく、アカウントあたり毎秒5回、1ページ200件、1つの条件で取れるのは1万件まで、本文は3,000字までです。
- 前回の同期の開始時刻を保存しておく
hs_lastmodifieddateがその時刻以降の会社を、1ページ200件で検索する- 1万件に近づいたら、取得済みの最後の更新日時で条件を切り直す
- 検索の間に0.2秒以上の間隔を置き、毎秒5回を超えないようにする
- 取得したIDを100件ずつバッチ読み取りに回し、必要な項目をそろえる
初回の全件取り込みに検索APIを使うと、1万件の壁と毎秒5回の制限にすぐ当たります。初回はエクスポートやバッチ読み取りで行い、検索APIは日々の差分だけに使う分け方が安全です。
429の応答本文とpolicyNameで1日上限か短期上限かを判別する処理
上限を超えると429が返り、本文のpolicyNameで原因を区別できます。DAILYなら1日の枠を使い切っており、午前0時まで待つしかありません。TEN_SECONDLY_ROLLING(10秒の上限)なら、数秒待てば再開できます。前の章のコードは両方を同じ待ち方で扱うので、1日の上限に近い規模の連携ではDAILYのときだけ処理を止めて通知する分岐を足してください。
公式ガイドは、エラーになった要求を1日の総数の5%以下に抑えるよう求めています。上限の考え方と429の設計の一般論はAPIレート制限(レートリミット)とは?5方式の比較と429設計・分散実装【2026年版】にまとめています。
受託開発の視点で見る、API連携を個別開発すべき条件とiPaaSで足りる場面
ここからは、CRMの連携開発を受ける立場からの判断です。分かれ目は件数と処理の中身にあります。
個別開発を選ぶべき条件:一意キーの名寄せ・1日数万件・失敗行の再送
次のどれかに当てはまるなら、APIで個別に開発します。基幹側のコードで名寄せし、重複を判定する規則をコードで管理してテストしたいとき。1日に数万件以上を同期し、100件のバッチと上限の配分を自分で制御したいとき。失敗した行だけを再送し、どの行がなぜ失敗したかを記録したいとき。この3つは、画面の設定で組むiPaaSでは保守しにくい部分です。
個別開発には、版の更新、トークンのローテーション、Webhookの監視という保守が続きます。保守を誰が持つかは契約の前に決めておくべき事項です。要件の整理から実装、保守までをまとめて頼みたい場合は、HubSpot導入支援サービスで連携開発も受け付けています。
個別開発を見送る場面:項目を写すだけの連携と標準コネクタがある製品
一方で、フォームの回答をSlackに流す、kintoneの数項目をHubSpotに写す、といった1日数百件までの連携に個別開発は過剰です。iPaaSなら画面の設定で組め、担当者が自分で直せます。料金の数え方はiPaaSとは?仕組み・RPAとの違い・料金の数え方と導入判断をわかりやすく解説で確認してください。
SalesforceのようにHubSpotが標準コネクタを用意している製品も、まずは標準コネクタで始めます。同期ルールの設計はHubSpotとSalesforceの連携|標準コネクタの同期ルールとマスタ設計・iPaaSで補う場面で扱っています。標準コネクタで届かない項目が出た段階で、その部分だけをAPIで作り足すのが費用を抑える進め方です。
HubSpot APIのよくある質問:無料版での利用・旧APIキー・v3パスの扱い
HubSpot APIで連携を作る前によく寄せられる質問に、2026年10月時点の公式ドキュメントをもとに回答します。
HubSpotの無料版やStarterでもAPIは使えますか?
使えます。非公開アプリはどのエディションでも作れ、Free・Starterの上限は10秒あたり100回、1日25万回です。ただしカスタムオブジェクトのように、エディションで機能そのものが制限される項目があります。使いたいオブジェクトが自社の契約で使えるかを先に確かめてください。
以前のAPIキー(hapikey)で書いた連携はまだ動きますか?
アカウントのデータを読み書きする用途の旧APIキーは廃止されており、非公開アプリか静的認証アプリのトークンに置き換える必要があります。なお、開発者アカウント向けの開発者APIキーは別物で、一部のアプリ設定のAPIで今もhapikeyパラメータとして使われます。
既存の/crm/v3/のパスは書き換えないといけませんか?
すぐに書き換える必要はありません。v1からv4までの旧パスは従来のURLのまま使えると公式に案内されています。公式は新しい連携に最新の日付版を使うよう案内しているので、新しく書く部分から日付版にし、既存部分は改修の機会に移すのが現実的です。
レガシーの非公開アプリは今後使えなくなりますか?
2026年10月時点では、レガシーアプリはサポートが続くと公式に書かれています。ただし最新のアプリ機能はレガシーアプリでは使えません。今の連携が非公開アプリで足りているなら急いで移す必要はなく、サーバーレス関数などが必要になった時点で移行を検討すれば十分です。
不動産や製造業など業種ごとのAPI連携で気を付ける点はありますか?
APIの使い方は業種で変わりません。差が出るのは、どのオブジェクトに何を入れるかです。不動産なら物件を表すリスティング(0-420)、製造業なら製品と商品項目、金融や保険なら契約のように、業務の単位に合う標準オブジェクトがあるかを先に確かめます。無ければカスタムオブジェクトを設計し、基幹側のコードを一意プロパティにして同期します。
関連記事
- HubSpot導入の進め方|既存CRM・名刺データの移行設計とオンボーディングの必須条件:API連携の前に行うデータ移行の設計
- HubSpotとSalesforceの連携|標準コネクタの同期ルールとマスタ設計・iPaaSで補う場面:標準コネクタで済む連携の設計
- kintoneのデータ連携を実装視点で選ぶ|4方式の判定表とAPI制限の設計:連携相手がkintoneの場合の相手側の方式
- CRM連携とは?MA・SFA・基幹をつなぐAPI連携の設計と選定基準を解説:3つ以上のシステムをつなぐ設計の考え方
- ShopifyとkintoneのAPI連携|手段の選び方・自動化できることとAPI制限の設計【2026年版】:別製品同士のAPI連携の実例