TL;DR — VBA の HTTP リクエストが返してくれるのは、ステータスコード と バイト列 のちょうど二つだけです。データが新しいかどうか、バイト列がどの文字コードなのか、そもそもリクエストが時間内にサーバーへ届いたのかは教えてくれません。それはあなたの仕事です。オブジェクトは通過するネットワークに合わせて選び(
MSXML2.XMLHTTPはユーザーのブラウザー設定とキャッシュを使い、ServerXMLHTTPとWinHttpRequestはタイムアウトを設定できてキャッシュを持ちません)、404 や 500 は決してエラーにならないので ステータスは自分で確認し、サーバーが 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
この記事は、Web のデータを Excel に取り込む シリーズの第一回です。ここではリクエストそのものを扱い、続いて VBA の JSON 解析 でレスポンスを構造に変え、VBA の Web スクレイピング でコードに読まれることを想定していないページを扱います。シリーズを貫く考え方は、Web リクエストが渡してくれるのはデータではなくテキストだ ということです。サーバーとセルの間には、あなたが責任を持つ三つの変換があります。バイト列からテキストへ、テキストから構造へ、構造からグリッドへ。失敗のほとんどはリクエストの中ではなく、この境界のどこかで起きます。
この記事で学べること
- 考え方の軸:ステータスとバイト列
- 三つのオブジェクト、三つのネットワークスタック
- 2 種類の失敗:エラーになるものと、ならないもの
- GET が昨日のデータを返す理由
- タイムアウトと、リクエストで Excel が固まる理由
- アクセント付き文字の文字化け:バイト列を自分でデコードする
- JSON 本文の POST を送る
- クエリ文字列と API キー
考え方の軸:ステータスとバイト列
http.send を呼ぶと、VBA はリクエストを Windows のネットワークライブラリに渡して待ちます。返ってくるものはわずかです。数値(Status。200 や 404 など)、いくつかのヘッダー、そしてバイト列としての本文です。responseText は本文そのものではありません。推測ですでにテキストへデコードされた 本文です。responseBody が生のバイト列です。
この図を頭に置いておけば、奇妙な失敗は奇妙ではなくなります。データが古いのは、あなたとサーバーの間にある層が代わりに答えたからです。文字化けは、デコードの推測が外れたからです。「昨日は動いた」マクロが今日は固まるのは、ネットワークが遅くなり、それでも諦めるようリクエストに誰も伝えていなかったからです。
三つのオブジェクト、三つのネットワークスタック
フォーラムから貼り付けられる三つのオブジェクトは、どれも同じように見えます。実際は違います。それぞれが Windows の別のネットワークスタックの上に乗っていて、そのスタックが、ユーザーのマシンでリクエストがどう振る舞うかを決めます。
| オブジェクト | ネットワークスタック | キャッシュ | プロキシとサインイン | タイムアウト |
|---|---|---|---|---|
MSXML2.XMLHTTP.6.0 |
WinINet(旧来のブラウザー設定を支えるスタック) | あり。GET のレスポンスはキャッシュされうる | ユーザーの Windows のインターネット設定を自動で使う | setTimeouts メソッドなし |
MSXML2.ServerXMLHTTP.6.0 |
WinHTTP | なし | WinHTTP 独自のプロキシ設定(空のことが多い)。setProxy で設定する |
setTimeouts |
WinHttp.WinHttpRequest.5.1 |
WinHTTP | なし | 上と同じ。SetProxy |
SetTimeouts。TLS バージョンなどのオプションもあり |
この表で、いちばんよくある問い合わせ「自分のノート PC では動くのに、顧客のオフィスでは失敗する」が説明できます。そのオフィスでは Web へのアクセスがプロキシ経由になっています。XMLHTTP はそのプロキシをユーザーの設定から拾いますが、ServerXMLHTTP と WinHttpRequest は拾いません。そのため、同じマシンのブラウザーは問題なく動くのに、これらは接続できないのです。プロキシは明示的に教えてください。
http.setProxy 2, "proxy.company.local:8080" ' 2 = このプロキシを使う
三つとも CreateObject で作成でき、参照設定は不要なので、同じコードが 32 ビット版でも 64 ビット版の Office でも動きます。遅延バインディング全般については VBA の CreateObject を参照してください。
2 種類の失敗:エラーになるものと、ならないもの
リクエストが失敗する場所は二つあり、VBA はそれぞれを別の経路で知らせます。
通信レベルの失敗は実行時エラーになります。 ネットワークがない、サーバー名が解決できない、タイムアウトした、TLS のハンドシェイクに失敗した。こうした場合、send は実行時エラーで止まります。エラー番号はたいてい大きな負の数で、-2147012889(サーバー名を解決できなかった)や -2147012894(操作がタイムアウトした)などです。これらは On Error で捕まえます。
HTTP レベルの失敗は何も発生させません。 404、キーの期限切れによる 401、サーバーが壊れたことによる 500。いずれも send は普通に戻り、Status にコードが入り、responseText にはエラーページかエラーの JSON が入ります。ステータスの確認を飛ばしたコードは、「Unauthorized」やページ一枚分の HTML を平然とシートに書き込みます。
つまり、どのリクエストにも両方の確認が必要です。それを一つの関数にまとめ、ほかの場所では決して send を呼ばないでください。
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 ' 通信エラーはここで発生する
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
これで、どちらの失敗も読みやすいメッセージ付きの実行時エラーとして届き、呼び出し側は一か所で処理できます。その周りの書き方は VBA のエラー処理 を参照してください。
GET が昨日のデータを返す理由
MSXML2.XMLHTTP は WinINet を経由し、WinINet はキャッシュを持っています。サーバーのレスポンスがキャッシュを許可している場合、あるいはキャッシュについて何も言っていない場合、同じ URL への GET を繰り返すと、サーバーにまったく届かないままキャッシュから答えが返ることがあります。マクロは動き、ステータスは 200、そして為替レートは今朝のものです。
抜け道は三つあります。良いものから順に並べます。
- キャッシュを持たない
ServerXMLHTTPかWinHttpRequestを使う。 - (プロキシの扱いのために)どうしても
XMLHTTPを使い続けるなら、ずっと昔の日付を送って、キャッシュにサーバーへ問い合わせ直させる:http.setRequestHeader "If-Modified-Since", "Sat, 01 Jan 2000 00:00:00 GMT"。 "&_=" & Format(Now, "yyyymmddhhnnss")のような使い捨てのパラメーターで URL を毎回一意にする。これでも動きますが、知らないパラメーターを拒否する API もあります。
キャッシュの見分け方は、リクエストが一瞬で返り、前回とまったく同じデータなのに、同じ URL をブラウザーで開くともっと新しいものが表示される、という状態です。
タイムアウトと、リクエストで Excel が固まる理由
http.Open "GET", url, False の False は、リクエストを 同期 にします。VBA はレスポンスが届くまでその行で待ちます。待っている間、Excel は再描画も応答もできず、Windows はウィンドウに 応答なし と表示します。遅い API は固まった Excel になり、止まった API はタイムアウトがなければ長時間 Excel を固めることがあります。
setTimeouts はミリ秒単位の四つの値を取ります。名前解決、接続、送信、受信です。API に合った数値を設定してください。5000, 5000, 10000, 30000 は、5 秒以内にサーバーが見つからない、または到達できない場合、あるいは 30 秒以内に応答がない場合に諦める、という意味です。XMLHTTP にはこのメソッドがありません。ユーザーが待つ処理には ServerXMLHTTP を選ぶべき理由がまた一つ増えます。
多数のリクエストをループで送るときは、呼び出しの合間に ステータスバー を更新して、ユーザーに進捗が見えるようにしてください。非同期リクエスト(第 3 引数に True)もありますが、VBA からそれをポーリングするには DoEvents のループが必要で、減らす失敗より増やす失敗のほうが多くなります。リクエストは同期のまま、短く保ってください。
アクセント付き文字の文字化け:バイト列を自分でデコードする
responseText は、サーバーが Content-Type ヘッダーで宣言した文字セットを使ってバイト列をデコードし、何も宣言がなければ UTF-8 とみなします。最近の API の多くは UTF-8 で送り、そう宣言しているので、すべてうまくいきます。古いサーバーが Windows-1252、ISO-8859-1、Shift_JIS のテキストを 宣言なしで 送ってくると、München のような文字や、疑問符や四角として届きます。
そうなったら responseText は使わず、生のバイト列を受け取って正しい文字セットでデコードしてください。
Function BytesToText(ByVal bytes As Variant, ByVal charset As String) As String
With CreateObject("ADODB.Stream")
.Type = 1 ' バイナリ
.Open
.Write bytes
.Position = 0
.Type = 2 ' テキスト
.Charset = charset ' "windows-1252", "iso-8859-1", "shift_jis"
BytesToText = .ReadText
.Close
End With
End Function
body = BytesToText(http.responseBody, "windows-1252")
デバッグ中の落とし穴が一つあります。イミディエイトウィンドウでテキストを判断しないでください。VBA エディターは Windows のシステムコードページの文字しか表示できないので、正しい日本語のテキストもドイツ語の Windows では ??? と出力され、正しいドイツ語のテキストも日本語の Windows では崩れて出力されることがあります。文字列はセルに書き込んでください。セルが本当の姿を見せてくれます。
JSON 本文の POST を送る
POST は、メソッド、コンテンツタイプ、本文を指定するだけの同じ呼び出しです。
http.Open "POST", "https://api.example.com/orders", False
http.setRequestHeader "Content-Type", "application/json"
http.send "{""sku"":""A-100"",""qty"":3}"
落とし穴は、数値を文字列に連結して本文を組み立てることです。VBA は数値をテキストに変換するときに Windows の地域設定 を使います。そのため、ドイツ、フランス、スペインの PC では "{""price"":" & 3.5 & "}" が {"price":3,5} になり、これは正しい JSON ではありません。あなたの PC やニューヨークでは動く同じマクロが、ミュンヘンの同僚の PC で実行すると失敗するのです。数値は常にピリオドを使う Trim$(Str$(x)) で変換するか(VBA の Str を参照)、より良い方法として、VBA の JSON 解析 で紹介している JSON ライブラリで本文を組み立ててください。
クエリ文字列と API キー
URL の中の値はパーセントエンコードしなければなりません。クエリパラメーターの中のスペース、アンパサンド、ウムラウトは、リクエストを壊すか、黙って内容を変えてしまいます。Excel 2013 以降にはそのための関数があり、アクセント付きの文字を UTF-8 でエンコードするので、Zürich は Z%C3%BCrich になります。
url = "https://api.example.com/search?city=" & _
Application.WorksheetFunction.EncodeURL(Range("B2").Value)
' B2 = Zurich & Geneva の場合 -> city=Zurich%20%26%20Geneva
API キーはヘッダーに入れます。たいていは http.setRequestHeader "Authorization", "Bearer " & apiKey です。コードの中には書きません。ブックを持っている人なら誰でも VBA エディターを開けますし、VBA プロジェクトのパスワードは本当の保護にはなりません。キーは実行時に、ユーザーのプロファイル内のファイルから、または Environ で環境変数から読み込んでください。そうすれば、キーを共有せずにブックを共有できます。
判断の分かれ目:リクエスト関数は一つ、確認は三つ
壊れた HTTP マクロのほとんどは、HTTP について間違っているわけではありません。ライブラリが代わりにやってくれない三つの確認のどれかを飛ばしているのです。時間内に届いたか、サーバーは「はい」と言ったか、テキストは正しくデコードされたか。 だから、タイムアウトを設定し、ステータスを確認し、意図してデコードするリクエスト関数を一つ書き、それをプロジェクト内で send を呼ぶ唯一の場所にしてください。
オブジェクトは MSXML2.ServerXMLHTTP.6.0 を既定にしてください。古いキャッシュがなく、本物のタイムアウトがあります。MSXML2.XMLHTTP.6.0 に切り替えるのは、ユーザーが設定できないプロキシの内側にいるときだけにし、その場合は If-Modified-Since ヘッダーを追加してください。そして、更新のたびに同じ表を取り込むだけで、周りにロジックが何もないなら、VBA を書く前に Power Query の Web から を検討してください。
ExcelMaster の活用
API マクロは他人のマシンで失敗します。見たこともないプロキシ、別の言語に設定された Windows、月曜の朝だけ応答が遅いサーバー。
ExcelMaster は目の前のブックに合わせてリクエストのコードを書き、何かがセルに届く前にステータスとデコード結果を確認し、期待したものと違うときは API が実際に何を返したのかを見せてくれます。
よくある質問
Excel VBA で HTTP の GET リクエストを送るには?
CreateObject で MSXML2.ServerXMLHTTP.6.0 を作成し、.Open "GET", url, False、続いて .send を呼び、.Status を確認してから .responseText を読みます。参照設定は不要です。
XMLHTTP と ServerXMLHTTP の違いは何ですか?
XMLHTTP は WinINet を使います。ユーザーのプロキシ設定に従い、GET のレスポンスをキャッシュすることがあり、タイムアウトの設定はありません。ServerXMLHTTP は WinHTTP を使います。キャッシュがなく本物のタイムアウトがありますが、setProxy でプロキシを設定する必要があるかもしれません。
VBA のリクエストが古いデータを返すのはなぜですか?
MSXML2.XMLHTTP は、繰り返された GET に WinINet のキャッシュから答えることがあります。ServerXMLHTTP に切り替えるか、send の前に setRequestHeader で古い日付の If-Modified-Since を送ってください。
404 や 500 で VBA がエラーにならないのはなぜですか?
リクエスト自体は成功している、つまりサーバーが答えているからです。実行時エラーになるのは、ネットワークがない、タイムアウトしたといった通信レベルの失敗だけです。send のたびに .Status を確認してください。
レスポンスのテキストが文字化けするのはなぜですか?
サーバーが宣言していない文字コードでテキストを送り、responseText が UTF-8 とみなしたからです。.responseBody を ADODB.Stream と正しい Charset でデコードしてください。
検証環境
検証環境: Excel 365 (Windows 11)、VBA 7.1 — 最終確認 2026-10-05。
関連ガイド: VBA の JSON 解析 · VBA の Web スクレイピング · VBA の CreateObject · VBA のエラー処理 · VBA の On Error · VBA の Str 関数 · VBA の Environ 関数 · VBA の StatusBar
