A runnable Go program from one curl command
Go developers often receive an API call as a curl command and need the net/http equivalent, which is verbose: build a request, set headers, configure a client, handle every error, read and close the body. This converter writes all of that for you as a complete program with package main, a main function and only standard-library imports. Save it as main.go and go run it, or lift the request-building lines into your own code.
The layout matches gofmt exactly — tabs, sorted imports, aligned struct fields — so your editor will not reformat it.
Request construction
- The request is created with
http.NewRequest, using constants such ashttp.MethodPostfor standard verbs and a string for custom ones. - A
-dor--jsonbody is wrapped instrings.NewReader, using a raw string literal (backticks) when the body allows it, so JSON stays readable. -Ffields are written withmime/multipart:writer.WriteFieldfor text and a smalladdFilehelper that streams a file from disk withos.OpenandCreateFormFile. The Content-Type header then comes fromwriter.FormDataContentType(), which includes the boundary.-T fileuploads the opened file as the body.- Each
-Hbecomesreq.Header.Set; headers that repeat usereq.Header.Addso every value is sent. AHostheader is assigned toreq.Host, because Go ignores it in the header map. -u user:passbecomesreq.SetBasicAuth.
Client and transport settings
Options that affect the connection are set on http.Client and its http.Transport:
-m 2.5→Timeout: 2500 * time.Millisecond(whole seconds usetime.Second).--connect-timeout→ anet.Dialerwith its own timeout inDialContext.-k→TLSClientConfig: &tls.Config{InsecureSkipVerify: true}.-x→Proxy: http.ProxyURL(proxyURL), with-Ucredentials attached throughurl.UserPassword.- No
-L→ aCheckRedirectfunction returninghttp.ErrUseLastResponse, because Go’s client follows up to ten redirects by default while curl follows none.
--compressed needs no code: Go’s transport already requests and transparently decompresses gzip, as an info note explains. Errors are handled with log.Fatal, and the program prints resp.Status and the body.
Limits and privacy
Per-part content types and custom file names given with -F '[email protected];type=image/png' are not reproduced by the addFile helper, and a note says so. Options that only change curl’s own output are skipped; anything with behaviour the code does not reproduce is listed as a warning or info message. The command, including any secrets, is processed entirely in your browser. Format other Go code with the Go formatter.
If the response is JSON, the next step is usually decoding it into a struct: paste a sample response into JSON to Go struct and swap io.ReadAll for json.NewDecoder(resp.Body).Decode(&v).
Examples
PUT JSON with a timeout and -k
The JSON body uses a raw string literal, the client gets a 2500 ms timeout and a transport that skips TLS verification.
curl -X PUT https://staging.example.com/api/items/42 \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc' \
-d '{"name":"Mechanical keyboard","price":129.9}' \
-k --max-time 2.5package main
import (
"crypto/tls"
"fmt"
"io"
"log"
"net/http"
"strings"
"time"
)
func main() {
body := strings.NewReader(`{"name":"Mechanical keyboard","price":129.9}`)
req, err := http.NewRequest(http.MethodPut, "https://staging.example.com/api/items/42", body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc")
client := &http.Client{
Timeout: 2500 * time.Millisecond,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
// curl follows redirects only with -L
return http.ErrUseLastResponse
},
}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(respBody))
}
Multipart upload through a proxy
Builds a multipart body with an addFile helper, configures an authenticated proxy, and omits CheckRedirect because -L is present.
curl https://files.example.com/upload \
-F '[email protected]' -F 'user=aisha' \
-x http://proxy.corp:3128 -U svc:hunter2 -Lpackage main
import (
"bytes"
"fmt"
"io"
"log"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"
)
func main() {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
if err := addFile(writer, "avatar", "me.png"); err != nil {
log.Fatal(err)
}
if err := writer.WriteField("user", "aisha"); err != nil {
log.Fatal(err)
}
if err := writer.Close(); err != nil {
log.Fatal(err)
}
req, err := http.NewRequest(http.MethodPost, "https://files.example.com/upload", body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
proxyURL, err := url.Parse("http://proxy.corp:3128")
if err != nil {
log.Fatal(err)
}
proxyURL.User = url.UserPassword("svc", "hunter2")
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
fmt.Println(string(respBody))
}
// addFile copies a file from disk into a multipart form field.
func addFile(writer *multipart.Writer, field, path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
part, err := writer.CreateFormFile(field, filepath.Base(path))
if err != nil {
return err
}
_, err = io.Copy(part, file)
return err
}
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
The file "me.png" for form field "avatar" is not available here; the code uploads it from disk or contains a placeholder | A warning: file uploads are read from disk when the Go program runs, not during conversion. | Run the program from a directory where the path resolves, or change it to an absolute path. |
A GET request with a body is unusual; many clients (including fetch) refuse to send it. Did you mean -G? | The command sets -X GET together with -d data. | Add -G to send the data as a query string, or change the method. |
The upload file "backup.tar" (-T) cannot be read here; the code uploads it from disk or contains a placeholder | The command uploads a file with -T. | Nothing to change if the path is right; the generated code opens the file with os.Open. |
Only the first command is converted; everything from "&" onwards is ignored | The pasted text chains several commands. | Convert each curl command separately. |
Frequently asked questions
Does the generated code need third-party packages?
No. It uses only the standard library: net/http, plus packages such as mime/multipart, crypto/tls or net/url when the command needs them.
Why is there a CheckRedirect function?
Go follows redirects by default, but your curl command does not use -L. Returning http.ErrUseLastResponse makes the client stop at the first response, as curl would.
Will gofmt change the output?
No. Imports are sorted, indentation uses tabs and composite-literal fields are aligned the way gofmt prints them.
Is the curl command uploaded anywhere?
No. It is parsed by JavaScript running in this page, and the Go code is generated on your device.