Go の http.Client で外部 API を叩く:Get・Post・ヘッダー設定の基本

Go で外部の API を呼び出すには、標準ライブラリの net/http パッケージに含まれる http.Client を使います。GET・POST・ヘッダー設定まで、すべて標準ライブラリだけで対応可能です。

GET リクエストを送る

もっとも基本的な GET リクエストは http.Get で送れます。

package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	resp, err := http.Get("https://httpbin.org/get")
	if err != nil {
		fmt.Println("エラー:", err)
		return
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("読み取りエラー:", err)
		return
	}

	fmt.Println(string(body))
}

http.Get はレスポンスとエラーを返します。レスポンスのボディは io.ReadAll でバイト列として読み取れますが、必ず defer resp.Body.Close() でクローズする必要があります。クローズしないとコネクションがリークし、長時間動くプログラムではリソースが枯渇する原因になります。

http.Get でリクエスト送信

resp.Body をクローズ予約(defer)

io.ReadAll でボディ読み取り

取得したデータを処理

POST リクエストを送る

JSON を送信する POST リクエストもシンプルに書けます。

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	data := map[string]string{
		"name":  "Alice",
		"email": "alice@example.com",
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		fmt.Println("JSON 変換エラー:", err)
		return
	}

	resp, err := http.Post(
		"https://httpbin.org/post",
		"application/json",
		bytes.NewBuffer(jsonData),
	)
	if err != nil {
		fmt.Println("リクエストエラー:", err)
		return
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("読み取りエラー:", err)
		return
	}

	fmt.Println(string(body))
}

http.Post の第 2 引数には Content-Type を指定します。JSON を送るなら "application/json" が定番です。リクエストボディは bytes.NewBufferio.Reader に変換して渡します。

ポイントは json.Marshal でマップや構造体を JSON バイト列に変換する部分です。構造体を使う場合はフィールドタグでキー名を制御できます。

type User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

http.NewRequest でヘッダーを設定する

http.Gethttp.Post は便利ですが、カスタムヘッダーを追加したい場合は http.NewRequest を使います。API キーの送信や認証トークンの付与が必要なケースでは、こちらが必須になります。

req, err := http.NewRequest("GET", "https://api.example.com/data", nil)
if err != nil {
	fmt.Println("リクエスト作成エラー:", err)
	return
}

req.Header.Set("Authorization", "Bearer your-token-here")
req.Header.Set("Accept", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
	fmt.Println("リクエストエラー:", err)
	return
}
defer resp.Body.Close()

http.NewRequest でリクエストオブジェクトを作り、Header.Set でヘッダーを追加してから client.Do で送信する流れです。

http.Get / http.Post

手軽だがヘッダーのカスタマイズができない。シンプルなリクエスト向き。

http.NewRequest + client.Do

柔軟にヘッダーやメソッドを設定可能。認証付き API や複雑なリクエストに対応。

タイムアウトを設定する

デフォルトの http.Client にはタイムアウトが設定されていません。外部 API が応答しない場合、プログラムが永遠に待ち続ける危険があります。

client := &http.Client{
	Timeout: 10 * time.Second,
}

resp, err := client.Get("https://httpbin.org/delay/5")
if err != nil {
	fmt.Println("タイムアウトまたはエラー:", err)
	return
}
defer resp.Body.Close()

Timeout フィールドに time.Duration を設定するだけで、指定時間を超えたリクエストは自動的にキャンセルされます。本番コードでは必ず設定しておきましょう。10 秒程度が一般的な目安ですが、API の特性に応じて調整してください。

ステータスコードを確認する

API を叩いたら、レスポンスのステータスコードを確認する習慣をつけましょう。http.Get などが err == nil でも、サーバーが 404 や 500 を返している可能性があります。

resp, err := http.Get("https://httpbin.org/status/404")
if err != nil {
	fmt.Println("通信エラー:", err)
	return
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
	fmt.Printf("想定外のステータス: %d\n", resp.StatusCode)
	return
}

fmt.Println("成功!")

err は通信レベルのエラー(DNS 解決失敗やタイムアウトなど)だけを捕捉します。HTTP レベルのエラーは resp.StatusCode を見て自分で判定する必要がある点に注意してください。