Golang HTTP POST JSON: send a post request with a JSON body in Go

Golang http post json and golang post request with json body using net/http: http.NewRequest with bytes.Reader, Content-Type application/json, json.Marshal, and a minimal server decode with json.Decoder; httptest example you can run locally.

Published

Updated

Read time 3 min read

Reviewed byDeepak Prasad

Golang HTTP POST JSON: send a post request with a JSON body in Go

A golang post request with a JSON body is the usual way to call REST APIs from Go: you create a POST, attach UTF-8 JSON bytes, set Content-Type to application/json, then read the response with io.ReadAll. This page is a compact golang http post example using only net/http and encoding/json—no third-party HTTP client. For wider client and server patterns, see HTTP in Go; for decoding arbitrary JSON into structs or map[string]any, see parse JSON in Go.

Checked with Go 1.24 on 64-bit Linux (Fedora and Ubuntu family).


golang http post json with net/http

The flow for send post request golang code matches most languages: marshal a value to JSON, open an http.Request with a body io.Reader (often bytes.NewReader), set headers, then Client.Do. On the server, json.NewDecoder(r.Body).Decode is the common way to read the golang post json payload without buffering the whole body first.

Self-contained golang http post example (httptest)

The snippet below starts an in-memory server with httptest.NewServer, sends one golang http post with JSON, and prints the status line plus body. Save it as main.go, run go mod init example once in that folder, then go run .—no separate ListenAndServe on a real port.

go
package main

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

type loginRequest struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

type loginResponse struct {
	Message string `json:"message"`
}

func handler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
	defer r.Body.Close()
	var req loginRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	if req.Username == "user1" && req.Password == "pw1" {
		_ = json.NewEncoder(w).Encode(loginResponse{Message: "ok"})
		return
	}
	w.WriteHeader(http.StatusUnauthorized)
	_ = json.NewEncoder(w).Encode(loginResponse{Message: "bad credentials"})
}

func main() {
	srv := httptest.NewServer(http.HandlerFunc(handler))
	defer srv.Close()

	payload, err := json.Marshal(loginRequest{Username: "user1", Password: "pw1"})
	if err != nil {
		log.Fatal(err)
	}
	req, err := http.NewRequest(http.MethodPost, srv.URL, bytes.NewReader(payload))
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)
	fmt.Println(res.StatusCode, string(body))
}
Output

You should see a 200 line and JSON like {"message":"ok"}.

Request body: raw []byte vs json.Marshal

Either shape is valid for golang post json:

  • Fixed literals: build []byte from a Go raw string literal that contains your JSON so you avoid doubling backslashes on quotes.
  • Dynamic data: build a struct or map and json.Marshal(v) so field names and escaping stay correct.

Always pass the body reader to http.NewRequest before you send; if json.Marshal fails, do not call the API with a partial buffer.

Client details people miss

Check the error from http.NewRequest before calling Header.Set—if construction failed, req may be nil. Prefer http.MethodPost over the string "POST" for clarity. For production go post request code, attach a deadline with context.WithTimeout and http.NewRequestWithContext so slow APIs cannot hang your worker forever.


Summary

This golang http post example shows how to perform a golang post request with json body: marshal with encoding/json, post with http.NewRequest and bytes.NewReader, set Content-Type to application/json, and read the reply with io.ReadAll. The server side decodes JSON with json.NewDecoder on r.Body and writes JSON back with json.NewEncoder. Together that covers the intent behind searches like golang post request, golang http post json, send post request golang, and post request golang—without a separate terminal server or deprecated ioutil helpers.


References


Frequently Asked Questions

1. How do I send a golang post request with json body?

Build the body with json.Marshal or a bytes.Buffer of JSON, call http.NewRequest with method POST, set req.Header.Set("Content-Type", "application/json"), then client.Do(req). Always check the error from NewRequest before using req.

2. What Content-Type should I use for golang post json?

Use application/json. Charset is optional; many APIs accept application/json without charset=UTF-8.

3. Should I use ioutil.ReadAll on the response body?

Prefer io.ReadAll from the standard library; ioutil.ReadAll is deprecated since Go 1.16 and only aliases io.ReadAll.

4. Is http.Post enough for golang http post json?

http.Post sets Content-Type from the fourth argument; for JSON you still pass application/json and strings.NewReader or bytes.NewReader. For custom headers, timeouts, or context, http.NewRequest with Client.Do is clearer.
Tuan Nguyen

Data Scientist

Proficient in Golang, Python, Java, MongoDB, Selenium, Spring Boot, Kubernetes, Scrapy, API development, Docker, Data Scraping, PrimeFaces, Linux, Data Structures, and Data Mining. With expertise …