You raised the thread count because requests were timing out. Throughput improved for an afternoon, then the provider's endpoint started returning errors and half the workers stalled. The plan advertised 200 concurrency, so the ceiling was supposed to be somewhere far away.
Four different numbers get described as "how much you can run at once", and they sit on three different layers. Mixing them up is how people buy a bigger tier to fix a problem that was never on the provider's side.
Four terms, three layers
| Layer | Numbers that live here | Who sets it | What exceeding it looks like |
|---|---|---|---|
| Your machine | threads, open sockets, upstream bandwidth | your client settings, your NIC | queueing, timeouts, CPU or bandwidth saturation |
| Your plan | concurrency, rotation quota, extraction frequency, IP allowlist | the provider, per tier | endpoint rejection or a limit-exceeded message |
| The destination | per-IP rate limits, fingerprint scoring | the site you are calling | CAPTCHAs and 429s however many IPs you rotate |
That third row is where the money goes missing. When the destination throttles you, more concurrency and more threads make it worse. Backing both off is frequently the actual fix.
Threads are yours; concurrency is theirs
Concurrency is an authorisation number printed on the plan: connections the provider agrees to hold open for you at one instant. It is a contract, not a measurement.
Threads are your own parallelism unit inside a scraper or a multi-boxing tool. Connection count is what the machine is holding right now — visible in Proxifier, NekoBox, or ss -tn. The three move together, but not one to one.
A thread that issues one request and waits holds one connection. A thread with keep-alive across ten hosts can hold ten.
So "raise the threads" pushes on all three layers at once. Hit your machine's wall first and you get timeouts with a sagging success rate. Hit the plan's wall first and you get endpoint errors. The symptoms differ, so read them before changing anything.
Do the arithmetic before upgrading: 40 threads touching two hosts each is up to 80 sockets. Against a tier that allows 50 concurrency, a configuration with "only" 40 threads is already over.
Extraction frequency is a rhythm, not a capacity
Extraction frequency governs how often you may call the endpoint that hands out new IPs. It says nothing about how long an IP stays usable — that is session length or rotation quota, a separate line item.
| Pattern | Rhythm | Fits | Watch out for |
|---|---|---|---|
| Timed batch | one list per fixed interval | crawling, batch registration | missing the window costs a whole cycle; the script must wait |
| On-demand | one call, immediate response | flash sales, short bursts | the rate cap usually tracks the tier |
| Manual pull | a human clicks in a dashboard | topping up a handful of IPs | cannot be scripted, so it does not scale |
One question decides which pattern you need: does a script or a human trigger the rotation? If a script does, the interval and its retries belong in the code, with a backoff that respects the window.
Measure your own ceiling
Provider pages describe the tier, not your NIC, your CPU, or your distance from the exit. The only trustworthy number is the one you measure, and it takes about ten minutes.
Ramp the level and record the first failure. A minimal SOCKS5 greeting is enough to test connection slots — you do not need to fetch a real page to find out when sockets stop opening.
import socket, time from concurrent.futures import ThreadPoolExecutor PROXY = ("203.0.113.10", 1080) # your exit def probe(_): t0 = time.time() try: s = socket.create_connection(PROXY, timeout=8) s.sendall(b"\x05\x01\x00") # ver 5, 1 method, no-auth if s.recv(2) != b"\x05\x00": return None req = b"\x05\x01\x00\x01" + socket.inet_aton("1.1.1.1") + (443).to_bytes(2, "big") s.sendall(req) ok = s.recv(10)[1] == 0 # REP byte s.close() return (time.time() - t0, ok) except OSError: return None for level in (10, 20, 40, 80, 120): with ThreadPoolExecutor(level) as ex: rows = list(ex.map(probe, range(level))) ok = sorted(r for r in rows if r and r[1]) p50 = f"p50={ok[len(ok)//2][0]:.2f}s" if ok else "-" print(level, f"ok={len(ok)/level:.0%}", p50) Read the output as a curve, not a single figure. The level just before the success rate drops is your sustainable setting; the level just before median latency doubles is your burst setting. Re-run after changing machines, exits, or targets, because the line moves.
Five mistakes that cost a tier upgrade
- Reading "unlimited" as unlimited concurrency. It usually modifies rotation count. Concurrency is a separate authorisation, often printed in the same table two columns away.
- Counting threads and calling it concurrency. Threads are yours and are bounded by hardware, not by the plan.
- Assuming a missing allowlist entry means a dead line. An unauthorised source returns an endpoint-level rejection while the exit is perfectly reachable. If the socket opens, the line is not the problem.
- Rotating IPs to escape a destination rate limit. Per-IP limits reset; account and fingerprint signals do not. More IPs against one account is the standard way to burn a whole pool.
- Sizing on peak, buying on average. Pick the tier from the level you can hold for a full business cycle, not from a burst you hit twice a day.
FAQ
Is concurrency the same as connection count? No. Concurrency is the cap the provider grants per tier; connection count is what your machine holds right now. Compare them deliberately or you will never know which one you exceeded.
The plan says unlimited — can I run unlimited threads then? No. Unlimited typically refers to rotations. Your host still has a socket and bandwidth ceiling, and the tier lists concurrency separately.
Why do I get CAPTCHAs with 200 healthy exits? Because the limit lives at the destination, not in your pool. Per-IP limits may reset while account and fingerprint signals persist across rotations.
I rotated faster and throughput fell. Why? Every rotation costs a handshake, a TLS session, and often a cold cookie jar. Past some point the switching cost exceeds the gain, and the pool looks healthy while success rate drops.
Where to verify the numbers
Term definitions and per-tier comparisons sit side by side at the proxy IP knowledge base, and exits can be measured before you commit at the IP check center. If a tier's wording is ambiguous, ask the vendor to confirm the concurrency cap and the rotation quota in writing before paying — those two numbers, not the headline price, decide whether the plan fits.