Go · setup guide

Go (Golang) proxy setup

Go needs no packages for this. Put http.ProxyURL on a Transport, and the standard library sends the credentials from the URL on every CONNECT. The one setting worth thinking about is keep-alive, because it decides whether you rotate.
You will need

Residentialresi.proxymonkey.io:8000

ISP or datacenterIP:PORT

CredentialsUSER:PASS

Copy your own from the dashboard, which also lists the host and port for every order. Where it differs from this page, the dashboard is right.

Install

Nothing to install beyond Go itself; everything here is in the standard library.

terminal
go mod init monkeycrawl

Rotating residential

With DisableKeepAlives, every request opens a new tunnel through the residential gateway and so gets a new address. Leave keep-alives on and requests to the same host share one tunnel and one IP.

main.go
package main

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

func main() {
	proxy, _ := url.Parse("http://USER:[email protected]:8000")
	client := &http.Client{
		Timeout: 30 * time.Second,
		Transport: &http.Transport{
			Proxy:             http.ProxyURL(proxy),
			DisableKeepAlives: true,
		},
	}

	for i := 0; i < 3; i++ {
		resp, err := client.Get("https://httpbin.org/ip")
		if err != nil {
			fmt.Println(err)
			continue
		}
		body, _ := io.ReadAll(resp.Body)
		resp.Body.Close()
		fmt.Print(string(body))
	}
}

A static datacenter or ISP IP

On a static datacenter IP keep-alives are what you want. This version fetches a list of URLs with at most eight in flight, using a buffered channel as the limit; it is the body of main, with log and sync added to the imports. For an ISP order, use the host and port from your dashboard.

crawl.go
proxy, _ := url.Parse("http://USER:PASS@IP:PORT")
client := &http.Client{
	Timeout: 30 * time.Second,
	Transport: &http.Transport{
		Proxy:               http.ProxyURL(proxy),
		MaxIdleConnsPerHost: 8,
	},
}

sem := make(chan struct{}, 8)
var wg sync.WaitGroup
for _, u := range urls {
	wg.Add(1)
	sem <- struct{}{}
	go func(u string) {
		defer wg.Done()
		defer func() { <-sem }()
		resp, err := client.Get(u)
		if err != nil {
			log.Println(u, err)
			return
		}
		io.Copy(io.Discard, resp.Body)
		resp.Body.Close()
		log.Println(u, resp.StatusCode)
	}(u)
}
wg.Wait()

Keeping one identity

With keep-alives on, requests to one host reuse a tunnel and keep its exit address until the connection idles out. That is not a guarantee, so for a residential IP that must hold across a login, use a sticky session; the session setting for your account is in the dashboard.

Specific to Go (Golang)

Things worth knowing

Build the URL, do not concatenate it

url.UserPassword escapes a password with @ or : in it correctly. String concatenation does not.

proxy.go
proxy := &url.URL{
	Scheme: "http",
	User:   url.UserPassword("USER", "PASS"),
	Host:   "resi.proxymonkey.io:8000",
}

Drain the body before closing

A connection goes back to the pool only once its body has been read to the end. io.Copy(io.Discard, resp.Body) before Close keeps keep-alive working on a static IP.

The default client already reads HTTPS_PROXY

http.DefaultTransport uses http.ProxyFromEnvironment. Setting HTTPS_PROXY routes every default-client request through the proxy with no code change, which is handy for tools you did not write.

When it breaks

Common errors and fixes

  • An error ending in Proxy Authentication Required

    Why
    The CONNECT was answered with a 407: the credentials were rejected.
    Fix
    Build the URL with url.UserPassword and check the pair against your dashboard.
  • proxyconnect tcp: dial tcp: lookup ...: no such host

    Why
    A typo in the proxy host, or DNS that cannot resolve it from where the program runs.
    Fix
    Resolve the host from the same container or machine, then compare it with the dashboard.
  • context deadline exceeded (Client.Timeout exceeded while awaiting headers)

    Why
    The request took longer than Client.Timeout.
    Fix
    Retry with a fresh request. With keep-alives off on residential, the retry leaves from a new address.
  • x509: certificate signed by unknown authority

    Why
    Usually a scratch or minimal image with no CA certificates in it. The proxy does not touch TLS.
    Fix
    Copy /etc/ssl/certs/ca-certificates.crt into the image, or install ca-certificates.

A failed connection that moved no data is not billed. Retries you send are billed like any other request, and each shows as its own line in your usage log.

The community layer

Go (Golang) still misbehaving?

Paste the error and the few lines that set up the proxy into Discord, with the password taken out. Someone there has seen it before.

Join the Discord

4,200+monkeys in the Discord

  • Help from humans

    Post your error, get an answer. Usually in minutes, usually from someone who has hit the same wall.

  • A status bot that tells on us

    Pool health, incidents and maintenance posted automatically. Including the bad days.

  • Deals and free traffic

    Bonus GB drops, early access to new pools, and the occasional giveaway for a good bug report.

Join the Discord4,200+ monkeys, free to lurk