TL;DR — Ein HTTP-Request aus VBA gibt Ihnen genau zwei Dinge zurück: einen Statuscode und einen Block Bytes. Er sagt Ihnen nicht, ob die Daten aktuell sind, in welcher Textkodierung die Bytes vorliegen oder ob die Anfrage den Server überhaupt rechtzeitig erreicht hat. Das ist Ihre Aufgabe. Wählen Sie das Objekt nach dem Netzwerk, das es durchqueren muss (
MSXML2.XMLHTTPnutzt die Browsereinstellungen und den Cache des Benutzers,ServerXMLHTTPundWinHttpRequestbieten Timeouts und keinen Cache), prüfen Sie den Status selbst, denn ein 404 oder 500 löst nie einen Fehler aus, und dekodieren Sie die Bytes bewusst, wenn der Server etwas anderes als UTF-8 schickt.
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
Dies ist der erste Artikel einer Reihe darüber, Daten aus dem Web nach Excel zu holen: hier die Anfrage selbst, danach VBA JSON, um die Antwort in eine Struktur zu verwandeln, und VBA Web Scraping für Seiten, die nie dafür gedacht waren, von Code gelesen zu werden. Die Idee der Reihe: Eine Web-Anfrage liefert Ihnen Text, keine Daten. Zwischen dem Server und Ihren Zellen liegen drei Übersetzungen, für die Sie verantwortlich sind: Bytes zu Text, Text zu Struktur, Struktur zu Raster. Fast jeder Fehler passiert an einer dieser Grenzen, nicht in der Anfrage.
Was Sie lernen
- Das mentale Modell: ein Status und ein paar Bytes
- Drei Objekte, drei Netzwerk-Stacks
- Zwei Arten von Fehlern: solche, die auslösen, und Status, die es nicht tun
- Warum ein GET die Daten von gestern liefert
- Timeouts, und warum eine Anfrage Excel einfrieren kann
- Verstümmelte Umlaute: die Bytes selbst dekodieren
- Einen POST mit JSON-Body senden
- Query-Strings und API-Schlüssel
Das mentale Modell: ein Status und ein paar Bytes
Wenn Sie http.send aufrufen, übergibt VBA die Anfrage an eine Windows-Netzwerkbibliothek und wartet. Was
zurückkommt, ist überschaubar: eine Zahl (Status, etwa 200 oder 404), einige Header und der Body als Bytes.
responseText ist nicht der Body; es ist der Body, bereits anhand einer Vermutung in Text dekodiert.
responseBody sind die rohen Bytes.
Behalten Sie dieses Bild im Kopf, und die seltsamen Fehler sind nicht mehr seltsam. Veraltete Daten bedeuten, dass eine Schicht zwischen Ihnen und dem Server an seiner Stelle geantwortet hat. Verstümmelter Text bedeutet, dass die Vermutung beim Dekodieren falsch war. Ein Makro, das „gestern noch lief“ und jetzt hängt, bedeutet, dass das Netzwerk langsamer geworden ist und niemand der Anfrage gesagt hat, wann sie aufgeben soll.
Drei Objekte, drei Netzwerk-Stacks
Die drei Objekte, die man aus Foren kopiert, wirken austauschbar. Sie sind es nicht: Jedes sitzt auf einem anderen Windows-Netzwerk-Stack, und der Stack entscheidet, wie sich die Anfrage auf dem Rechner Ihres Benutzers verhält.
| Objekt | Netzwerk-Stack | Cache | Proxy und Anmeldung | Timeouts |
|---|---|---|---|---|
MSXML2.XMLHTTP.6.0 |
WinINet (der Stack hinter den alten Browsereinstellungen) | ja, GET-Antworten können zwischengespeichert werden | nutzt automatisch die Windows-Interneteinstellungen des Benutzers | keine Methode setTimeouts |
MSXML2.ServerXMLHTTP.6.0 |
WinHTTP | nein | eigene Proxy-Einstellung von WinHTTP, oft leer; mit setProxy setzen |
setTimeouts |
WinHttp.WinHttpRequest.5.1 |
WinHTTP | nein | wie oben; SetProxy |
SetTimeouts, dazu Optionen wie TLS-Versionen |
Diese Tabelle erklärt das häufigste Support-Ticket: „Auf meinem Laptop läuft es, im Büro des Kunden nicht.“ Das
Büro leitet den Webverkehr über einen Proxy. XMLHTTP übernimmt diesen Proxy aus den Einstellungen des
Benutzers; ServerXMLHTTP und WinHttpRequest tun das nicht, also bekommen sie keine Verbindung, während der
Browser auf demselben Rechner problemlos funktioniert. Teilen Sie ihnen den Proxy ausdrücklich mit:
http.setProxy 2, "proxy.company.local:8080" ' 2 = diesen Proxy verwenden
Alle drei werden mit CreateObject erzeugt und brauchen keinen Verweis, also läuft derselbe Code in 32-Bit-
und 64-Bit-Office. Zum späten Binden allgemein siehe VBA CreateObject.
Zwei Arten von Fehlern: solche, die auslösen, und Status, die es nicht tun
Eine Anfrage kann an zwei Stellen scheitern, und VBA meldet das über zwei verschiedene Kanäle.
Transportfehler lösen einen Laufzeitfehler aus. Kein Netzwerk, ein Servername, der sich nicht auflösen
lässt, ein Timeout, ein fehlgeschlagener TLS-Handshake: send bricht mit einem Laufzeitfehler ab, meist einer
großen negativen Zahl wie -2147012889 (der Servername konnte nicht aufgelöst werden) oder -2147012894 (das
Zeitlimit für den Vorgang wurde überschritten). Fangen Sie diese mit On Error ab.
HTTP-Fehler lösen gar nichts aus. Ein 404, ein 401, weil der Schlüssel abgelaufen ist, ein 500, weil der
Server kaputt ist: send kehrt normal zurück, Status enthält den Code, und responseText enthält eine
Fehlerseite oder ein Fehler-JSON. Code, der die Statusprüfung auslässt, schreibt munter „Unauthorized“ oder eine
Seite HTML in Ihr Blatt.
Jede Anfrage braucht also beide Prüfungen. Packen Sie sie in eine Funktion und rufen Sie send nirgendwo
sonst auf:
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 ' Transportfehler werden hier ausgeloest
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
Jetzt kommen beide Fehlerarten als Laufzeitfehler mit lesbarer Meldung an, und der Aufrufer behandelt sie an einer Stelle. Das Muster drumherum finden Sie unter VBA-Fehlerbehandlung.
Warum ein GET die Daten von gestern liefert
MSXML2.XMLHTTP läuft über WinINet, und WinINet führt einen Cache. Wenn die Antwort des Servers das
Zwischenspeichern erlaubt oder nichts dazu sagt, kann ein wiederholter GET auf dieselbe URL aus dem Cache
beantwortet werden, ohne den Server überhaupt zu erreichen. Das Makro läuft, der Status ist 200, und die
Wechselkurse sind von heute Morgen.
Drei Auswege, vom besten zum schlechtesten:
- Verwenden Sie
ServerXMLHTTPoderWinHttpRequest, die keinen Cache haben. - Wenn Sie bei
XMLHTTPbleiben müssen (wegen seiner Proxy-Behandlung), zwingen Sie den Cache, erneut beim Server nachzufragen, indem Sie ein weit zurückliegendes Datum senden:http.setRequestHeader "If-Modified-Since", "Sat, 01 Jan 2000 00:00:00 GMT". - Machen Sie jede URL mit einem Wegwerf-Parameter eindeutig, etwa
"&_=" & Format(Now, "yyyymmddhhnnss"). Das funktioniert, aber manche APIs lehnen Parameter ab, die sie nicht kennen.
Das verräterische Zeichen des Caches ist eine Anfrage, die sofort zurückkommt, mit genau denselben Daten wie beim letzten Mal, während dieselbe URL im Browser etwas Neueres zeigt.
Timeouts, und warum eine Anfrage Excel einfrieren kann
Das False in http.Open "GET", url, False macht die Anfrage synchron: VBA wartet in dieser Zeile, bis die
Antwort da ist. Während es wartet, kann Excel weder neu zeichnen noch reagieren, und Windows kennzeichnet das
Fenster mit Keine Rückmeldung. Eine langsame API wird zu einem eingefrorenen Excel; eine tote API kann es ohne
Timeout sehr lange einfrieren.
setTimeouts nimmt vier Werte in Millisekunden: Namensauflösung, Verbindungsaufbau, Senden und Empfangen.
Setzen Sie sie auf Werte, die zur API passen. 5000, 5000, 10000, 30000 bedeutet: Aufgeben, wenn der Server
nicht innerhalb von fünf Sekunden gefunden oder erreicht werden kann oder nicht innerhalb von dreißig Sekunden
geantwortet hat. XMLHTTP hat keine solche Methode — ein Grund mehr, für alles, worauf ein Benutzer wartet,
ServerXMLHTTP zu bevorzugen.
Bei einer Schleife über viele Anfragen aktualisieren Sie zwischen den Aufrufen die
Statusleiste, damit der Benutzer den Fortschritt sieht. Asynchrone Anfragen gibt es
(True als drittes Argument), aber sie aus VBA abzufragen braucht eine DoEvents-Schleife und bringt mehr
Fehlerquellen hinzu, als es beseitigt; halten Sie Anfragen synchron und kurz.
Verstümmelte Umlaute: die Bytes selbst dekodieren
responseText dekodiert die Bytes mit dem Zeichensatz, den der Server in seinem Content-Type-Header angibt,
und nimmt UTF-8 an, wenn der Server keinen angibt. Die meisten modernen APIs senden UTF-8 und sagen das auch,
und alles funktioniert. Ein älterer Server, der Windows-1252, ISO-8859-1 oder Shift_JIS sendet, ohne es zu
sagen, kommt als München oder als Fragezeichen und Kästchen an.
Wenn das passiert, lassen Sie responseText links liegen, nehmen die rohen Bytes und dekodieren sie mit dem
richtigen Zeichensatz:
Function BytesToText(ByVal bytes As Variant, ByVal charset As String) As String
With CreateObject("ADODB.Stream")
.Type = 1 ' binaer
.Open
.Write bytes
.Position = 0
.Type = 2 ' Text
.Charset = charset ' "windows-1252", "iso-8859-1", "shift_jis"
BytesToText = .ReadText
.Close
End With
End Function
body = BytesToText(http.responseBody, "windows-1252")
Eine Falle beim Debuggen: Beurteilen Sie den Text nicht im Direktfenster. Der VBA-Editor kann nur Zeichen aus
der Windows-Systemcodepage anzeigen, also erscheint korrekter japanischer Text auf Ihrem deutschen Windows als
???, und korrekter deutscher Text kann auf einem japanischen Windows falsch erscheinen. Schreiben Sie den
String in eine Zelle; die Zelle zeigt die Wahrheit.
Einen POST mit JSON-Body senden
Ein POST ist derselbe Aufruf mit einer Methode, einem Content-Type und einem Body:
http.Open "POST", "https://api.example.com/orders", False
http.setRequestHeader "Content-Type", "application/json"
http.send "{""sku"":""A-100"",""qty"":3}"
Die Falle ist, diesen Body zu bauen, indem man Zahlen in einen String klebt. VBA wandelt eine Zahl mit den
Windows-Regionaleinstellungen in Text um, also erzeugt "{""price"":" & 3.5 & "}" auf Ihrem deutschen
Windows (ebenso auf einem französischen oder spanischen) {"price":3,5}, und das ist kein gültiges JSON.
Dasselbe Makro läuft auf einem englischen Rechner und scheitert bei Ihnen. Wandeln Sie Zahlen entweder mit
Trim$(Str$(x)) um, das immer einen Punkt verwendet (siehe VBA Str), oder besser, bauen Sie
den Body mit einer JSON-Bibliothek, wie in VBA JSON gezeigt.
Query-Strings und API-Schlüssel
Werte in einer URL müssen prozentkodiert sein: Ein Leerzeichen, ein kaufmännisches Und oder ein Umlaut in einem
Query-Parameter macht die Anfrage kaputt oder verändert sie stillschweigend. Excel 2013 und neuer haben dafür
eine Funktion, und sie kodiert Buchstaben mit Akzent oder Umlaut als UTF-8, sodass aus Zürich dann
Z%C3%BCrich wird:
url = "https://api.example.com/search?city=" & _
Application.WorksheetFunction.EncodeURL(Range("B2").Value)
' B2 = Zurich & Geneva -> city=Zurich%20%26%20Geneva
API-Schlüssel gehören in einen Header, meist http.setRequestHeader "Authorization", "Bearer " & apiKey. Sie
gehören nicht in den Code. Jeder, der die Arbeitsmappe hat, kann den VBA-Editor öffnen, und ein Kennwort für
das VBA-Projekt ist kein echter Schutz. Lesen Sie den Schlüssel zur Laufzeit aus einer Datei im Benutzerprofil
oder aus einer Umgebungsvariablen mit Environ, damit sich die Arbeitsmappe weitergeben
lässt, ohne den Schlüssel weiterzugeben.
Die Abwägung: eine Request-Funktion, drei Prüfungen
Die meisten kaputten HTTP-Makros liegen bei HTTP gar nicht falsch. Sie lassen eine der drei Prüfungen aus, die
die Bibliothek nicht für Sie erledigt: Kam es rechtzeitig an, hat der Server Ja gesagt, und ist der Text
richtig dekodiert? Schreiben Sie also eine Request-Funktion, die Timeouts setzt, den Status prüft und bewusst
dekodiert, und machen Sie sie zur einzigen Stelle im Projekt, die send aufruft.
Als Objekt nehmen Sie standardmäßig MSXML2.ServerXMLHTTP.6.0: kein veralteter Cache, echte Timeouts.
Wechseln Sie nur zu MSXML2.XMLHTTP.6.0, wenn Ihre Benutzer hinter einem Proxy sitzen, den Sie nicht
konfigurieren können, und ergänzen Sie dann den Header If-Modified-Since. Und wenn Sie nur bei jeder
Aktualisierung dieselbe Tabelle abholen wollen, ohne Logik drumherum, ziehen Sie Power Query mit
Daten > Aus dem Web in Betracht, bevor Sie überhaupt VBA schreiben.
Wie ExcelMaster hilft
API-Makros scheitern auf den Rechnern anderer Leute: an einem Proxy, den Sie nie gesehen haben, an einem Windows in einer anderen Sprache, an einem Server, der montagmorgens langsam antwortet.
ExcelMaster schreibt den Request-Code für die Arbeitsmappe, die vor ihm liegt, prüft den Status und das dekodierte Ergebnis, bevor irgendetwas Ihre Zellen erreicht, und zeigt Ihnen, was die API tatsächlich zurückgegeben hat, wenn es nicht Ihren Erwartungen entspricht.
Häufig gestellte Fragen
Wie sende ich in Excel VBA einen HTTP-GET-Request?
Erzeugen Sie MSXML2.ServerXMLHTTP.6.0 mit CreateObject, rufen Sie .Open "GET", url, False und dann
.send auf, prüfen Sie .Status und lesen Sie .responseText. Ein Verweis ist nicht nötig.
Was ist der Unterschied zwischen XMLHTTP und ServerXMLHTTP?
XMLHTTP nutzt WinINet: Es folgt den Proxy-Einstellungen des Benutzers, kann GET-Antworten zwischenspeichern
und hat keine Timeout-Einstellung. ServerXMLHTTP nutzt WinHTTP: kein Cache und echte Timeouts, aber Sie müssen
eventuell mit setProxy einen Proxy setzen.
Warum liefert mein VBA-Request alte Daten?
MSXML2.XMLHTTP kann einen wiederholten GET aus dem WinINet-Cache beantworten. Wechseln Sie zu
ServerXMLHTTP, oder senden Sie vor send mit setRequestHeader ein altes If-Modified-Since-Datum.
Warum löst VBA bei einem 404 oder 500 keinen Fehler aus?
Weil die Anfrage selbst erfolgreich war; der Server hat geantwortet. Nur Transportfehler wie fehlendes Netzwerk
oder ein Timeout lösen einen Laufzeitfehler aus. Prüfen Sie .Status nach jedem send.
Warum ist der Antworttext verstümmelt?
Der Server hat Text in einer Kodierung gesendet, die er nicht angegeben hat, und responseText hat UTF-8
angenommen. Dekodieren Sie .responseBody mit ADODB.Stream und dem richtigen Charset.
Getestet in
Getestet in: Excel 365 (Windows 11), VBA 7.1 — zuletzt geprüft am 05.10.2026.
Verwandte Anleitungen: VBA JSON · VBA Web Scraping · VBA CreateObject · VBA-Fehlerbehandlung · VBA On Error · VBA Str · VBA Environ · VBA StatusBar
