Before you start

  • A Go toolchain on your PATH. Only long-standing net/http and net/url APIs are used, so any current release will do.
  • A folder for a small module, created in step 1.
  • HOST, PORT, USERNAME and PASSWORD copied from your service page.

Your connection details

Sign in to the dashboard and open the service you bought. Its page shows the host, port, username and password for that service. There is no single ProxyPanda address to remember, so copy them from there each time. The code in these guides uses the placeholders below; replace each one with your own value.

HOST
The proxy address for this service, exactly as the service page shows it.
PORT
The port to connect to. Copy it with the host, since it can differ from one service to the next.
USERNAME
Your proxy login. It is separate from the email you use for the dashboard.
PASSWORD
Copy it in full. On residential, the options you choose in the dashboard are added to the end of the password, so a password typed from memory loses them.

You can skip the username and password by adding the IP address you connect from to the service’s allowlist. Then only HOST and PORT go into your code.

The examples use HTTP, which reaches https sites through an encrypted tunnel. SOCKS5 works too: residential ports accept both, and ISP and datacenter proxies switch protocol in the dashboard.

Steps

  1. Create a module

    go version confirms which toolchain runs the code. Each example below is a complete main.go for this folder, so replace the file each time and run it with go run .

    terminal
    go version
    mkdir proxycheck && cd proxycheck
    go mod init proxycheck
  2. Point the Transport at the proxy

    http.ProxyURL wraps a fixed URL in the function the Transport calls for each request. url.UserPassword holds the login and percent-encodes it when the URL is written out, so a password with @, : or / needs no escaping by hand. Client.Timeout caps the whole exchange, the CONNECT to the proxy included.

    main.go
    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    	"net/url"
    	"time"
    )
    
    func main() {
    	proxyURL := &url.URL{
    		Scheme: "http",
    		User:   url.UserPassword("USERNAME", "PASSWORD"),
    		Host:   "HOST:PORT",
    	}
    
    	client := &http.Client{
    		Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
    		Timeout:   30 * time.Second,
    	}
    
    	resp, err := client.Get("https://api.ipify.org?format=json")
    	if err != nil {
    		fmt.Println("request failed:", err)
    		return
    	}
    	defer resp.Body.Close()
    
    	body, _ := io.ReadAll(resp.Body)
    	fmt.Println(resp.Status, string(body))
    }
  3. Run it and check the exit

    go run . prints 200 OK followed by the JSON from api.ipify.org. The ip field is the address the site saw, which should belong to the proxy.

  4. Set Transport timeouts and a new connection per request

    The Dialer timeout limits how long reaching the proxy may take, TLSHandshakeTimeout covers the handshake with the site inside the tunnel, and ResponseHeaderTimeout the wait for the first response byte. DisableKeepAlives closes each connection after one request: in testing, three requests opened three CONNECT tunnels with it and a single tunnel without it.

    main.go
    package main
    
    import (
    	"fmt"
    	"io"
    	"net"
    	"net/http"
    	"net/url"
    	"time"
    )
    
    func main() {
    	proxyURL := &url.URL{
    		Scheme: "http",
    		User:   url.UserPassword("USERNAME", "PASSWORD"),
    		Host:   "HOST:PORT",
    	}
    
    	transport := &http.Transport{
    		Proxy:                 http.ProxyURL(proxyURL),
    		DialContext:           (&net.Dialer{Timeout: 10 * time.Second}).DialContext,
    		TLSHandshakeTimeout:   10 * time.Second,
    		ResponseHeaderTimeout: 30 * time.Second,
    		DisableKeepAlives:     true,
    	}
    	client := &http.Client{Transport: transport, Timeout: 60 * time.Second}
    
    	for i := 0; i < 3; i++ {
    		resp, err := client.Get("https://api.ipify.org")
    		if err != nil {
    			fmt.Println(i, "error:", err)
    			continue
    		}
    		body, _ := io.ReadAll(resp.Body)
    		resp.Body.Close()
    		fmt.Println(i, string(body))
    	}
    }
  5. Read the proxy from HTTPS_PROXY instead

    http.ProxyFromEnvironment picks HTTPS_PROXY for https URLs and HTTP_PROXY for http ones, and skips hosts listed in NO_PROXY. http.DefaultTransport already calls it. A Transport you build yourself does not: with no Proxy field, it went straight out even with the variable exported.

    main.go
    package main
    
    import (
    	"fmt"
    	"io"
    	"net/http"
    	"time"
    )
    
    func main() {
    	client := &http.Client{
    		Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
    		Timeout:   30 * time.Second,
    	}
    
    	resp, err := client.Get("https://api.ipify.org")
    	if err != nil {
    		fmt.Println("request failed:", err)
    		return
    	}
    	defer resp.Body.Close()
    
    	body, _ := io.ReadAll(resp.Body)
    	fmt.Println(string(body))
    }
  6. Export the variable and run

    The login stays in the variable, so the Go code carries no credentials. The URL here is plain text, so percent-encode any @, : or / in the password yourself. Unset the variable afterwards if other tools share the shell.

    terminal
    export HTTPS_PROXY="http://USERNAME:PASSWORD@HOST:PORT"
    go run .

Rotating and sticky IPs

With keep-alives on, the Transport holds a tunnel open and sends later requests to the same site through it, and a reused tunnel keeps its exit address. On residential with Randomize IP, set DisableKeepAlives when every request should be able to get a new address.

On Sticky IP, or on an ISP or datacenter IP, leave keep-alives on. The address is meant to stay the same, and skipping a fresh handshake per request saves time.

One http.Client can be shared by many goroutines. To spread work over several static IPs, build one Transport per IP and choose between them per request.

Common errors and fixes

Get "https://api.ipify.org…": Proxy Authentication Required

The proxy turned down the login. Copy USERNAME and PASSWORD again. If you build the proxy URL with url.Parse from a string, percent-encode special characters in the password first, or switch to url.UserPassword.

proxyconnect tcp: dial tcp …: connect: connection refused

No proxy is listening at HOST:PORT. Compare both with the service page and check that the service is still active.

context deadline exceeded (Client.Timeout exceeded while awaiting headers)

Nothing came back before Client.Timeout ran out. Residential exits are home connections and some are slow, so give those requests more time and retry just that URL.

The program prints your own address

The Transport has no Proxy field, or HTTPS_PROXY was exported in a different terminal. A Transport you construct ignores the environment until Proxy is set to http.ProxyFromEnvironment.

Which line to pick

Go workers that poll APIs or pull pages in bulk run cheapest on datacenter IPs wherever the target accepts them. Move to residential once requests from server ranges get refused, and give a long-lived worker an ISP IP when it must keep one address across sessions.

Stuck on a step?

Paste the command and the error into Discord, with your password taken out. People there have met most of these errors before.

Join the Discorddiscord.gg/proxypanda
Start with $5Ask in Discord