If you've ever tried to scrape a site at scale, manage more than one account on the same platform, or run a bot that doesn't get blocked after twenty requests, you already know why SOCKS5 keeps coming up. It's not the newest protocol on the block, but it's still the one most automation tools, anti-detect browsers, and scraping frameworks expect when things get serious.
This is a hands-on guide. No theory dump, no history lesson about the SOCKS protocol family. Just working setups in three languages: Python, Node.js, and Go, plus the small details that trip people up the first time (auth format, sticky sessions, timeouts, and how to actually confirm the proxy is doing its job).
Why SOCKS5 and not plain HTTP
HTTP proxies are fine when all you're doing is fetching web pages. The moment your stack includes non-HTTP traffic, custom TCP connections, or tools that talk to the internet outside a browser context, HTTP proxies start falling short. SOCKS5 works at a lower layer, so it doesn't care what kind of traffic you're pushing through it. That makes it the default choice for automation frameworks, anti-detect browser profiles, app-level traffic, and anything where you need broader protocol compatibility than a browser plugin can offer.
For this tutorial we'll be connecting through a residential SOCKS5 endpoint. A quick note on IP quality: a proxy setup is only as good as the IPs behind it. If half your requests come from flagged or overused addresses, no amount of retry logic will save your success rate. That's the part worth checking before you write a single line of code, not after your scraper starts throwing CAPTCHAs.
Getting your credentials
Every provider formats proxy credentials a little differently. With NodeMaven, a SOCKS5 endpoint looks like this:
gate.nodemaven.com:1080:username:password
Break that down and you get four pieces: the gateway host, the port, your username, and your password. You'll plug these four values into every example below, so keep them somewhere handy. If you're setting this up for the first time, grab a plan through the NodeMaven SOCKS5 proxy page and copy your credentials from the dashboard.
One thing worth knowing before you start: NodeMaven supports both rotating and sticky sessions on SOCKS5. Rotating gives you a fresh IP roughly every request, sticky keeps the same IP for up to 24 hours, which matters a lot if you're logging into accounts or doing anything that depends on session continuity. We'll touch on how sticky sessions affect your username string later on.
Python: PySocks for synchronous requests
PySocks is the simplest way to route the standard requests library through a SOCKS5 proxy. Install it alongside requests:
pip install pysocks requests
Here's a minimal working example:
import requests
PROXY_HOST = "gate.nodemaven.com"
PROXY_PORT = "1080"
PROXY_USER = "your_username"
PROXY_PASS = "your_password"
proxy_url = f"socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
proxies = {
"http": proxy_url,
"https": proxy_url,
}
response = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=15)
print(response.json())
Notice the socks5h scheme instead of plain socks5. That single letter matters more than it looks. socks5 resolves DNS locally on your machine before the request goes out, while socks5h sends the hostname to the proxy and lets it resolve DNS remotely. For anything scraping-related, you almost always want socks5h, otherwise you're leaking your real DNS resolver and possibly your location along with it.
If you're rotating through multiple accounts or need a fresh IP on every run, wrap the request in a retry loop and catch connection errors separately from HTTP errors, since a bad proxy handshake and a 403 response need different handling:
import time
def fetch_with_retry(url, proxies, retries=3):
for attempt in range(retries):
try:
r = requests.get(url, proxies=proxies, timeout=15)
r.raise_for_status()
return r
except requests.exceptions.RequestException as e:
print(f"Attempt {attempt + 1} failed: {e}")
time.sleep(2)
raise RuntimeError("All retry attempts failed")
Python: async scraping with aiohttp
Synchronous requests are fine for small jobs, but once you're pulling from dozens or hundreds of URLs, async is where the real speed gains show up. aiohttp doesn't support SOCKS5 out of the box, so you'll need aiohttp-socks as a connector.
pip install aiohttp aiohttp-socks
import asyncio
import aiohttp
from aiohttp_socks import ProxyConnector
async def fetch(session, url):
async with session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as resp:
return await resp.text()
async def main():
connector = ProxyConnector.from_url(
"socks5://your_username:your_password@gate.nodemaven.com:1080"
)
urls = [
"https://api.ipify.org?format=json",
"https://httpbin.org/headers",
]
async with aiohttp.ClientSession(connector=connector) as session:
tasks = [fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
for r in results:
print(r)
asyncio.run(main())
For large scraping jobs, cap your concurrency with a semaphore instead of firing off hundreds of coroutines at once. It keeps memory usage sane and gives your proxy pool time to breathe:
sem = asyncio.Semaphore(20)
async def bounded_fetch(session, url):
async with sem:
return await fetch(session, url)
Node.js: socks-proxy-agent with axios and native fetch
Node doesn't have native SOCKS5 support baked into its HTTP client, so you'll route requests through socks-proxy-agent. It plugs into both axios and the built-in fetch (Node 18+) without much fuss.
npm install socks-proxy-agent axios
Basic example with axios:
const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');
const proxyUrl = 'socks5://your_username:your_password@gate.nodemaven.com:1080';
const agent = new SocksProxyAgent(proxyUrl);
async function checkIp() {
const response = await axios.get('https://api.ipify.org?format=json', {
httpAgent: agent,
httpsAgent: agent,
timeout: 15000,
});
console.log(response.data);
}
checkIp().catch(console.error);
If you'd rather skip axios and use native fetch, you'll need undici under the hood since the standard fetch implementation doesn't accept a custom agent directly:
const { SocksProxyAgent } = require('socks-proxy-agent');
const { fetch: undiciFetch } = require('undici');
const agent = new SocksProxyAgent(
'socks5://your_username:your_password@gate.nodemaven.com:1080'
);
async function run() {
const res = await undiciFetch('https://api.ipify.org?format=json', {
dispatcher: agent,
});
const data = await res.json();
console.log(data);
}
run();
A common gotcha in Node: forgetting to set both httpAgent and httpsAgent on axios. Most target sites are HTTPS, so if you only set one, half your requests silently bypass the proxy and go out through your real connection. Worth double-checking any time a scraper "works" but your success rate looks suspiciously good for a proxy setup.
Go: golang.org/x/net/proxy
Go's standard library doesn't ship SOCKS5 support either, but golang.org/x/net/proxy fills that gap cleanly and integrates with net/http through a custom Dialer.
go get golang.org/x/net/proxy
package main
import (
"fmt"
"io"
"net/http"
"time"
"golang.org/x/net/proxy"
)
func main() {
auth := &proxy.Auth{
User: "your_username",
Password: "your_password",
}
dialer, err := proxy.SOCKS5("tcp", "gate.nodemaven.com:1080", auth, proxy.Direct)
if err != nil {
panic(err)
}
transport := &http.Transport{
Dial: dialer.Dial,
}
client := &http.Client{
Transport: transport,
Timeout: 15 * time.Second,
}
resp, err := client.Get("https://api.ipify.org?format=json")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
If you're running Go 1.18 or newer and want context-aware dialing (useful for cancellation and timeouts in larger apps), swap the dialer for one that implements proxy.ContextDialer:
contextDialer, ok := dialer.(proxy.ContextDialer)
if !ok {
panic("dialer does not support context")
}
transport := &http.Transport{
DialContext: contextDialer.DialContext,
}
This matters more than it seems once you start running dozens of goroutines against the same proxy pool. Without context support, a hung connection can block a goroutine indefinitely instead of respecting your timeout.
Choosing rotating vs sticky for your use case
Before you wire any of this into a real project, it helps to decide upfront whether you need rotation or persistence, because retrofitting one into the other later usually means rewriting your session logic.
Rotating proxies make sense when each request is independent and doesn't rely on cookies or login state. Think price monitoring, SERP checks, or pulling product listings from an e-commerce site where every page load stands on its own. You want a new IP often enough that no single address gets rate-limited or flagged.
Sticky sessions make sense the moment state matters. Logging into an account, filling out a multi-step form, or running a browser automation flow where the site expects the same visitor across several actions all need the same IP throughout. Switching IPs mid-session on these workflows is one of the fastest ways to get a login flagged as suspicious, since the site sees a session that started in one location and continued from a completely different one a few seconds later.
A reasonable default for a lot of scraping projects is a hybrid approach: rotate IPs between logically separate tasks or accounts, but hold a sticky session for the duration of a single task. That way you're not burning through your IP pool faster than you need to, and you're not risking session breaks either.
Sticky sessions: keeping the same IP across requests
Rotating a fresh IP per request is great for scraping, but it's the wrong move for anything that needs session continuity, like staying logged into an account across multiple requests. NodeMaven handles sticky sessions through the username string itself, so instead of changing your proxy configuration, you append a session identifier to the username when you authenticate. Check the current session parameter format in your NodeMaven dashboard before wiring it into production code, since these details get refined over time and you want the exact syntax rather than a guess.
Verifying your proxy actually works
Before you plug a proxy into a real scraper or automation script, confirm it's routing correctly. A quick IP check against a service like api.ipify.org (used in every example above) tells you two things: that the connection succeeded, and that the returned IP matches what you'd expect from your proxy provider rather than your own network. For a deeper check, an IP lookup tool can also confirm the ISP, ASN, and location tied to that address, which is handy when you need to verify a proxy is actually exiting where it claims to.
If you want to go a step further, check response headers too. Some sites fingerprint based on header order and TLS handshake details, not just IP address, so a proxy alone doesn't guarantee you'll fly under the radar. That's a separate topic, but worth keeping in the back of your mind if you're seeing blocks despite a clean IP.
Common mistakes worth avoiding
Skipping the timeout. Every example above sets an explicit timeout. Without one, a single stalled connection through a proxy can hang your script indefinitely, especially in loops processing hundreds of URLs.
Mixing up socks5 and socks5h in Python. This one bites people constantly. Use socks5h unless you specifically need local DNS resolution, which is rare in a scraping or automation context.
Forgetting to set the proxy on both HTTP and HTTPS traffic. This shows up in Node and Python alike. If your proxy config object only covers one scheme, you'll get inconsistent behavior that's hard to debug because some requests will look fine.
Hardcoding credentials in source files. Pull your username and password from environment variables instead of hardcoding them, especially if this code is going into a repo, even a private one.
import os
PROXY_USER = os.environ.get("NODEMAVEN_USER")
PROXY_PASS = os.environ.get("NODEMAVEN_PASS")
The same pattern applies in Node (process.env) and Go (os.Getenv).
Reusing one connection pool across too many concurrent tasks. Whether you're in aiohttp, Node's HTTP agent, or Go's transport, connection pooling exists to save you the overhead of a new TCP handshake per request. But push too many concurrent tasks through a single pool and you'll start seeing timeouts that have nothing to do with the proxy itself, just contention for available connections. If you're running high concurrency, size your connection pool deliberately instead of leaving it on defaults.
Not handling proxy-specific errors separately from target-site errors. A connection refused by the proxy gateway and a 429 from the site you're scraping need different responses. The first usually means retry with backoff or check your credentials, the second means you're hitting the target too fast and should slow down or rotate identity. Lumping both into one generic "request failed" branch makes debugging painful later.
Wrapping up
SOCKS5 setup itself isn't complicated once you've done it once in each language, the syntax differences between Python, Node, and Go are mostly cosmetic. What actually determines whether your scraper or automation script holds up in production is the proxy pool behind the connection: IP quality, rotation behavior, and how well sticky sessions are handled when you need continuity. Test with a small batch of requests first, watch your success rate, and scale up once you're confident the setup is solid.
If you're evaluating providers, it's worth running the same test script against a trial plan before committing to anything long term. That way you're judging real performance numbers instead of a marketing page.
Top comments (0)