Credential warm-up: why a new string can 407 for a minute

2026-10-06 · Bazyl

A brand-new residential credential can answer 407 for about a minute while our gateways pick it up. In that minute the gateway's answer is the same one a wrong password gets, so you cannot read the difference off the response. You read it off the clock.

This post gives you the clock: a loop that retries for 90 seconds and then stops with a message a human can act on, plus the one diff that finds a 407 which was never going to clear. The mistake I see most often is the opposite of patience. Someone regenerates the string three times inside the window and then writes in about a broken account.

The gateway's challenge is identical both ways

The 407 our gateways send is one fixed header, and it says nothing about why. Run curl -sI through the rotating port with placeholder credentials and this is the whole answer:

HTTP/1.1 407 Proxy Authentication Required
Proxy-Authenticate: Basic realm="ProxyServer"

A wrong password gets that header. A username that doesn't exist gets that header. A credential created ten seconds ago gets it too. The realm names no account and no reason, so there is nothing in the response to parse for. The only signal you have is whether the 407 is still there after the warm-up window has passed.

Two things the header does rule out. A 407 can only come from a gateway that answered, so a wrong host or port never produces one; it produces silence. And it comes from our gateway, not from the site you asked for, so the target's own status codes are a separate question that starts once the tunnel is open.

The minute is propagation, and the API tells you its length

A credential exists in your account before the gateways have it. Make one in the dashboard, or with POST /customers on the partner API, and the three doors into the pool, eu-, us- and ap-residential2.basilproxies.com, pick it up within about a minute. Until they do, the door you hit treats the pair as unknown and challenges it.

The partner API puts a number on this. Every credentials response carries warmupSeconds: 60 next to the username, the password, the hosts, the two ports and the ready-made urls.

Sixty is the number I stand behind, and the loop below waits 90: the window plus margin for a slow first request. The lag runs one way. Taking a credential away has no warm-up: rotate-credentials on the partner API kills the old password at once, and even the slowest case, a sub-account password reset in the dashboard, stops the old strings within about a minute with the data untouched. Birth takes a minute. Death takes a minute at most.

Retry for 90 seconds, then stop

Bounded retry is the whole fix: a loop that gives the window its minute and then refuses to wait any longer. This one runs as written once you paste in your own pair:

#!/usr/bin/env bash
# Retry a brand-new string through the rotating port for up to 90 seconds.
# Warm-up is about 60 s; a 407 that outlives 90 s is the string, not the clock.
PROXY="http://USER-country-de:[email protected]:4242"
deadline=$(( $(date +%s) + 90 ))

while :; do
  # http_connect = the gateway's answer to curl's CONNECT: 200 open, 407 refused
  connect=$(curl -s -o /dev/null -m 15 -w '%{http_connect}' -x "$PROXY" https://example.com)
  case "$connect" in
    200) echo "live: the gateway opened the tunnel"; exit 0 ;;
    407) ;;
    *)   echo "no tunnel (code $connect): host, port or network, not auth" >&2; exit 2 ;;
  esac
  if [ "$(date +%s)" -ge "$deadline" ]; then
    echo "still 407 after 90 s: not warm-up. Diff the string against one the dashboard generated." >&2
    exit 1
  fi
  echo "407, retrying in 5 s"
  sleep 5
done

Three exits, three meanings. 200 on the CONNECT means the gateway accepted the pair and opened the tunnel; whatever the site says next is the site's business. 407 past the deadline means the string, and the next section is for you. Anything else, 000 nearly always, means nothing answered: a typo in the host, a port that isn't 4242 or 4243, a firewall on your side. Silence is never an auth problem.

The variable matters. For an https:// target curl sends a CONNECT, and the gateway's answer to it lives in %{http_connect}, not in %{http_code}, which stays 000 when the tunnel never opens. Swap the port for 4243, add your session tokens, and the same loop tests a sticky string.

Don't loop without a deadline. A loop that never gives up turns a wrong password into a job that "hangs", and you'll spend an hour debugging the wrong thing.

Past 90 seconds it's the string, and it's usually the password

The format is http://USER-<tokens>:PASS@<gateway>:<port>. Every targeting token rides the username, hyphen-separated, in one fixed order: country, then city or ISP (ASN), then OS, then session, then lifetime. The password carries nothing and never changes.

A token on the password is a password the gateway has never seen, so it challenges every request, forever. A bot written for a format that put flags on the password does this on day one, and the symptom is a 407 that looks exactly like warm-up and never ends:

# never clears: the country token landed on the password
http://USER:[email protected]:4242

# clears within about a minute: tokens on the username, bare password
http://USER-country-de:[email protected]:4242

The check is a diff, not a guess. Generate one string in the dashboard, or pull one from the account API's /proxies, which builds lines from the credentials your account already holds and spends no data, and compare it with yours token by token. The full token reference is in the docs, and the one format a bot should parse and build is in the string-format post. A string that matches a generated one and still 407s after 90 seconds is the case to write in about, and that case is rare.

One more cause that isn't a fault: a credential you ended. If a sub-account's password was reset, its old strings stop within about a minute. If you're a partner and you called rotate-credentials, or kill, which is a suspend plus that rotation, the old password died at once. A 407 from a customer whose plan you ended yesterday is correct behaviour.

Don't hand a fresh credential to someone who will test it now

For a partner the warm-up is a delivery-timing problem, and the API gives you the number to time it with. A customer who pays in your Discord and gets a string ten seconds later tests it inside the window, sees 407, and opens a ticket. Every time.

Three ways to deliver, best first:

  1. Create the customer the moment the order is paid and send the credentials once warmupSeconds has passed. POST /customers with initialGb funds the customer and returns working credentials in one call, and the number to sleep is in the same response. Send an Idempotency-Key on that call, so a webhook that fires twice gets the stored response instead of a second customer.
  2. Deliver at once, but put the fact in the delivery message: new strings can answer 407 for the first minute, so retry before writing in.
  3. Deliver at once and say nothing. That is the ticket.

What we don't do: we don't pre-warm a credential that doesn't exist yet, and we don't make the challenge say more than it does. There is no SOCKS5 on this pool either; it's an HTTP proxy on both ports, and https:// targets tunnel through it.

Start from the residential page, make one new string in the dashboard, and point the loop at it, so you've seen the window once before a drop morning shows it to you.

Common questions

Is a 407 from a new proxy string a wrong password?
Not in the first minute. Brand-new credentials can answer 407 for about a minute while our gateways pick them up; the partner API reports this as warmupSeconds 60. A 407 that outlives 90 seconds is a wrong string.
How can I tell a warm-up 407 from a wrong password?
Not from the response: the challenge is the same header both times, Proxy-Authenticate: Basic realm="ProxyServer". Retry for 90 seconds. A 407 still there afterwards is the string, most often a targeting token that landed on the password.
Where do targeting tokens go, username or password?
Username, always: country, city or ASN, OS, session and lifetime, in that order. The password never changes and carries nothing.
How fast does an old password stop working?
On the partner API, rotate-credentials kills the old password at once. A sub-account password reset in the dashboard stops the old strings within about a minute and leaves the data untouched.