En bref
API HubSpot permet aux développeurs de s'intégrer en profondeur avec les hubs CRM, marketing, ventes et services HubSpot. Elle propose l'authentification OAuth 2.0 ou via applications privées, des points de terminaison RESTful pour contacts, entreprises, transactions, tickets, etc., et des limites de débit adaptées à votre abonnement. Ce guide détaille la configuration de l'authentification, les principaux endpoints, la gestion des webhooks et les bonnes pratiques pour un déploiement fiable.
Essayez Apidog dès aujourd'hui
Introduction
HubSpot gère plus de 194 000 comptes clients et des milliards d'enregistrements CRM. Pour automatiser la synchronisation CRM, l'automatisation marketing ou la gestion des ventes, l'intégration native de l'API HubSpot s'impose pour toucher plus de 7 millions d'utilisateurs.
À retenir : les entreprises perdent 15 à 20 heures/semaine en saisie manuelle entre systèmes. Un connecteur robuste via l'API HubSpot automatise la synchronisation des contacts, la gestion des transactions, l'automatisation marketing et le reporting inter-applications.
💡 Apidog simplifie les tests d'intégration d'API : testez vos endpoints HubSpot, validez les flux OAuth, inspectez les payloads webhooks et déboguez l'authentification dans un seul workspace. Importez vos specs, simulez des réponses et partagez vos scénarios de tests en équipe.
Qu'est-ce que l'API HubSpot ?
HubSpot expose une API RESTful pour gérer vos données CRM et automatiser vos processus marketing. Vous pouvez manipuler :
- Contacts, entreprises, transactions, tickets, objets personnalisés
- E-mails marketing, landing pages
- Pipelines de vente, séquences
- Tickets et conversations de support
- Analytics et reporting
- Workflows d'automatisation
- Fichiers et assets
Fonctionnalités clés
| Fonctionnalité | Description |
|---|---|
| Conception RESTful | Méthodes HTTP standard, réponses JSON |
| OAuth 2.0 + Applications privées | Authentification flexible |
| Webhooks | Notifications temps réel sur les changements d'objets |
| Limitation de débit | Dépend du plan (100-400 req/sec) |
| Objets CRM | Standard et personnalisés |
| Associations | Lier des objets (contact-entreprise, etc.) |
| Propriétés | Champs personnalisés par objet |
| API de recherche | Filtrage & tri complexes |
Vue d'ensemble de l'architecture de l'API
API REST versionnées, base URL :
https://api.hubapi.com/
Comparaison des versions de l'API
| Version | Statut | Authentification | Cas d'utilisation |
|---|---|---|---|
| API CRM v3 | Actuelle | OAuth 2.0, Application privée | Toutes les nouvelles intégrations |
| API Automatisation v4 | Actuelle | OAuth 2.0, Application privée | Inscription au workflow |
| API E-mail marketing | Actuelle | OAuth 2.0, Application privée | Campagnes d'e-mail |
| API Contacts v1 | Obsolète | Clé API (héritée) | Migrer vers v3 |
| API Entreprises v1 | Obsolète | Clé API (héritée) | Migrer vers v3 |
Important : la clé API HubSpot est dépréciée. Utilisez OAuth 2.0 ou une application privée.
Premiers pas : Configuration de l'authentification
Étape 1 : Créer un compte développeur HubSpot
- Allez sur le Portail développeur HubSpot
- Connectez-vous ou créez un compte
- Naviguez vers Applications dans votre dashboard développeur
- Cliquez sur Créer une application
Étape 2 : Choisir la méthode d'authentification
| Méthode | Pour qui ? | Sécurité |
|---|---|---|
| OAuth 2.0 | Apps multi-locataires, publiques | Élevée (user-based) |
| Application privée | Intégrations internes, single portal | Élevée (portal-based) |
Étape 3 : Configurer une application privée (recommandé pour usage interne)
- Allez dans Paramètres > Intégrations > Applications privées
- Cliquez sur Créer une application privée
- Sélectionnez les scopes nécessaires :
contacts
crm.objects.companies
crm.objects.deals
crm.objects.tickets
automation
webhooks
- Générez un jeton d'accès et stockez-le dans un fichier
.env:
# .env
HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HUBSPOT_PORTAL_ID="12345678"
Étape 4 : Configurer OAuth 2.0 (pour apps multi-locataires)
Configurez vos paramètres OAuth :
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;
// Générer l'URL d'autorisation
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()}`;
};
Étape 5 : Échanger le code contre un jeton d'accès
const exchangeCodeForToken = async (code) => {
const response = await fetch('https://api.hubapi.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
};
};
// Gestion du callback OAuth
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// Stockez les tokens
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('Erreur OAuth :', error);
res.status(500).send('Échec de l\'authentification');
}
});
Étape 6 : Rafraîchir le jeton d'accès
const refreshAccessToken = async (refreshToken) => {
const response = await fetch('https://api.hubapi.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
};
};
// Middleware pour garantir un token valide
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;
};
Étape 7 : Effectuer des appels API authentifiés
Centralisez vos appels :
const HUBSPOT_BASE_URL = 'https://api.hubapi.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(`Erreur API HubSpot : ${error.message}`);
}
return response.json();
};
// Exemple d'utilisation
const contacts = await hubspotRequest('/crm/v3/objects/contacts');
Travailler avec les objets CRM
Création d'un contact
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;
};
// Utilisation
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 créé : ${contact.id}`);
Propriétés des contacts
| Propriété | Type | Description |
|---|---|---|
email |
String | E-mail principal (identifiant unique) |
firstname |
String | Prénom |
lastname |
String | Nom de famille |
phone |
String | Numéro de téléphone |
company |
String | Nom de l'entreprise |
website |
String | URL du site web |
lifecyclestage |
Enum | lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist, subscriber |
createdate |
DateTime | Généré automatiquement |
lastmodifieddate |
DateTime | Généré automatiquement |
Obtenir un contact
const getContact = async (contactId) => {
const response = await hubspotRequest(`/crm/v3/objects/contacts/${contactId}`);
return response;
};
// Utilisation
const contact = await getContact('12345');
console.log(`${contact.properties.firstname} ${contact.properties.lastname}`);
console.log(`E-mail : ${contact.properties.email}`);
Recherche de contacts
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;
};
// Exemple : trouver tous les contacts d'une entreprise spécifique
const results = await searchContacts({
filterGroups: [
{
filters: [
{
propertyName: 'company',
operator: 'EQ',
value: 'Acme Corp'
}
]
}
]
});
results.results.forEach(contact => {
console.log(`${contact.properties.email}`);
});
Opérateurs de filtre de recherche
| Opérateur | Description | Exemple |
|---|---|---|
EQ |
Égal à | company EQ 'Acme' |
NEQ |
Non égal à | lifecyclestage NEQ 'subscriber' |
CONTAINS_TOKEN |
Contient | email CONTAINS_TOKEN 'gmail' |
NOT_CONTAINS_TOKEN |
Ne contient pas | email NOT_CONTAINS_TOKEN 'test' |
GT |
Supérieur à | createdate GT '2026-01-01' |
LT |
Inférieur à | createdate LT '2026-12-31' |
GTE |
Supérieur ou égal | deal_amount GTE 10000 |
LTE |
Inférieur ou égal | deal_amount LTE 50000 |
HAS_PROPERTY |
A une valeur | phone HAS_PROPERTY |
NOT_HAS_PROPERTY |
Valeur manquante | phone NOT_HAS_PROPERTY |
Création d'une entreprise
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;
};
// Utilisation
const company = await createCompany({
name: 'Acme Corporation',
domain: 'acme.com',
industry: 'Technology',
employees: 500,
revenue: 50000000,
city: 'San Francisco',
state: 'CA',
country: 'USA'
});
Association d'objets
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 vers Entreprise
}
]
})
}
);
return response;
};
// Utilisation
await associateContactWithCompany('12345', '67890');
Types d'association
| Association | ID de type | Direction |
|---|---|---|
| Contact → Entreprise | 1 | Le contact est associé à l'entreprise |
| Entreprise → Contact | 1 | L'entreprise a un contact associé |
| Transaction → Contact | 3 | La transaction est associée au contact |
| Transaction → Entreprise | 5 | La transaction est associée à l'entreprise |
| Ticket → Contact | 16 | Le ticket est associé au contact |
| Ticket → Entreprise | 15 | Le ticket est associé à l'entreprise |
Création d'une transaction
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;
};
// Utilisation
const deal = await createDeal({
name: 'Acme Corp - Licence Entreprise',
amount: 50000,
stage: 'qualification',
closeDate: '2026-06-30',
type: 'newbusiness',
description: 'Abonnement annuel entreprise'
});
// Associer avec l'entreprise et le 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 }] }) }
);
Étapes de transaction (pipeline par défaut)
| Étape | Valeur interne |
|---|---|
| Rendez-vous programmés | appointmentscheduled |
| Qualifié pour acheter | qualifiedtobuy |
| Présentation programmée | presentationscheduled |
| Décideur convaincu | decisionmakerboughtin |
| Contrat envoyé | contractsent |
| Fermée gagnée | closedwon |
| Fermée perdue | closedlost |
Webhooks
Configuration des Webhooks
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 // Optionnel
})
});
return response;
};
// Utilisation
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 créé : ${webhook.id}`);
Types d'événements de webhook
| Type d'événement | Déclencheur |
|---|---|
contact.creation |
Nouveau contact créé |
contact.propertyChange |
Propriété du contact mise à jour |
contact.deletion |
Contact supprimé |
company.creation |
Nouvelle entreprise créée |
company.propertyChange |
Propriété de l'entreprise mise à jour |
deal.creation |
Nouvelle transaction créée |
deal.stageChange |
Étape de la transaction modifiée |
deal.propertyChange |
Propriété de la transaction à jour |
ticket.creation |
Nouveau ticket créé |
ticket.propertyChange |
Propriété du ticket à jour |
Gestion des Webhooks
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);
// Vérifier la signature du webhook
const isValid = verifyWebhookSignature(payload, signature, process.env.HUBSPOT_CLIENT_SECRET);
if (!isValid) {
console.error('Signature de webhook invalide');
return res.status(401).send('Non autorisé');
}
const events = req.body;
for (const event of events) {
console.log(`Événement : ${event.eventType}`);
console.log(`Objet : ${event.objectType} - ${event.objectId}`);
console.log(`Propriété : ${event.propertyName}`);
console.log(`Valeur : ${event.propertyValue}`);
// Dispatcher selon l'événement
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')
);
}
Limitation de débit
Comprendre les limites de débit
| Niveau | Requêtes/seconde | Requêtes/jour |
|---|---|---|
| Gratuit/Starter | 100 | 100 000 |
| Professionnel | 200 | 500 000 |
| Entreprise | 400 | 1 000 000 |
Un dépassement déclenche des réponses HTTP 429 (Too Many Requests).
En-têtes de limitation de débit
| En-tête | Description |
|---|---|
X-HubSpot-RateLimit-Second-Limit |
Limite max/seconde |
X-HubSpot-RateLimit-Second-Remaining |
Requêtes restantes cette seconde |
X-HubSpot-RateLimit-Second-Reset |
Secondes avant réinitialisation |
X-HubSpot-RateLimit-Daily-Limit |
Limite max/jour |
X-HubSpot-RateLimit-Daily-Remaining |
Requêtes restantes aujourd'hui |
X-HubSpot-RateLimit-Daily-Reset |
Secondes avant réinitialisation journalière |
Mise en œuvre de la gestion de la limitation de débit
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await hubspotRequest(endpoint, options);
// Journalisation de la limite de débit
const remaining = response.headers.get('X-HubSpot-RateLimit-Second-Remaining');
if (remaining < 10) {
console.warn(`Faible limite de débit restante : ${remaining}`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Limite de débit atteinte. Nouvelle tentative dans ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
// Rate limiter simple
class HubSpotRateLimiter {
constructor(requestsPerSecond = 90) { // Rester sous la limite
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;
}
}
Liste de contrôle pour le déploiement en production
Avant de passer en production, vérifiez les points suivants :
- [ ] Authentification via application privée ou OAuth 2.0
- [ ] Stockage sécurisé des jetons (base de données chiffrée)
- [ ] Rafraîchissement automatique des jetons
- [ ] Gestion de la limitation de débit et file d'attente des requêtes
- [ ] Point de terminaison Webhook en HTTPS
- [ ] Gestion complète des erreurs
- [ ] Journalisation des appels API
- [ ] Surveillance de l'utilisation des limites de débit
- [ ] Documentation sur la résolution des problèmes courants
Cas d'utilisation concrets
Synchronisation CRM
- Défi : Saisie manuelle des données entre l'app et HubSpot
- Solution : Synchronisation temps réel via webhooks + API
- Résultat : Plus de saisie manuelle, données 100% synchronisées
Routage des prospects
- Défi : Temps de réponse lent sur les nouveaux leads
- Solution : Webhooks pour router les prospects automatiquement vers les bons commerciaux
- Résultat : Temps de réponse réduit à 5 min, +40% de conversions
Conclusion
L'API HubSpot propose une plateforme robuste pour la gestion CRM et l'automatisation marketing. À retenir :
- Utilisez OAuth 2.0 pour les apps multi-locataires, l'application privée pour les usages internes
- Les limites de débit varient selon le plan (100-400 req/sec)
- Les webhooks garantissent la synchronisation temps réel
- Les objets CRM supportent associations et champs personnalisés
- Apidog facilite les tests d'API et la collaboration
Section FAQ
Comment s'authentifier avec l'API HubSpot ?
Utilisez OAuth 2.0 pour les applications multi-locataires, ou application privée pour les intégrations internes. La clé API est obsolète.
Quelles sont les limites de débit de HubSpot ?
De 100 req/sec (Gratuit) à 400 req/sec (Entreprise), et de 100 000 à 1 million de requêtes quotidiennes.
Comment créer un contact dans HubSpot ?
POST sur /crm/v3/objects/contacts avec les propriétés (email, prénom, nom, etc.).
Puis-je créer des propriétés personnalisées ?
Oui. Utilisez l'API Propriétés pour ajouter des champs sur tout type d'objet.
Comment fonctionnent les webhooks dans HubSpot ?
Configurez-les dans votre app : HubSpot enverra des POST vers votre endpoint sur les événements configurés.
Top comments (0)