Example
curl -X POST https://api.example.com/v1/events \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: YOUR_KEY' \
-d '{"type": "signup", "user_id": 42}' \
-m 5
package main
import (
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
)
func main() {
client := &http.Client{
Timeout: 5 * time.Second,
}
var data = strings.NewReader(`{"type": "signup", "user_id": 42}`)
req, err := http.NewRequest("POST", "https://api.example.com/v1/events", data)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", "YOUR_KEY")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Printf("%s\n", bodyText)
}
curl options in net/http
| curl | Go |
|---|---|
-X | First argument of http.NewRequest |
-H | req.Header.Set(…), or Add for repeated headers |
-d | strings.NewReader(body) as the request body |
-F | mime/multipart writer, with writer.FormDataContentType() as Content-Type |
-T file, --data-binary @file | os.Open(path) as the body |
-u user:pass | req.SetBasicAuth(user, pass) |
-k | &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} |
-x proxy | Transport.Proxy = http.ProxyURL(u) (or http.ProxyFromEnvironment) |
-m 5 | http.Client{Timeout: 5 * time.Second} |
-L | Default (up to 10 redirects; change with CheckRedirect) |
--compressed | Default (the transport requests and decodes gzip) |
Create one http.Client and reuse it for all requests: it holds the connection pool. A new client per
request works but opens a new connection each time. For the response body, generate Go structs with
JSON to Go.
Frequently asked questions
Does the generated Go code compile?
Yes. It is a complete main package that uses only the standard library, and our tests compile it with go vet and run it against a local server to check the request matches curl’s. Paste it into main.go and run go run main.go.
Why use http.NewRequest instead of http.Post?
http.Get and http.Post can’t set custom headers, and they use http.DefaultClient, which has no timeout. NewRequest plus client.Do works for every method, header and body, so the same pattern covers all curl commands.
Does Go’s HTTP client have a timeout?
Not by default: http.DefaultClient and &http.Client{} wait forever. The converter sets Timeout when curl had -m. In real code, always set one, or pass a context with a deadline using http.NewRequestWithContext.
Why does the code close resp.Body?
If the body isn’t read to the end and closed, the underlying TCP connection can’t be reused and leaks. defer resp.Body.Close() right after the error check is the standard idiom, even if you don’t need the body.
How do I decode the JSON response into a struct?
Replace io.ReadAll with json.NewDecoder(resp.Body).Decode(&result), where result is a struct matching the response. Paste a sample response into the JSON to Go converter to generate the struct with the right json tags.