
自訂內部工具的 HubSpot API 整合:Node + Python 實戰指南 (2026)
還在四處尋找用來設定 HubSpot API 整合 的 HubSpot API 金鑰嗎?別找了。HubSpot 已於 2022 年 11 月 30 日終止靜態 API 金鑰,而且目前的 Node SDK(@hubspot/api-client,現為 v14 版)也不再支援該金鑰。對於單一帳戶的內部工具而言,正確的憑證是私人應用程式存取權杖(private app access token)。本指南將帶領你使用 Node 和 Python,從第一次 create contact(建立聯絡人)呼叫到實作經簽章驗證的 Webhook,逐步建構一個真實的 HubSpot 至內部工具同步機制。
快速解答: HubSpot API 整合能讓自訂內部工具透過 HubSpot 的 v3 REST API 讀寫 CRM 資料。針對單一帳戶的內部工具,請使用私人應用程式存取權杖進行驗證(HubSpot 已於 2022 年淘汰 API 金鑰),並改用 Webhook 即時同步變更,而非輪詢。
你將建構以下內容:
- 私人應用程式權杖驗證,以及在 Node 和 Python 中執行第一次
create contact呼叫 - 一個在信任負載之前會驗證
X-HubSpot-Signature-v3的 Webhook 接收器 - 具備 429 錯誤防護機制,並以每批次 100 筆記錄同步至內部工單或 ERP 記錄
自訂內部工具的 HubSpot API 整合如何運作?
HubSpot API 整合 透過 v3 REST API 將自訂內部工具(如工單應用程式、ERP 系統、帳務儀表板或客戶入口網站)連接至 HubSpot 的 CRM。你的工具使用 私人應用程式存取權杖,透過 HTTPS 讀寫 CRM 物件(聯絡人、交易、公司或自訂物件),而即時變更則透過 Webhook 回傳。
你可以將 HubSpot 的 CRM 視為一個透過 HTTP 溝通的資料庫。每筆記錄都是一個具有類型和 ID 的物件。你正在建構的 hubspot crm api integration 主要執行兩項工作:將資料推送到 HubSpot(例如在開啟工單時建立聯絡人),以及從中拉取資料(例如在內部儀表板渲染時讀取交易資訊)。
同步運作有兩種方向。單向同步 將變更從 HubSpot 複製到你的工具,或從你的工具複製到 HubSpot。雙向同步 則同時執行兩者,但需要防止無限迴圈,我們稍後會討論這點。此外,與其每分鐘詢問 HubSpot「有新訊息嗎?」(輪詢),不如註冊 Webhook,讓 HubSpot 在記錄變更的第一時間通知你。
如果你傾向完全擁有自己的資料,而非整合託管式 CRM,那麼自行託管 開源 CRM 是另一條值得在投入前考量的路径。但如果 HubSpot 已經是你的單一事實來源,那麼 API 就是其他所有系統與之對話的方式。
對於單一帳戶的內部工具,你不需要 OAuth 或在應用程式市場上架清單。私人應用程式權杖加上 Webhook 就構成了完整的整合。
2026 年的驗證方式:為何不再有 HubSpot API 金鑰
針對單一帳戶內部工具的 HubSpot API 驗證,請使用 私人應用程式存取權杖。這是一種靜態 bearer token,你只需在 HubSpot 帳戶中產生一次,其權限範圍僅限於你的工具所接觸的物件。它沒有刷新流程,也不會過期。OAuth 適用於公開的多帳戶應用程式,而非你們營運團隊內部使用的儀表板。
私人應用程式權杖 vs. 已棄用的 API 金鑰
這裡有個陷阱,會讓半數搜尋到此結果的開發者栽跟頭。HubSpot 已於 2022 年 11 月 30 日停止支援 API 金鑰,目前完全不再支援。自動完成功能仍會建議「hubspot api key」,因為肌肉記憶尚未跟上,但實際上已無金鑰可取得。請改用 hubspot private app:在設定中建立它,授予所需範圍,並從「Auth」(驗證)標籤頁複製存取權杖。HubSpot 的 私人應用程式概覽 涵蓋了相關設定。
| 方法 | 使用案例 | 是否過期或需刷新? | 最適合 |
|---|---|---|---|
| API 金鑰 | 已移除 | 2022 年 11 月停用 | 無,已棄用 |
| 私人應用程式存取權杖 | 單一帳戶內部工具 | 否,靜態,無需刷新 | 你的內部工具,此處的預設選項 |
| OAuth 2.0 | 公開或多帳戶應用程式 | 是,權杖約 6 小時過期且需刷新 | 你為其他公司入口網站上架的應用程式 |
| Service Key(2026 年 2 月公開測試版) | 帳戶層級、僅限資料的憑證 | 帳戶範圍,依文件規定 | 僅限資料的伺服器作業,仍屬測試階段 |
關於權杖本身有兩條規則。授予 最小權限:如果你的工具只讀取交易並寫入聯絡人,則僅請求 crm.objects.contacts.write 和 crm.objects.deals.read,切勿多給。並將權杖儲存在環境變數或秘密管理器中,透過 Authorization: Bearer 標頭發送,絕不要硬編碼或發布到瀏覽器端。
結論很簡單。對於內部工具,請使用 私人應用程式存取權杖。只有當這後續變成其他公司安裝到自己入口網站的公開多帳戶應用程式時,才考慮使用 OAuth。
你的第一次 HubSpot API 呼叫:在 Node 和 Python 中建立聯絡人
標準的第一次呼叫是 create contact,官方 SDK 讓這變得只需幾行程式碼。安裝客戶端,使用來自環境變數的私人應用程式權杖進行初始化,然後建立聯絡人並讀回交易資訊。這是你將來用於公司、工單和 hubspot custom objects api 呼叫的相同模式,只是物件類型不同。
以下是使用 @hubspot/api-client (v14) 的 Node 版本:
// npm i @hubspot/api-client (v14.x)
import { Client } from "@hubspot/api-client";
// Private app token from a secret manager or env var, never hard-coded
const hubspot = new Client({ accessToken: process.env.HUBSPOT_PRIVATE_APP_TOKEN });
// Create a contact
const { id } = await hubspot.crm.contacts.basicApi.create({
properties: {
email: "[email protected]",
firstname: "Ada",
lastname: "Lovelace",
lifecyclestage: "lead",
},
associations: [],
});
console.log("Created contact", id);
// Read a deal by ID
const deal = await hubspot.crm.deals.basicApi.getById(
"1234567890",
["dealname", "amount", "dealstage"],
);
console.log(deal.properties.dealname, deal.properties.amount);以下是使用 hubspot-api-client (v12) 的 Python 相同操作:
# pip install hubspot-api-client (v12.x)
import os
from hubspot import HubSpot
from hubspot.crm.contacts import SimplePublicObjectInputForCreate
# Private app token from the environment, not source control
client = HubSpot(access_token=os.environ["HUBSPOT_PRIVATE_APP_TOKEN"])
# Create a contact
contact = client.crm.contacts.basic_api.create(
simple_public_object_input_for_create=SimplePublicObjectInputForCreate(
properties={
"email": "[email protected]",
"firstname": "Ada",
"lastname": "Lovelace",
"lifecyclestage": "lead",
}
)
)
print("Created contact", contact.id)
# Read a deal by ID
deal = client.crm.deals.basic_api.get_by_id(
deal_id="1234567890",
properties=["dealname", "amount", "dealstage"],
)
print(deal.properties["dealname"], deal.properties["amount"])專業提示:請針對 HubSpot 開發者沙盒環境 進行測試,切勿直接在生產環境上測試。生產環境中格式錯誤的建立呼叫會留下真實的垃圾記錄,迫使你的銷售團隊進行清理。權杖、範圍和物件模型在沙盒環境中的行為完全相同。
如何即時將 HubSpot 同步到內部工具?
使用 Webhook,而非輪詢。在你的私人應用程式中,為你关心的物件和事件(例如 deal.propertyChange)註冊 Webhook 訂閱,將其指向你託管的 HTTPS 端點,當發生符合條件的變更時,HubSpot 會立即 POST 一個小型 JSON 陣列給你。只有在沒有現有訂閱涵蓋你需要監控的內容時,才使用輪詢。
這樣做的好處是效率。輪詢每分鐘詢問一次「有新訊息嗎?」並消耗你的速率限制;而 Webhook 則會在交易變更的那一刻立即通知你。這種差異在大規模應用下至關重要,且 Webhook 現在已是主流而非罕見技術:Postman 的 2025 年 API 現狀報告(一項針對超過 5,700 名開發者的調查)發現,約有一半的團隊依賴它們。
在私人應用程式的 Webhooks 標籤頁下註冊訂閱,設定目標 URL,並選擇事件。HubSpot 會發送一個事件物件陣列,每個物件包含 subscriptionType、objectId 以及變更內容。以下是使用 Express 的 Node 接收器存根:
import express from "express";
const app = express();
// Capture the raw body: you need the exact bytes to validate the signature next
app.use(express.json({
verify: (req, _res, buf) => { req.rawBody = buf.toString("utf8"); },
}));
// HubSpot POSTs an array of events to this URL
app.post("/webhooks/hubspot", (req, res) => {
const events = req.body; // [{ subscriptionType: "deal.propertyChange", objectId: 1234, ... }]
for (const event of events) {
console.log("HubSpot event:", event.subscriptionType, event.objectId);
// Do NOT trust this payload yet. The next section validates it before we act.
}
res.sendStatus(200);
});
app.listen(3000, () => console.log("Listening on :3000"));這就是實作過程變得真實的地方。假設你正在 將交易同步到小型製造商的 ERP 記錄:Webhook 觸發,你的處理常式建立或更新對應的 ERP 工單,你的營運團隊無需接觸 HubSpot 即可看到變更。這與我們用來 將語音代理同步到 CRM 的即時方法相同,只是觸發因素是屬性變更而非電話呼叫。有一個警告:上面的存根信任任何 POST 到它的內容。在上線前請修正這個問題。
驗證 Webhook 簽章 (v3),確保永不信任偽造的負載
使用 v3 簽章 驗證每個傳入的 Webhook。HubSpot 使用你的應用程式密鑰對每個請求進行簽章,並發送兩個標頭:X-HubSpot-Signature-v3 和 X-HubSpot-Request-Timestamp。拒絕任何超過 5 分鐘的請求,將來源字串重建為 method + full URL + raw body + timestamp,使用應用程式密鑰進行 HMAC-SHA256 運算,進行 base64 編碼,並在恆定時間內進行比較。
如果你跳過此步驟,任何猜到你 Webhook URL 的人都可以偽造交易更新。驗證並非可選項目。HubSpot 的 驗證請求文件 和 v3 簽章變更日誌 詳細說明了確切的配方。以下是可直接使用的 Express 中介軟體:
import crypto from "crypto";
const CLIENT_SECRET = process.env.HUBSPOT_APP_SECRET; // from your private app settings
const MAX_AGE_MS = 5 * 60 * 1000; // reject anything older than 5 minutes
export function validateHubSpotSignature(req, res, next) {
const signature = req.header("X-HubSpot-Signature-v3");
const timestamp = req.header("X-HubSpot-Request-Timestamp");
// 1. Reject stale requests (replay protection)
if (!signature || !timestamp || Date.now() - Number(timestamp) > MAX_AGE_MS) {
return res.sendStatus(401);
}
// 2. Rebuild the exact source string: method + full URL + raw body + timestamp
const uri = `https://${req.get("host")}${req.originalUrl}`;
const source = `${req.method}${uri}${req.rawBody}${timestamp}`;
// 3. HMAC-SHA256 with the app secret, base64-encoded
const hash = crypto
.createHmac("sha256", CLIENT_SECRET)
.update(source, "utf8")
.digest("base64");
// 4. Constant-time compare against the header
const expected = Buffer.from(hash);
const received = Buffer.from(signature);
if (expected.length !== received.length ||
!crypto.timingSafeEqual(expected, received)) {
return res.sendStatus(401);
}
next();
}以下是作為 Python 函式的相同檢查,涵蓋兩種技術堆疊:
import base64
import hashlib
import hmac
import os
import time
CLIENT_SECRET = os.environ["HUBSPOT_APP_SECRET"].encode("utf-8")
MAX_AGE_MS = 5 * 60 * 1000 # 5 minutes
def is_valid_signature(method, uri, body, signature, timestamp):
# 1. Reject stale requests
if not signature or not timestamp:
return False
if int(time.time() * 1000) - int(timestamp) > MAX_AGE_MS:
return False
# 2. method + full URL + raw body + timestamp
source = f"{method}{uri}{body}{timestamp}".encode("utf-8")
# 3. HMAC-SHA256, base64
digest = hmac.new(CLIENT_SECRET, source, hashlib.sha256).digest()
expected = base64.b64encode(digest).decode("utf-8")
# 4. Constant-time compare
return hmac.compare_digest(expected, signature)讓人耗費一下午的陷阱在於:HubSpot 對 完整目標 URL(包括協定、主機和路徑)進行簽章。在代理、負載平衡器或 ngrok 隧道後面,req.get("host") 可能會回報內部主機,而非 HubSpot 簽章的公共主機。如果你確定負載合法但驗證持續失敗,請記錄你重建的確切 URI,並逐字元比對你的公共 Webhook URL。
速率限制、429 錯誤與 Batch API:我們在生產環境中的實際經驗
私人應用程式大約獲得 每秒 10 次請求(Free/Starter 方案每 10 秒 100 次,Pro/Enterprise 方案每 10 秒 190 次),每日上限介於 250,000 到 1,000,000 之間。陷阱在於:CRM Search 單獨限制為 每秒 4 次請求,而 batch 端點每次請求最多接受 100 筆記錄。HubSpot 的 使用指南 列出了各層級詳情。
| 層級 | 每 10 秒 | 每秒 | 每日上限 | 備註 |
|---|---|---|---|---|
| Free / Starter(私人應用程式) | 100 | ~10 | 250,000 | CRM Search 單獨限制為每秒 4 次請求 |
| Pro / Enterprise(私人應用程式) | 190 | ~19 | 高達 1,000,000 | Batch 端點每次請求最多 100 筆記錄 |
理論與紅色警示儀表板在此交會。今年春天的一次回填作業中,我們從內部工單工具推送了約 8,000 筆現有記錄到 HubSpot,並使用 CRM Search 查找豐富每筆記錄。我們在 Node 端運行 @hubspot/api-client v14,並在 Python 豐富化工作程序中運行 hubspot-api-client v12。大量寫入沒問題。但 Search 呼叫在一分鐘內崩潰,因為我們的工作程序以約每秒 15 次請求的速度發起 Search,遠超過我們未單獨預算的每秒 4 次硬性上限。
兩項改變解決了問題。首先,一個重試包裝器,讀取 X-HubSpot-RateLimit-* 回應標頭並在遇到 429 時退避:
// Wrap any HubSpot call; retries on 429 with exponential backoff
async function withRetry(fn, maxRetries = 5) {
let attempt = 0;
while (true) {
try {
return await fn();
} catch (err) {
const status = err.code ?? err.response?.status;
if (status !== 429 || attempt >= maxRetries) throw err;
// Honor HubSpot's reset window if the header is present
const headers = err.response?.headers ?? {};
const resetMs = Number(headers["x-hubspot-ratelimit-interval-milliseconds"]) || 0;
const backoff = Math.max(resetMs, 2 ** attempt * 500); // 0.5s, 1s, 2s, 4s...
console.warn(`429 hit, retry ${attempt + 1} in ${backoff}ms`);
await new Promise((r) => setTimeout(r, backoff));
attempt++;
}
}
}其次,我們停止逐一寫入記錄。Batch 端點 在 POST /crm/v3/objects/{objectType}/batch/create 中每次最多接受 100 筆記錄,因此我們將回填作業分塊為 80 次批次呼叫,而非 8,000 次單次 POST:
// HubSpot batch endpoints accept at most 100 records per request
function chunk(arr, size = 100) {
const out = [];
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
return out;
}
// POST /crm/v3/objects/contacts/batch/create, chunked to 100 at a time
async function batchCreateContacts(records) {
for (const group of chunk(records, 100)) {
const inputs = group.map((r) => ({
properties: { email: r.email, firstname: r.firstName, lastname: r.lastName },
associations: [],
}));
await withRetry(() => hubspot.crm.contacts.batchApi.create({ inputs }));
console.log(`Wrote ${group.length} contacts`);
}
}將 Search 工作程序限制為每秒 4 次請求並批次化寫入,讓原本淹沒在重試中的運行變得安靜地完成。如果你要從本節記住一個數字,那就是 4:CRM Search 上限是生產環境中最常遇到的限制,也是許多綜述文章忘記提及的一點。順便一提,舊有的「聯絡人批次上限 10 筆」已不存在;目前所有物件類型均為 100 筆。
走向雙向:將變更寫回 HubSpot 而不陷入無限迴圈
雙向同步 除了讀取資料外,還會將內部工具的變更寫回 HubSpot。危險在於回饋迴圈:你的寫回動作觸發了啟動處理常式的同一個 Webhook,導致再次寫入,永無止境。透過 冪等性金鑰(跳過已應用的變更)和來源標記(忽略由你自己的工具引起的傳入事件)來防止这种情况。
const processed = new Set(); // use Redis or a unique DB constraint in production
async function writeBackToHubSpot(record) {
// Dedup key: object id + a hash of the change we're about to apply
const key = `${record.id}:${record.updatedHash}`;
if (processed.has(key)) return; // already synced this exact change
processed.add(key);
await withRetry(() =>
hubspot.crm.contacts.basicApi.update(record.id, {
// Tag the source so the resulting webhook is ignored by our own receiver
// (check for source: "internal-tool" before acting on an inbound event)
properties: { internal_status: record.status, last_sync_source: "internal-tool" },
})
);
}這個模式很小,但忽略它會導致同步在一夜之間悄悄使你的寫入量加倍。一旦雙向資料都乾淨了,團隊通常會將其下游饋送到 AI SDR 管道 或報告層。如果你想要關於雙向同步的第二種觀點,Nango 的 HubSpot 整合教學 是一個堅實的僅 Node 參考資源,不過你需要自行移植防止迴圈的概念。
應該內部自建還是聘请整合夥伴?
當同步機制 規模小、穩定且由內部擁有 時,請內部自建:單向流程、少數幾個物件,以及一位能吸收 HubSpot 大約每年兩次破壞性變更的工程師。當你需要 雙向同步、自訂物件建模,或者團隊中沒有人能負責持續維護時,請聘请夥伴。決定因素很少是最初的建構;而是誰在一年後負責監控它。
以下是一份誠實的檢查清單。如果符合以下條件,請自行建構: 方向是單向的,你正在同步標準物件,你有一位可以託管 Webhook 端點的開發人員,並且當負載開始失敗時有人會注意到。上述所有内容就是你的藍圖。
如果符合以下條件,請聘请夥伴: 你需要跨多個物件進行具有迴圈防止機制的雙向同步,你正在建模具有類型化關聯的自訂物件,你正在連接多個系統(HubSpot 加上 ERP 加上帳務),或者負責維護的人已經滿載。HubSpot 使用基於日期的 API 版本控制,破壞性變更每年僅發生約兩次,聽起來很溫和,直到其中一次發生在你最忙碌的一週,且沒有負責人。那種維護長尾,而非首次部署,才是悄悄拖垮內部整合的因素。如果你不想擁有它,那就是我們的 自訂 CRM 整合服務 發揮作用的地方。
重點摘要
- HubSpot API 金鑰已不存在。對於單一帳戶內部工具,請使用 私人應用程式存取權杖;OAuth 僅適用於公開的多帳戶應用程式。
- 在信任 Webhook 負載之前,務必驗證
X-HubSpot-Signature-v3。使用完整目標 URL 重建來源字串。 - 尊重 每秒 4 次請求的 CRM Search 上限,並以 100 筆為一組批次化大型寫入,並實施 429 退避機制。
- 對於即時同步,優先使用 Webhook 而非輪詢,且 HubSpot 整合是更廣泛 企業 AI 工具 堆疊的一部分。
卡在維護方面,或者希望在發布前讓第二雙眼睛檢查?預約免費整合諮詢。無論如何都沒有壓力;上面的程式碼無論如何都由你執行。
關於作者
Mert Batur Gurbuz 是 Techsy.io 的共同創辦人,該團隊為 B2B 客戶交付 AI 代理、自動化系統以及語音/SDR 管道。他就讀於伯明翰大學,並撰寫有關 Techsy 團隊在生產環境中實際使用的 LLM 工具堆疊的文章。資格:Techsy.io 共同創辦人,伯明翰大學。在 LinkedIn 上聯繫。
常見問題
2026 年我仍然需要 HubSpot API 金鑰嗎?
不需要。HubSpot 已於 2022 年 11 月 30 日停止支援靜態 API 金鑰,目前完全不再支援。自動完成功能仍會因習慣建議「hubspot api key」,但實際上無物可取。對於單一帳戶內部工具,請在設定中建立私人應用程式並改用其存取權杖。
HubSpot 的私人應用程式權杖與 OAuth 有什麼區別?
私人應用程式存取權杖是單一 HubSpot 帳戶的靜態憑證,沒有過期時間且無需刷新流程,非常適合內部工具。OAuth 2.0 適用於其他公司安裝到自己入口網站的公開多帳戶應用程式;其權杖約六小時過期且需要刷新週期。
2026 年 HubSpot 的 API 速率限制是多少?
私人應用程式大約獲得每秒 10 次請求(Free/Starter 方案每 10 秒 100 次,Pro/Enterprise 方案每 10 秒 190 次),每日上限為 250,000 到 1,000,000。CRM Search API 單獨限制為每秒 4 次請求,而 batch 端點每次請求最多接受 100 筆記錄。
如何驗證 HubSpot Webhook 簽章?
使用 v3 配方:拒絕 X-HubSpot-Request-Timestamp 超過五分鐘的請求,然後建構由方法、完整目標 URL、原始正文和時間戳組成的來源字串。使用你的應用程式密鑰對其進行 HMAC-SHA256 運算,對結果進行 base64 編碼,並在恆定時間內與 X-HubSpot-Signature-v3 進行比較。
我應該使用哪種 HubSpot SDK,Node 還是 Python?
兩者都是官方維護的。Node 使用 @hubspot/api-client (v14),Python 使用 hubspot-api-client (v12)。它們公開相同的 v3 CRM 物件模型,因此選擇符合你技術堆疊的那一個即可。本指南以兩種語言提供相同的驗證和簽章驗證程式碼。
如何即時將 HubSpot 與自訂內部工具同步?
在你的私人應用程式中,為你关心的物件和事件註冊 Webhook 訂閱,然後託管一個 HTTPS 端點,當發生符合條件的變更時,HubSpot 會 POST 到該端點。驗證簽章,然後將變更寫入你的內部工具。僅在沒有 Webhook 訂閱涵蓋你所需內容時才使用輪詢。
什麼是 HubSpot Service Key,我應該使用它嗎?
Service Key 是 HubSpot 於 2026 年 2 月進入公開測試階段的帳戶層級、僅限資料的憑證。它旨在用於僅接觸資料的伺服器端作業。對於目前的標準內部工具,私人應用程式存取權杖仍然是更安全、文件更完善的預設選項;在 Service Keys 正式畢業前,請將其視為測試版。
我可以在不影響生產環境的情況下測試 HubSpot 整合嗎?
可以。建立 HubSpot 開發者沙盒環境,並將你的私人應用程式權杖指向它。範圍、物件模型、Webhook 和速率限制的行為與生產環境相同,因此你可以建立測試聯絡人並觸發 Webhook,而不會留下垃圾記錄供你的銷售團隊日後清理。
HubSpot batch API 一次可以接受多少筆記錄?
Batch 端點(POST /crm/v3/objects/{objectType}/batch/create 及其更新和 upsert 兄弟端點)每次請求最多接受 100 筆記錄。將較大的負載分塊為每組 100 筆。一些教學文章仍引用的舊有「聯絡人 10 筆記錄」上限已被移除;目前所有物件類型均為 100 筆。
我應該內部自建還是聘请代理商?
當同步是單向的、使用標準物件,且有一位所有者能吸收 HubSpot 每年兩次的破壞性變更時,請內部自建。對於雙向同步、自訂物件建模,或者沒有人能負責維護時,請聘请夥伴。首次部署很容易;之後一年的維護才是真正的成本。