🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Requête HTTP en VBA dans Excel — GET et POST avec XMLHTTP, et pourquoi la réponse arrive périmée ou illisible

|

Requête HTTP en VBA dans Excel — GET et POST avec XMLHTTP, et pourquoi la réponse arrive périmée ou illisible

TL;DR — Une requête HTTP lancée depuis VBA vous rend exactement deux choses : un code de statut et un bloc d'octets. Elle ne vous dit pas si les données sont fraîches, quel encodage de texte utilisent ces octets, ni même si la requête a atteint le serveur à temps. Tout cela, c'est votre travail. Choisissez l'objet en fonction du réseau qu'il doit traverser (MSXML2.XMLHTTP utilise les paramètres et le cache du navigateur de l'utilisateur, ServerXMLHTTP et WinHttpRequest vous donnent des délais d'expiration et aucun cache), vérifiez le statut vous-même, car un 404 ou un 500 ne déclenche jamais d'erreur, et décodez les octets délibérément dès que le serveur envoie autre chose que de l'UTF-8.

Dim http As Object
Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
http.setTimeouts 5000, 5000, 10000, 30000
http.Open "GET", "https://api.example.com/rates?base=EUR", False
http.send
If http.Status = 200 Then Debug.Print http.responseText

Voici le premier article d'une série consacrée à faire entrer des données du web dans Excel : ici la requête elle-même, puis VBA JSON pour transformer la réponse en structure, et web scraping en VBA pour les pages qui n'ont jamais été conçues pour être lues par du code. L'idée de la série : une requête web vous remet du texte, pas des données. Entre le serveur et vos cellules se trouvent trois traductions qui vous appartiennent : des octets au texte, du texte à la structure, de la structure à la grille. Presque toutes les pannes se produisent à l'une de ces frontières, pas dans la requête.

Ce que vous allez apprendre

  • Le modèle mental : un statut et quelques octets
  • Trois objets, trois piles réseau
  • Deux sortes d'échec : les erreurs qui se déclenchent et les statuts qui ne déclenchent rien
  • Pourquoi un GET renvoie les données d'hier
  • Les délais d'expiration, et pourquoi une requête peut figer Excel
  • Accents illisibles : décoder les octets vous-même
  • Envoyer un POST avec un corps JSON
  • Chaînes de requête et clés d'API

Le modèle mental : un statut et quelques octets

Quand vous appelez http.send, VBA confie la requête à une bibliothèque réseau de Windows et attend. Ce qui revient est maigre : un nombre (Status, par exemple 200 ou 404), quelques en-têtes, et le corps sous forme d'octets. responseText n'est pas le corps ; c'est le corps déjà décodé en texte sur la foi d'une supposition. responseBody, ce sont les octets bruts.

Gardez cette image en tête et les pannes étranges cessent de l'être. Des données périmées signifient qu'une couche entre vous et le serveur a répondu à sa place. Un texte illisible signifie que la supposition de décodage était fausse. Une macro qui « marchait hier » et qui se bloque aujourd'hui signifie que le réseau a ralenti et que rien n'a dit à la requête d'abandonner.

Trois objets, trois piles réseau

Les trois objets que l'on copie depuis les forums semblent interchangeables. Ils ne le sont pas : chacun repose sur une pile réseau Windows différente, et c'est la pile qui décide du comportement de la requête sur le poste de votre utilisateur.

Objet Pile réseau Cache Proxy et authentification Délais d'expiration
MSXML2.XMLHTTP.6.0 WinINet (la pile derrière les anciens paramètres du navigateur) oui, les réponses GET peuvent être mises en cache utilise automatiquement les paramètres Internet Windows de l'utilisateur pas de méthode setTimeouts
MSXML2.ServerXMLHTTP.6.0 WinHTTP non le paramètre proxy propre à WinHTTP, souvent vide ; setProxy pour en définir un setTimeouts
WinHttp.WinHttpRequest.5.1 WinHTTP non comme ci-dessus ; SetProxy SetTimeouts, plus des options comme les versions de TLS

Ce tableau explique le ticket de support le plus fréquent : « ça marche sur mon portable et ça échoue dans les bureaux du client ». Les bureaux font passer le web par un proxy. XMLHTTP récupère ce proxy dans les paramètres de l'utilisateur ; ServerXMLHTTP et WinHttpRequest non, si bien qu'ils ne peuvent pas se connecter alors que le navigateur de la même machine fonctionne très bien. Indiquez-leur le proxy explicitement :

http.setProxy 2, "proxy.company.local:8080"     ' 2 = utiliser ce proxy

Les trois se créent avec CreateObject et ne demandent aucune référence, si bien que le même code tourne dans Office 32 bits comme 64 bits. Voir VBA CreateObject pour la liaison tardive en général.

Deux sortes d'échec : les erreurs qui se déclenchent et les statuts qui ne déclenchent rien

Une requête peut échouer à deux endroits, et VBA vous le signale par deux canaux différents.

Les échecs de transport déclenchent une erreur d'exécution. Pas de réseau, un nom de serveur qui ne se résout pas, un délai dépassé, une négociation TLS qui échoue : send s'arrête sur une erreur d'exécution, généralement un grand nombre négatif comme -2147012889 (le nom du serveur n'a pas pu être résolu) ou -2147012894 (le délai de l'opération a expiré). Interceptez-les avec On Error.

Les échecs HTTP ne déclenchent rien du tout. Un 404, un 401 parce que la clé a expiré, un 500 parce que le serveur est en panne : send se termine normalement, Status contient le code et responseText contient une page d'erreur ou un JSON d'erreur. Un code qui saute la vérification du statut écrit sans broncher « Unauthorized » ou une page de HTML dans votre feuille.

Chaque requête a donc besoin des deux vérifications. Regroupez-les dans une seule fonction et n'appelez send nulle part ailleurs :

Function HttpGet(ByVal url As String) As String
    Dim http As Object
    Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    http.setTimeouts 5000, 5000, 10000, 30000
    http.Open "GET", url, False
    http.setRequestHeader "Accept", "application/json"
    http.send                                   ' les erreurs de transport se declenchent ici
    If http.Status < 200 Or http.Status >= 300 Then
        Err.Raise vbObjectError + 513, "HttpGet", _
            "HTTP " & http.Status & " " & http.statusText & " for " & url
    End If
    HttpGet = http.responseText
End Function

Désormais, les deux sortes d'échec arrivent sous forme d'erreur d'exécution avec un message lisible, et l'appelant les traite en un seul endroit. Voir la gestion des erreurs en VBA pour le schéma qui l'entoure.

Pourquoi un GET renvoie les données d'hier

MSXML2.XMLHTTP passe par WinINet, et WinINet tient un cache. Si la réponse du serveur autorise la mise en cache, ou n'en dit rien, un GET répété vers la même URL peut recevoir sa réponse du cache sans jamais atteindre le serveur. La macro s'exécute, le statut vaut 200, et les taux de change datent de ce matin.

Trois issues, de la meilleure à la pire :

  1. Utilisez ServerXMLHTTP ou WinHttpRequest, qui n'ont pas de cache.
  2. Si vous devez rester sur XMLHTTP (pour sa gestion du proxy), obligez le cache à interroger de nouveau le serveur en envoyant une date très ancienne : http.setRequestHeader "If-Modified-Since", "Sat, 01 Jan 2000 00:00:00 GMT".
  3. Rendez chaque URL unique avec un paramètre jetable, comme "&_=" & Format(Now, "yyyymmddhhnnss"). Cela fonctionne, mais certaines API rejettent les paramètres qu'elles ne connaissent pas.

Le signe révélateur du cache : une requête qui répond instantanément avec exactement les mêmes données que la fois précédente, alors que la même URL dans un navigateur affiche quelque chose de plus récent.

Les délais d'expiration, et pourquoi une requête peut figer Excel

Le False de http.Open "GET", url, False rend la requête synchrone : VBA attend sur cette ligne jusqu'à l'arrivée de la réponse. Pendant cette attente, Excel ne peut ni se redessiner ni réagir, et Windows affiche la fenêtre comme Ne répond pas. Une API lente se transforme en Excel figé ; une API morte, sans délai d'expiration, peut le figer longtemps.

setTimeouts prend quatre valeurs en millisecondes : résolution du nom, connexion, envoi et réception. Réglez-les sur des valeurs adaptées à l'API. 5000, 5000, 10000, 30000 signifie : abandonner si le serveur ne peut être trouvé ou joint en cinq secondes, ou s'il n'a pas répondu en trente. XMLHTTP n'a pas de méthode équivalente, ce qui est une raison de plus de préférer ServerXMLHTTP pour tout ce qu'un utilisateur attend.

Pour une boucle qui enchaîne de nombreuses requêtes, mettez à jour la barre d'état entre les appels afin que l'utilisateur voie la progression. Les requêtes asynchrones existent (True comme troisième argument), mais les surveiller depuis VBA exige une boucle DoEvents et ajoute plus de sources de panne qu'elle n'en retire ; gardez des requêtes synchrones et courtes.

Accents illisibles : décoder les octets vous-même

responseText décode les octets avec le jeu de caractères que le serveur déclare dans son en-tête Content-Type, et suppose de l'UTF-8 quand le serveur n'en déclare aucun. La plupart des API modernes envoient de l'UTF-8 et le disent, et tout fonctionne. Un serveur plus ancien qui envoie du Windows-1252, de l'ISO-8859-1 ou du Shift_JIS sans le dire arrive sous la forme München, ou de points d'interrogation et de petits carrés.

Dans ce cas, laissez responseText de côté, prenez les octets bruts et décodez-les avec le bon jeu de caractères :

Function BytesToText(ByVal bytes As Variant, ByVal charset As String) As String
    With CreateObject("ADODB.Stream")
        .Type = 1                  ' binaire
        .Open
        .Write bytes
        .Position = 0
        .Type = 2                  ' texte
        .Charset = charset         ' "windows-1252", "iso-8859-1", "shift_jis"
        BytesToText = .ReadText
        .Close
    End With
End Function

body = BytesToText(http.responseBody, "windows-1252")

Un piège pendant le débogage : ne jugez pas le texte dans la fenêtre Exécution. L'éditeur VBA ne sait afficher que les caractères de la page de codes système de Windows : un texte japonais correct s'affiche donc en ??? sur votre Windows français, et un texte français correct peut s'afficher de travers sur un Windows japonais. Écrivez la chaîne dans une cellule ; la cellule montre la vérité.

Envoyer un POST avec un corps JSON

Un POST, c'est le même appel avec une méthode, un type de contenu et un corps :

http.Open "POST", "https://api.example.com/orders", False
http.setRequestHeader "Content-Type", "application/json"
http.send "{""sku"":""A-100"",""qty"":3}"

Le piège consiste à construire ce corps en collant des nombres dans une chaîne. VBA convertit un nombre en texte selon les paramètres régionaux de Windows : sur votre Windows français (comme sur un poste allemand ou espagnol), "{""price"":" & 3.5 & "}" produit {"price":3,5}, qui n'est pas du JSON valide. La même macro fonctionne à New York et échoue à Paris ou à Munich. Convertissez les nombres avec Trim$(Str$(x)), qui utilise toujours un point (voir VBA Str), ou mieux, construisez le corps avec une bibliothèque JSON comme le montre VBA JSON.

Chaînes de requête et clés d'API

Les valeurs placées dans une URL doivent être encodées en pourcentage : une espace, une esperluette ou un caractère accentué dans un paramètre de requête casse la requête ou la modifie en silence. Excel 2013 et versions ultérieures disposent d'une fonction pour cela, qui encode les lettres accentuées en UTF-8 : Zürich devient ainsi Z%C3%BCrich :

url = "https://api.example.com/search?city=" & _
      Application.WorksheetFunction.EncodeURL(Range("B2").Value)
' B2 = Zurich & Geneva  ->  city=Zurich%20%26%20Geneva

Les clés d'API vont dans un en-tête, généralement http.setRequestHeader "Authorization", "Bearer " & apiKey. Elles ne vont pas dans le code. Quiconque possède le classeur peut ouvrir l'éditeur VBA, et un mot de passe de projet VBA n'est pas une vraie protection. Lisez la clé à l'exécution, depuis un fichier du profil de l'utilisateur ou depuis une variable d'environnement avec Environ, afin de pouvoir partager le classeur sans partager la clé.

Le parti pris : une seule fonction de requête, trois vérifications

La plupart des macros HTTP défaillantes ne se trompent pas sur HTTP. Elles sautent l'une des trois vérifications que la bibliothèque ne fera pas à votre place : la réponse est-elle arrivée à temps, le serveur a-t-il dit oui, et le texte est-il correctement décodé. Écrivez donc une seule fonction de requête qui fixe les délais, vérifie le statut et décode délibérément, et faites-en le seul endroit du projet qui appelle send.

Pour l'objet, partez par défaut sur MSXML2.ServerXMLHTTP.6.0 : pas de cache périmé, de vrais délais d'expiration. Ne passez à MSXML2.XMLHTTP.6.0 que si vos utilisateurs sont derrière un proxy que vous ne pouvez pas configurer, et ajoutez alors l'en-tête If-Modified-Since. Et si tout ce qu'il vous faut, c'est récupérer le même tableau à chaque actualisation, sans aucune logique autour, envisagez Données > À partir du web de Power Query avant d'écrire la moindre ligne de VBA.

Comment ExcelMaster aide

Les macros d'API échouent sur les machines des autres : un proxy que vous n'avez jamais vu, un Windows réglé dans une autre langue, un serveur qui répond lentement le lundi matin.

ExcelMaster écrit le code de requête pour le classeur qu'il a sous les yeux, vérifie le statut et le résultat décodé avant que quoi que ce soit n'atteigne vos cellules, et vous montre ce que l'API a réellement renvoyé quand cela ne correspond pas à ce que vous attendiez.

Questions fréquentes

Comment faire une requête HTTP GET en Excel VBA ?

Créez MSXML2.ServerXMLHTTP.6.0 avec CreateObject, appelez .Open "GET", url, False, puis .send, vérifiez .Status et lisez .responseText. Aucune référence n'est nécessaire.

Quelle est la différence entre XMLHTTP et ServerXMLHTTP ?

XMLHTTP utilise WinINet : il suit les paramètres proxy de l'utilisateur, peut mettre en cache les réponses GET et n'a aucun réglage de délai d'expiration. ServerXMLHTTP utilise WinHTTP : pas de cache et de vrais délais d'expiration, mais vous devrez peut-être définir un proxy avec setProxy.

Pourquoi ma requête VBA renvoie-t-elle d'anciennes données ?

MSXML2.XMLHTTP peut répondre à un GET répété depuis le cache WinINet. Passez à ServerXMLHTTP, ou envoyez une ancienne date If-Modified-Since avec setRequestHeader avant send.

Pourquoi VBA ne déclenche-t-il pas d'erreur sur un 404 ou un 500 ?

Parce que la requête elle-même a réussi : le serveur a répondu. Seuls les échecs de transport, comme l'absence de réseau ou un délai dépassé, déclenchent une erreur d'exécution. Vérifiez .Status après chaque send.

Pourquoi le texte de la réponse est-il illisible ?

Le serveur a envoyé du texte dans un encodage qu'il n'a pas déclaré, et responseText a supposé de l'UTF-8. Décodez .responseBody avec ADODB.Stream et le bon Charset.

Testé dans

Testé dans : Excel 365 (Windows 11), VBA 7.1 — vérifié le 05/10/2026.

Guides connexes : VBA JSON · Web scraping en VBA · VBA CreateObject · Gestion des erreurs en VBA · VBA On Error · VBA Str · VBA Environ · Barre d'état en VBA (StatusBar)