API REST et SOAP dans Salesforce Marketing Cloud : le guide 2026

Tout projet Salesforce Marketing Cloud finit par buter sur la même question : comment faire dialoguer la plateforme avec le reste du système d'information ? Un site e-commerce doit déclencher un e-mail transactionnel en quelques secondes, un entrepôt de données doit alimenter une Data Extension chaque nuit, un back-office doit corriger un statut d'abonnement en temps réel. SFMC expose pour cela deux familles d'API — REST et SOAP — qui coexistent, ne couvrent pas les mêmes objets et ne réagissent pas de la même façon sous charge. Voici les patterns d'intégration que nous déployons chez nos clients, avec le code correspondant et les pièges à éviter.

REST ou SOAP : trancher selon l'objet manipulé

La règle mentale est simple : REST pour les données et les envois, SOAP pour la configuration et les objets historiques. L'API REST couvre les Data Extensions, les Journeys, les Assets du Content Builder et les envois transactionnels. L'API SOAP, héritée d'ExactTarget, reste la seule voie d'accès à un ensemble d'objets que REST n'a jamais rapatriés.

BesoinAPIObjet / endpoint
Upsert de lignes en Data ExtensionREST/data/v1/async/dataextensions/key:CLE/rows
Envoi transactionnelREST/messaging/v1/email/messages
Injection dans un JourneyREST/interaction/v1/events
Statut d'abonnement globalSOAPSubscriber
Listes et abonnementsSOAPListSubscriber
Création d'une Data ExtensionSOAPDataExtension
Lecture des Data ViewsSOAPAutomation + requête SQL

En pratique, la quasi-totalité des intégrations sérieuses sont hybrides. Assumez-le dès la conception plutôt que de forcer un seul protocole : vous éviterez des contournements coûteux six mois plus tard.

Fondation n°1 : un package installé par intégration

L'erreur la plus fréquente consiste à créer un seul package installé et à distribuer ses identifiants à toutes les équipes. Le jour où vous devez révoquer un accès, vous coupez tout le monde. Créez un package par consommateur (site web, ETL, back-office, outil de support) avec le périmètre minimal.

Dans Setup > Apps > Installed Packages, ajoutez un composant API Integration de type Server-to-Server, puis ne cochez que les portées nécessaires. Un ETL n'a besoin que de data_extensions_read et data_extensions_write ; il n'a aucune raison de disposer de email_write ou de journeys_execute.

Obtenir un token

POST https://VOTRE_SOUS_DOMAINE.auth.marketingcloudapis.com/v2/token
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "xxxxxxxx",
  "client_secret": "yyyyyyyy",
  "account_id": "7281234",
  "scope": "data_extensions_read data_extensions_write"
}

La réponse contient access_token, expires_in (1 080 secondes) et surtout rest_instance_url et soap_instance_url. Ne codez jamais en dur ces deux URL : lisez-les depuis la réponse du token. Les tenants sont régulièrement déplacés d'une stack à l'autre et un endpoint figé finit toujours par tomber.

Fondation n°2 : mettre le token en cache

Demander un token à chaque appel est le meilleur moyen de saturer votre quota d'authentification et d'ajouter 200 ms inutiles à chaque requête. Mettez-le en cache et renouvelez-le avec une marge de sécurité. Voici le pattern en SSJS, utilisable depuis une CloudPage ou un Script Activity :

<script runat="server">
Platform.Load("Core", "1.1.1");

var de = DataExtension.Init("API_TOKEN_CACHE");
var rows = de.Rows.Lookup(["TokenKey"], ["ETL"]);
var now = new Date();
var token = null;

if (rows.length > 0 && new Date(rows[0].ExpiresAt) > now) {
    token = rows[0].AccessToken;
} else {
    var auth = HTTP.Post(
        "https://VOTRE_SOUS_DOMAINE.auth.marketingcloudapis.com/v2/token",
        "application/json",
        Stringify({
            grant_type: "client_credentials",
            client_id: clientId,
            client_secret: clientSecret,
            account_id: mid
        })
    );
    var body = Platform.Function.ParseJSON(auth.Response[0]);
    token = body.access_token;

    // marge de 120 secondes avant expiration reelle
    var expires = new Date(now.getTime() + (body.expires_in - 120) * 1000);
    de.Rows.Update(
        { AccessToken: token, ExpiresAt: expires },
        ["TokenKey"], ["ETL"]
    );
}
</script>

La marge de 120 secondes évite le cas classique du token valide au moment du lookup mais expiré au moment où la requête arrive côté serveur.

Charger du volume : l'upsert asynchrone

Pour les chargements de masse, l'endpoint synchrone plafonne rapidement. L'endpoint asynchrone accepte des lots plus larges, rend la main immédiatement et vous fournit un identifiant de suivi :

POST /data/v1/async/dataextensions/key:CONTACT_MASTER/rows
Authorization: Bearer {token}
Content-Type: application/json

{
  "items": [
    { "SubscriberKey": "C-10482", "Email": "a.dupont@exemple.fr",
      "Segment": "VIP", "DateMaj": "2026-09-08T09:12:00Z" },
    { "SubscriberKey": "C-10483", "Email": "m.leroy@exemple.fr",
      "Segment": "Standard", "DateMaj": "2026-09-08T09:12:00Z" }
  ]
}

La réponse renvoie un requestId. Interrogez ensuite /data/v1/async/{requestId}/status puis /results pour récupérer le détail des lignes rejetées. Trois règles de production :

  • Des lots de 2 000 à 5 000 lignes offrent le meilleur compromis débit/reprise sur erreur.
  • La clé primaire de la Data Extension doit être indexée, sinon l'upsert dégénère en balayage complet.
  • Journalisez systématiquement le requestId côté appelant : sans lui, aucun diagnostic n'est possible a posteriori.

Ce que seul SOAP sait encore faire

Modifier le statut d'un abonné au niveau du All Subscribers reste hors de portée de REST. Le désabonnement global, notamment, passe obligatoirement par SOAP :

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <fueloauth>{token}</fueloauth>
  </s:Header>
  <s:Body>
    <UpdateRequest xmlns="http://exacttarget.com/wsdl/partnerAPI">
      <Options/>
      <Objects xsi:type="Subscriber"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <SubscriberKey>C-10482</SubscriberKey>
        <EmailAddress>a.dupont@exemple.fr</EmailAddress>
        <Status>Unsubscribed</Status>
      </Objects>
    </UpdateRequest>
  </s:Body>
</s:Envelope>

Notez que le token OAuth 2.0 obtenu plus haut fonctionne aussi en SOAP : il se place dans l'en-tête fueloauth. Vous n'avez donc qu'un seul mécanisme d'authentification à maintenir, même dans une intégration hybride.

Le token OAuth 2.0 est commun à REST et à SOAP. Un cache de token unique, correctement partagé, simplifie l'architecture bien plus que le choix du protocole lui-même.

Quotas, erreurs et idempotence

SFMC applique des limites par tenant et par endpoint. Elles ne sont pas publiées de façon exhaustive, ce qui impose de concevoir défensivement dès le départ.

CodeSignificationRéaction attendue
401Token expiré ou révoquéPurger le cache, redemander un token, rejouer une fois
403Portée manquante sur le packageNe pas rejouer : alerter, corriger la configuration
429Quota dépasséBackoff exponentiel avec jitter
500 / 502Incident côté plateformeRejouer, puis mettre en file d'attente

Un backoff exponentiel avec bruit aléatoire évite que tous vos workers ne repartent en même temps :

function callWithRetry(fn, maxAttempts) {
  var attempt = 0;
  while (true) {
    try {
      return fn();
    } catch (e) {
      attempt++;
      if (attempt >= maxAttempts || !isRetryable(e)) throw e;
      var base = Math.pow(2, attempt) * 500;
      var jitter = Math.random() * 500;
      sleep(base + jitter);
    }
  }
}

Rendez enfin vos appels idempotents. Un upsert sur clé primaire l'est nativement. Un envoi transactionnel ne l'est pas : sans précaution, un rejeu double l'e-mail. Utilisez le champ messageKey de l'API Transactional Messaging, qui déduplique côté SFMC pendant plusieurs jours.

Sécuriser et observer

Stockez les secrets dans un coffre (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), jamais dans un Script Activity ni dans une Data Extension en clair. Prévoyez une rotation semestrielle documentée et testée. Côté observabilité, trois métriques suffisent à détecter 90 % des incidents : le taux de 429 par minute, la latence du p95 par endpoint et le nombre de lignes rejetées par lot asynchrone. Poussez-les dans l'outil de supervision que vos équipes consultent déjà — un tableau de bord que personne n'ouvre ne sert à rien.

À retenir

Hybride par conception. REST pour les données et les envois, SOAP pour la configuration et les objets historiques : n'essayez pas de tout faire avec un seul protocole.

Un package par consommateur. Des portées minimales et des identifiants séparés vous permettent de révoquer un accès sans interrompre les autres intégrations.

Un cache de token partagé. Le même token OAuth 2.0 sert REST et SOAP ; renouvelez-le avec 120 secondes de marge et lisez toujours les URL d'instance depuis la réponse.

Asynchrone pour le volume. Des lots de 2 000 à 5 000 lignes, une clé primaire indexée et un requestId journalisé rendent vos chargements diagnosticables.

Idempotence et backoff. Un rejeu ne doit jamais créer de doublon ; utilisez messageKey pour le transactionnel et un backoff exponentiel avec jitter sur les 429.

Vous préparez une intégration SFMC ou vous cherchez à fiabiliser une architecture existante ? Parlons de votre projet : nos consultants Salesforce Marketing Cloud auditent vos flux d'API et vous livrent un plan de remédiation concret.

A voir: