HTTP Clients (net/http)
Go's net/http package lets you call web APIs from your program: send GET and POST requests, set headers and timeouts, read the response body, and decode JSON straight into your structs — all from the standard library.
Learn HTTP Clients (net/http) in our free Go course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ The simplest request: http.Get
http.Get(url) performs a GET and returns a *http.Response and an error . Check the error, defer closing the body , then read it. We target an httptest.NewServer so the whole thing runs offline with a real, predictable response.
2️⃣ A real client: request, headers, timeout
For anything beyond a toy call, build the request with http.NewRequest so you can choose the method and set headers, and send it through your own &http.Client{Timeout: ...} . A timeout is non-negotiable in production — the default client has none and will happily wait forever on a dead server.
3️⃣ Decoding a JSON response
Most APIs speak JSON. Define a struct with json:"..." tags that match the keys, then decode straight from the response stream with json.NewDecoder(resp.Body).Decode(&v) — no need to read the whole body into memory first.
🎯 Your Turn
Make a GET request and tidy up after it. Fill in the two blanks marked ___ , then run it.
❌ Forgetting resp.Body.Close() — leaks connections under load.
✅ defer resp.Body.Close() immediately after the error check.
❌ Using http.Get with no timeout in production — a hung server blocks forever.
✅ Build a &http.Client{Timeout: ...} or pass a context with a deadline.
❌ Assuming err == nil means success — a 404/500 still returns err == nil .
✅ Also check resp.StatusCode (e.g. reject anything outside 200-299).
❌ Touching resp before the error check — on error resp is nil and you panic.
✅ if err != nil { return } first, always.
No. The round trip succeeded, so err is nil ; the failure shows up only in resp.StatusCode == 500 . Always check both.
Zero — meaning no timeout. That's why http.Get can hang forever. Set your own Timeout or use a context deadline.
GET the JSON from the test server, decode it into the Weather struct, and print Oslo is 14.5C .
Practice quiz
What does http.Get(url) return?
- a string body
- just an error
- a *http.Response and an error
- a *http.Request
Answer: a *http.Response and an error. http.Get performs a GET and returns a *http.Response plus an error to check first.
Why must you call resp.Body.Close()?
- the body is an open network stream that leaks connections if not closed
- to send the request
- to parse JSON
- it is optional and pointless
Answer: the body is an open network stream that leaks connections if not closed. Not closing the body leaks the underlying connection and file descriptors under load.
When should you check err before touching resp?
- never
- only for POST
- only in tests
- always — on error resp is nil and resp.Body would panic
Answer: always — on error resp is nil and resp.Body would panic. On an error resp is nil, so accessing resp.Body.Close() would panic; check err first.
What is the timeout on http.DefaultClient (used by http.Get)?
- 30 seconds
- zero — no timeout
- 5 seconds
- 1 minute
Answer: zero — no timeout. DefaultClient has no timeout, so http.Get can hang forever on a dead server.
How do you set a timeout for real-world requests?
- create &http.Client{Timeout: ...} and call client.Do(req)
- pass it to http.Get
- it is automatic
- use resp.Timeout
Answer: create &http.Client{Timeout: ...} and call client.Do(req). Build your own http.Client with a Timeout (or use a context deadline) and call Do.
Which function builds a request so you can set the method and headers?
- http.Get
- http.Handle
- http.NewRequest
- http.Client
Answer: http.NewRequest. http.NewRequest (or NewRequestWithContext) lets you choose method and set req.Header.
How do you add an Authorization header to a request?
- resp.Header.Set(...)
- req.Header.Set("Authorization", "Bearer ...")
- client.Auth(...)
- http.SetHeader(...)
Answer: req.Header.Set("Authorization", "Bearer ..."). Set fields on req.Header before sending; Set replaces any existing value.
Does an HTTP 404 or 500 status make client.Do return a non-nil error?
- yes, always
- only 500 does
- only 404 does
- no — err is nil; check resp.StatusCode yourself
Answer: no — err is nil; check resp.StatusCode yourself. err is non-nil only for transport problems; a 404/500 is a successful round trip.
What is the memory-efficient way to read a JSON response?
- io.ReadAll then json.Unmarshal
- json.NewDecoder(resp.Body).Decode(&v)
- fmt.Sscanf
- resp.JSON()
Answer: json.NewDecoder(resp.Body).Decode(&v). Decoding straight from the stream avoids buffering the whole payload in memory.
What is httptest.NewServer used for in these examples?
- mocking the client
- compiling the code
- a real local server on a random port so requests run offline
- logging requests
Answer: a real local server on a random port so requests run offline. It starts a real HTTP server on a random localhost port with a deterministic response.