TÓM TẮT
API của HubSpot cho phép các nhà phát triển tích hợp với CRM, marketing, bán hàng, dịch vụ. Sử dụng xác thực OAuth 2.0 và ứng dụng riêng tư, hỗ trợ RESTful endpoints cho danh bạ, công ty, giao dịch, phiếu hỗ trợ, v.v. Bài này hướng dẫn từng bước thiết lập xác thực, gọi API, cấu hình Webhook và chiến lược vận hành sản xuất.
Giới thiệu
HubSpot quản lý hàng trăm nghìn tài khoản khách hàng, hàng tỷ bản ghi CRM. Khi tự động hóa marketing, CRM hay bán hàng, tích hợp API HubSpot là bước bắt buộc để đồng bộ dữ liệu cho hàng triệu người dùng.
Thực tế, doanh nghiệp mất hàng chục giờ nhập liệu thủ công mỗi tuần. Tích hợp API HubSpot giúp tự động đồng bộ danh bạ, cập nhật giao dịch, workflow marketing, báo cáo và giảm lỗi nhập dữ liệu.
💡 Apidog đơn giản hóa kiểm thử tích hợp API: test endpoint HubSpot, xác thực OAuth, kiểm tra webhook, debug xác thực trong một workspace. Nhập spec API, tạo mock response, chia sẻ test scenario cho team.
API của HubSpot là gì?
HubSpot cung cấp API RESTful để truy cập dữ liệu CRM, tự động hóa marketing:
- Danh bạ, công ty, giao dịch, phiếu hỗ trợ, đối tượng tùy chỉnh
- Email marketing, landing page
- Quy trình bán hàng, chuỗi workflow
- Hỗ trợ, hội thoại
- Analytics & báo cáo
- Workflow & automation
- File & asset management
Các tính năng chính
| Tính năng | Mô tả |
|---|---|
| Thiết kế RESTful | HTTP chuẩn, JSON response |
| OAuth 2.0 & Ứng dụng riêng tư | Xác thực linh hoạt |
| Webhooks | Nhận realtime notification khi đối tượng thay đổi |
| Giới hạn tốc độ | 100-400 requests/giây, tùy gói |
| Đối tượng CRM | Hỗ trợ cả object chuẩn và custom |
| Liên kết | Kết nối đối tượng (contact-company, deal-contact, ...) |
| Thuộc tính | Custom field cho mọi object |
| API Tìm kiếm | Lọc/sắp xếp phức tạp |
Tổng quan kiến trúc API
HubSpot API có phiên bản:
https://api.hubapi.com/
So sánh các phiên bản API
| Phiên bản | Trạng thái | Xác thực | Trường hợp sử dụng |
|---|---|---|---|
| API CRM v3 | Hiện hành | OAuth 2.0, App riêng | Tích hợp mới |
| API Automation v4 | Hiện hành | OAuth 2.0, App riêng | Ghi danh workflow |
| API Email Marketing | Hiện hành | OAuth 2.0, App riêng | Chiến dịch email |
| API Danh bạ v1 | Đã ngừng hỗ trợ | API Key (cũ) | Di chuyển sang v3 |
| API Công ty v1 | Đã ngừng hỗ trợ | API Key (cũ) | Di chuyển sang v3 |
Lưu ý: Xác thực bằng API key đã ngừng hỗ trợ, phải dùng OAuth 2.0 hoặc ứng dụng riêng tư.
Bắt đầu: Thiết lập xác thực
Bước 1: Tạo tài khoản Developer HubSpot
- Truy cập Cổng thông tin nhà phát triển HubSpot
- Đăng nhập/tạo tài khoản mới
- Vào Ứng dụng trong dashboard Developer
- Nhấn Tạo ứng dụng
Bước 2: Chọn phương thức xác thực
| Phương thức | Dùng cho | Bảo mật |
|---|---|---|
| OAuth 2.0 | App đa tenant, tích hợp công khai | Cao (token user) |
| App riêng tư | Nội bộ, 1 portal | Cao (token portal) |
Bước 3: Tạo ứng dụng riêng tư (Khuyên dùng cho tích hợp nội bộ)
- Vào Cài đặt > Tích hợp > Ứng dụng riêng tư
- Nhấn Tạo ứng dụng riêng tư
- Chọn scope:
contacts
crm.objects.companies
crm.objects.deals
crm.objects.tickets
automation
webhooks
- Tạo mã truy cập, lưu trữ an toàn
# .env
HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HUBSPOT_PORTAL_ID="12345678"
Bước 4: Thiết lập OAuth 2.0 (App đa tenant)
- Vào Ứng dụng > Tạo ứng dụng
- Cấu hình xác thực:
const HUBSPOT_CLIENT_ID = process.env.HUBSPOT_CLIENT_ID;
const HUBSPOT_CLIENT_SECRET = process.env.HUBSPOT_CLIENT_SECRET;
const HUBSPOT_REDIRECT_URI = process.env.HUBSPOT_REDIRECT_URI;
const getAuthUrl = (state) => {
const params = new URLSearchParams({
client_id: HUBSPOT_CLIENT_ID,
redirect_uri: HUBSPOT_REDIRECT_URI,
scope: 'crm.objects.contacts.read crm.objects.contacts.write',
state: state,
optional_scope: 'crm.objects.deals.read'
});
return `https://app.hubspot.com/oauth/authorize?${params.toString()}`;
};
Bước 5: Trao đổi mã lấy access token
const exchangeCodeForToken = async (code) => {
const response = await fetch('https://api.hubspot.com/oauth/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: HUBSPOT_CLIENT_ID,
client_secret: HUBSPOT_CLIENT_SECRET,
redirect_uri: HUBSPOT_REDIRECT_URI,
code: code
})
});
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
portalId: data.hub_portal_id
};
};
// Xử lý callback
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
await db.installations.create({
portalId: tokens.portalId,
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
tokenExpiry: Date.now() + (tokens.expiresIn * 1000)
});
res.redirect('/success');
} catch (error) {
console.error('OAuth error:', error);
res.status(500).send('Authentication failed');
}
});
Bước 6: Làm mới access token
const refreshAccessToken = async (refreshToken) => {
const response = await fetch('https://api.hubspot.com/oauth/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: HUBSPOT_CLIENT_ID,
client_secret: HUBSPOT_CLIENT_SECRET,
refresh_token: refreshToken
})
});
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in
};
};
const ensureValidToken = async (portalId) => {
const installation = await db.installations.findByPortalId(portalId);
if (installation.tokenExpiry < Date.now() + 1800000) {
const newTokens = await refreshAccessToken(installation.refreshToken);
await db.installations.update(installation.id, {
accessToken: newTokens.accessToken,
refreshToken: newTokens.refreshToken,
tokenExpiry: Date.now() + (newTokens.expiresIn * 1000)
});
return newTokens.accessToken;
}
return installation.accessToken;
};
Bước 7: Gọi API đã xác thực
const HUBSPOT_BASE_URL = 'https://api.hubspot.com';
const hubspotRequest = async (endpoint, options = {}, portalId = null) => {
const accessToken = portalId ? await ensureValidToken(portalId) : process.env.HUBSPOT_ACCESS_TOKEN;
const response = await fetch(`${HUBSPOT_BASE_URL}${endpoint}`, {
...options,
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`HubSpot API Error: ${error.message}`);
}
return response.json();
};
// Ví dụ:
const contacts = await hubspotRequest('/crm/v3/objects/contacts');
Làm việc với các đối tượng CRM
Tạo liên hệ
const createContact = async (contactData) => {
const contact = {
properties: {
email: contactData.email,
firstname: contactData.firstName,
lastname: contactData.lastName,
phone: contactData.phone,
company: contactData.company,
website: contactData.website,
lifecyclestage: contactData.lifecycleStage || 'lead'
}
};
const response = await hubspotRequest('/crm/v3/objects/contacts', {
method: 'POST',
body: JSON.stringify(contact)
});
return response;
};
// Sử dụng
const contact = await createContact({
email: 'john.doe@example.com',
firstName: 'John',
lastName: 'Doe',
phone: '+1-555-0123',
company: 'Acme Corp',
lifecycleStage: 'customer'
});
console.log(`Contact created: ${contact.id}`);
Thuộc tính liên hệ
| Thuộc tính | Loại | Mô tả |
|---|---|---|
email |
Chuỗi | Email chính (unique) |
firstname |
Chuỗi | Tên |
lastname |
Chuỗi | Họ |
phone |
Chuỗi | Số điện thoại |
company |
Chuỗi | Tên công ty |
website |
Chuỗi | URL trang web |
lifecyclestage |
Enum | lead, marketingqualifiedlead, ... |
createdate |
Ngày giờ | Tự động tạo |
lastmodifieddate |
Ngày giờ | Tự động tạo |
Lấy liên hệ theo ID
const getContact = async (contactId) => {
const response = await hubspotRequest(`/crm/v3/objects/contacts/${contactId}`);
return response;
};
// Sử dụng
const contact = await getContact('12345');
console.log(`${contact.properties.firstname} ${contact.properties.lastname}`);
console.log(`Email: ${contact.properties.email}`);
Tìm kiếm liên hệ
const searchContacts = async (searchCriteria) => {
const response = await hubspotRequest('/crm/v3/objects/contacts/search', {
method: 'POST',
body: JSON.stringify({
filterGroups: searchCriteria,
properties: ['firstname', 'lastname', 'email', 'company'],
limit: 100
})
});
return response;
};
// Tìm contact thuộc công ty cụ thể
const results = await searchContacts({
filterGroups: [
{
filters: [
{
propertyName: 'company',
operator: 'EQ',
value: 'Acme Corp'
}
]
}
]
});
results.results.forEach(contact => {
console.log(`${contact.properties.email}`);
});
Các toán tử lọc tìm kiếm
| Toán tử | Mô tả | Ví dụ |
|---|---|---|
EQ |
Bằng | company EQ 'Acme' |
NEQ |
Không bằng | lifecyclestage NEQ 'subscriber' |
CONTAINS_TOKEN |
Chứa | email CONTAINS_TOKEN 'gmail' |
NOT_CONTAINS_TOKEN |
Không chứa | email NOT_CONTAINS_TOKEN 'test' |
GT |
Lớn hơn | createdate GT '2026-01-01' |
LT |
Nhỏ hơn | createdate LT '2026-12-31' |
GTE |
>= | deal_amount GTE 10000 |
LTE |
<= | deal_amount LTE 50000 |
HAS_PROPERTY |
Có giá trị | phone HAS_PROPERTY |
NOT_HAS_PROPERTY |
Thiếu giá trị | phone NOT_HAS_PROPERTY |
Tạo công ty
const createCompany = async (companyData) => {
const company = {
properties: {
name: companyData.name,
domain: companyData.domain,
industry: companyData.industry,
numberofemployees: companyData.employees,
annualrevenue: companyData.revenue,
city: companyData.city,
state: companyData.state,
country: companyData.country
}
};
const response = await hubspotRequest('/crm/v3/objects/companies', {
method: 'POST',
body: JSON.stringify(company)
});
return response;
};
// Sử dụng
const company = await createCompany({
name: 'Acme Corporation',
domain: 'acme.com',
industry: 'Technology',
employees: 500,
revenue: 50000000,
city: 'San Francisco',
state: 'CA',
country: 'USA'
});
Liên kết các đối tượng
const associateContactWithCompany = async (contactId, companyId) => {
const response = await hubspotRequest(
`/crm/v3/objects/contacts/${contactId}/associations/companies/${companyId}`,
{
method: 'PUT',
body: JSON.stringify({
types: [
{
associationCategory: 'HUBSPOT_DEFINED',
associationTypeId: 1 // Contact to Company
}
]
})
}
);
return response;
};
// Sử dụng
await associateContactWithCompany('12345', '67890');
Các loại liên kết
| Liên kết | ID loại | Hướng |
|---|---|---|
| Liên hệ → Công ty | 1 | Liên hệ được liên kết với Công ty |
| Công ty → Liên hệ | 1 | Công ty có liên hệ liên kết |
| Giao dịch → Liên hệ | 3 | Giao dịch liên kết Liên hệ |
| Giao dịch → Công ty | 5 | Giao dịch liên kết Công ty |
| Phiếu hỗ trợ → Liên hệ | 16 | Phiếu hỗ trợ liên kết Liên hệ |
| Phiếu hỗ trợ → Công ty | 15 | Phiếu hỗ trợ liên kết Công ty |
Tạo giao dịch (deal)
const createDeal = async (dealData) => {
const deal = {
properties: {
dealname: dealData.name,
amount: dealData.amount.toString(),
dealstage: dealData.stage || 'appointmentscheduled',
pipeline: dealData.pipelineId || 'default',
closedate: dealData.closeDate,
dealtype: dealData.type || 'newbusiness',
description: dealData.description
}
};
const response = await hubspotRequest('/crm/v3/objects/deals', {
method: 'POST',
body: JSON.stringify(deal)
});
return response;
};
// Sử dụng
const deal = await createDeal({
name: 'Acme Corp - Enterprise License',
amount: 50000,
stage: 'qualification',
closeDate: '2026-06-30',
type: 'newbusiness',
description: 'Enterprise annual subscription'
});
// Liên kết với company và contact
await hubspotRequest(
`/crm/v3/objects/deals/${deal.id}/associations/companies/${companyId}`,
{ method: 'PUT', body: JSON.stringify({ types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 5 }] }) }
);
await hubspotRequest(
`/crm/v3/objects/deals/${deal.id}/associations/contacts/${contactId}`,
{ method: 'PUT', body: JSON.stringify({ types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 3 }] }) }
);
Giai đoạn giao dịch mặc định
| Giai đoạn | Giá trị nội bộ |
|---|---|
| Cuộc hẹn đã được lên lịch | appointmentscheduled |
| Đủ điều kiện mua | qualifiedtobuy |
| Buổi trình bày đã được lên lịch | presentationscheduled |
| Người ra quyết định đã tham gia | decisionmakerboughtin |
| Hợp đồng đã gửi | contractsent |
| Đã chốt thành công | closedwon |
| Đã mất | closedlost |
Webhooks
Cấu hình webhook
const createWebhook = async (webhookData) => {
const response = await hubspotRequest('/webhooks/v3/my-app/webhooks', {
method: 'POST',
body: JSON.stringify({
webhookUrl: webhookData.url,
eventTypes: webhookData.events,
objectType: webhookData.objectType,
propertyName: webhookData.propertyName // Optional
})
});
return response;
};
// Sử dụng
const webhook = await createWebhook({
url: 'https://myapp.com/webhooks/hubspot',
events: [
'contact.creation',
'contact.propertyChange',
'company.creation',
'deal.creation',
'deal.stageChange'
],
objectType: 'contact'
});
console.log(`Webhook created: ${webhook.id}`);
Các loại sự kiện webhook
| Loại sự kiện | Kích hoạt |
|---|---|
contact.creation |
Liên hệ mới tạo |
contact.propertyChange |
Liên hệ thay đổi thuộc tính |
contact.deletion |
Liên hệ bị xóa |
company.creation |
Công ty mới tạo |
company.propertyChange |
Công ty thay đổi thuộc tính |
deal.creation |
Giao dịch mới tạo |
deal.stageChange |
Giao dịch đổi giai đoạn |
deal.propertyChange |
Giao dịch đổi thuộc tính |
ticket.creation |
Phiếu hỗ trợ mới tạo |
ticket.propertyChange |
Phiếu hỗ trợ đổi thuộc tính |
Xử lý webhook
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/hubspot', express.json(), async (req, res) => {
const signature = req.headers['x-hubspot-signature'];
const payload = JSON.stringify(req.body);
// Xác thực signature
const isValid = verifyWebhookSignature(payload, signature, process.env.HUBSPOT_CLIENT_SECRET);
if (!isValid) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
const events = req.body;
for (const event of events) {
console.log(`Event: ${event.eventType}`);
console.log(`Object: ${event.objectType} - ${event.objectId}`);
console.log(`Property: ${event.propertyName}`);
console.log(`Value: ${event.propertyValue}`);
// Xử lý tùy loại event
switch (event.eventType) {
case 'contact.creation':
await handleContactCreation(event);
break;
case 'contact.propertyChange':
await handleContactUpdate(event);
break;
case 'deal.stageChange':
await handleDealStageChange(event);
break;
}
}
res.status(200).send('OK');
});
function verifyWebhookSignature(payload, signature, clientSecret) {
const expectedSignature = crypto
.createHmac('sha256', clientSecret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
);
}
Giới hạn tốc độ
Hiểu về giới hạn tốc độ
| Cấp độ | Yêu cầu/giây | Yêu cầu/ngày |
|---|---|---|
| Miễn phí/Khởi đầu | 100 | 100.000 |
| Chuyên nghiệp | 200 | 500.000 |
| Doanh nghiệp | 400 | 1.000.000 |
Vượt giới hạn sẽ trả về HTTP 429.
Header giới hạn tốc độ
| Header | Ý nghĩa |
|---|---|
X-HubSpot-RateLimit-Second-Limit |
Max request/giây |
X-HubSpot-RateLimit-Second-Remaining |
Request còn lại trong 1 giây |
X-HubSpot-RateLimit-Second-Reset |
Số giây đến khi reset lại |
X-HubSpot-RateLimit-Daily-Limit |
Max request/ngày |
X-HubSpot-RateLimit-Daily-Remaining |
Request còn lại hôm nay |
X-HubSpot-RateLimit-Daily-Reset |
Số giây đến khi reset ngày |
Triển khai xử lý rate limit
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await hubspotRequest(endpoint, options);
const remaining = response.headers.get('X-HubSpot-RateLimit-Second-Remaining');
if (remaining < 10) {
console.warn(`Low rate limit remaining: ${remaining}`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
// Rate limiter class
class HubSpotRateLimiter {
constructor(requestsPerSecond = 90) {
this.queue = [];
this.interval = 1000 / requestsPerSecond;
this.processing = false;
}
async add(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push({ requestFn, resolve, reject });
this.process();
});
}
async process() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const { requestFn, resolve, reject } = this.queue.shift();
try {
const result = await requestFn();
resolve(result);
} catch (error) {
reject(error);
}
if (this.queue.length > 0) {
await new Promise(r => setTimeout(r, this.interval));
}
}
this.processing = false;
}
}
Checklist triển khai sản xuất
Trước khi go-live, kiểm tra:
- [ ] Xác thực bằng OAuth 2.0 hoặc ứng dụng riêng tư
- [ ] Lưu token an toàn (DB mã hóa)
- [ ] Refresh token tự động
- [ ] Xử lý rate limit/hàng đợi request
- [ ] Điểm cuối webhook dùng HTTPS
- [ ] Xử lý lỗi toàn diện
- [ ] Log toàn bộ call API
- [ ] Giám sát sử dụng rate limit
- [ ] Tạo runbook cho sự cố thường gặp
Các trường hợp sử dụng thực tế
Đồng bộ hóa CRM
- Vấn đề: Nhập liệu thủ công giữa app và HubSpot
- Giải pháp: Đồng bộ realtime qua webhook + API
- Kết quả: Không còn nhập tay, dữ liệu chính xác 100%
Định tuyến khách hàng tiềm năng
- Vấn đề: Phản hồi khách hàng tiềm năng chậm
- Giải pháp: Webhook tự động phân bổ cho sales
- Kết quả: Phản hồi trong 5 phút, tăng 40% chuyển đổi
Kết luận
API HubSpot cung cấp nền tảng tự động hóa CRM và marketing mạnh mẽ. Điểm then chốt:
- Dùng OAuth 2.0 cho app đa tenant, ứng dụng riêng tư cho nội bộ
- Giới hạn tốc độ theo gói (100-400 req/s)
- Webhooks hỗ trợ đồng bộ realtime
- Đối tượng CRM linh hoạt với custom field và liên kết
- Apidog tối ưu kiểm thử API, cộng tác nhóm
Phần Câu hỏi thường gặp (FAQ)
Làm cách nào để xác thực với API của HubSpot?
Dùng OAuth 2.0 cho app đa tenant, ứng dụng riêng tư cho tích hợp một portal. API key đã không còn hỗ trợ.
Giới hạn tốc độ của HubSpot là gì?
Từ 100 req/s (gói miễn phí) đến 400 req/s (Doanh nghiệp), giới hạn ngày 100.000–1.000.000 request.
Làm cách nào để tạo một liên hệ trong HubSpot?
Gửi POST /crm/v3/objects/contacts với thuộc tính email, tên, họ hoặc trường custom.
Tôi có thể tạo thuộc tính tùy chỉnh không?
Có, dùng API Thuộc tính để tạo custom field cho mọi object.
Webhooks hoạt động như thế nào trong HubSpot?
Cấu hình webhook trong app settings. HubSpot sẽ POST về endpoint bạn khi event xảy ra.
Top comments (0)