Back to all posts
Proxy Basic Auth vs API Bearer Tokens in Rotating Setups

Proxy Basic Auth vs API Bearer Tokens in Rotating Setups

November 6, 2026

Why Rotating Proxies Use Two Authentication Layers

A rotating proxy setup has two separate jobs: moving your traffic through an exit IP, and managing the session that assigns that IP. Because those jobs are handled by different systems, they require different authentication.

Confusing the two layers is a common source of 401 and 407 errors. The proxy tunnel needs session credentials for every request. The control plane needs an API key only when you list or rotate sessions. This post explains each layer, why they are separate, and how to use them together with cURL, Python, and shell scripts.

Layer 1: Proxy Connection Credentials (Basic Auth)

How the credentials work

Every request that passes through the proxy must authenticate to the gateway. The standard way is to embed the session username and password in the proxy URL:

http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT

The same HTTP port is used for both HTTP and HTTPS targets. For HTTPS, the client sends a CONNECT request through the proxy, and the gateway tunnels the encrypted traffic to the destination. Because the proxy never sees the decrypted HTTPS payload, one port can serve both schemes.

Each session has its own username and password. If you create multiple sessions, do not mix their credentials; a password from session A will not work with session B.

cURL examples

You can pass the credentials directly in the URL:

curl -x http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT \
  https://api.ipify.org?format=json

Alternatively, split the host and credentials:

curl --proxy http://GATEWAY_HOST:HTTP_PORT \
  --proxy-user USERNAME:PASSWORD \
  https://api.ipify.org?format=json

Both forms tell cURL to authenticate to the proxy before fetching the target. The -v flag is useful for debugging because it shows the proxy negotiation and any 407 Proxy Authentication Required response.

Python example

The requests library accepts a proxies dictionary. Use the same URL for both http and https keys because the gateway port handles both:

import requests

proxies = {
    'http': 'http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT',
    'https': 'http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT',
}

response = requests.get(
    'https://api.ipify.org?format=json',
    proxies=proxies,
    timeout=30,
)
print(response.json())

If you are using RoProxy, configure your client with the username and password for the session you created. Each session has its own credentials, so do not reuse one session's password with another session's ID.

Environment variables

Many command-line tools and language runtimes read HTTP_PROXY and HTTPS_PROXY automatically. This avoids hardcoding credentials in every script:

export HTTP_PROXY='http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT'
export HTTPS_PROXY='http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT'
export NO_PROXY='localhost,127.0.0.1,.internal'

The NO_PROXY list keeps local and internal traffic out of the tunnel, which prevents loops and unnecessary latency.

Layer 2: Session API Authentication (Bearer Token)

What the API key controls

The backend API is where you manage sessions, not where you send traffic. Authentication uses a bearer token in the request header:

Authorization: Bearer API_KEY

This header is never added to requests that go through the proxy to a target website. It is sent only to the API endpoints.

List your sessions:

curl -s -H 'Authorization: Bearer API_KEY' \
  https://backend-api.ro-proxy.com/api/session

Rotate a session's IP immediately:

curl -s -X POST -H 'Authorization: Bearer API_KEY' \
  https://backend-api.ro-proxy.com/api/session/SESSION_ID/rotate

Why the layers are separate

Separating data-plane credentials from control-plane credentials follows a basic security principle: the token used to change your exit IP should not be transmitted to every website you visit. If your application logs outgoing HTTP headers, the API key stays out of those logs because it is never sent through the proxy tunnel.

Rotation Modes and What Actually Changes

A session has a rotation mode that determines when its exit IP changes:

  • Sticky: Keeps the same IP until you rotate it manually or the IP fails. The session credentials stay the same.
  • Timed: Changes the IP after an interval you set. The session credentials stay the same.
  • Per request: Uses a new IP for every new connection. Available on the Business plan only. The session credentials stay the same.

Rotation changes the upstream IP mapped to your session. It does not change the username or password you use to authenticate to the gateway. That means your client configuration can remain static while the exit IP changes behind the scenes.

End-to-End Example: Check IP, Rotate, Verify

Here is a shell sequence that lists sessions, checks the current exit IP, rotates, and checks again.

  1. List sessions and note the SESSION_ID:
curl -s -H 'Authorization: Bearer API_KEY' \
  https://backend-api.ro-proxy.com/api/session
  1. Check the exit IP through the proxy:
curl -s -x http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT \
  https://api.ipify.org?format=json
  1. Rotate the session:
curl -s -X POST -H 'Authorization: Bearer API_KEY' \
  https://backend-api.ro-proxy.com/api/session/SESSION_ID/rotate
  1. Check the exit IP again:
curl -s -x http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT \
  https://api.ipify.org?format=json

The ip field in the JSON response should change between step 2 and step 4. If it does not, the session may be in sticky mode and the previous IP may still be valid, or the rotate request may have returned an error.

Python Script: Rotate on Target-Side Errors

For long-running scripts, you can automate rotation when the target returns 403 Forbidden or 429 Too Many Requests. Do not rotate on 407 Proxy Authentication Required; that means your proxy credentials are wrong, and rotating the IP will not fix authentication.

import requests
import time

API_KEY = 'API_KEY'
SESSION_ID = 'SESSION_ID'
USERNAME = 'USERNAME'
PASSWORD = 'PASSWORD'
GATEWAY_HOST = 'GATEWAY_HOST'
HTTP_PORT = 'HTTP_PORT'

proxy_url = 'http://USERNAME:PASSWORD@GATEWAY_HOST:HTTP_PORT'
proxies = {'http': proxy_url, 'https': proxy_url}

api_headers = {'Authorization': 'Bearer API_KEY'}


def rotate_session():
    url = 'https://backend-api.ro-proxy.com/api/session/' + SESSION_ID + '/rotate'
    requests.post(url, headers=api_headers, timeout=30)


def fetch(url):
    for attempt in range(2):
        try:
            response = requests.get(url, proxies=proxies, timeout=30)
            if response.status_code in (403, 429):
                rotate_session()
                time.sleep(2)
                continue
            return response
        except requests.RequestException:
            if attempt == 0:
                rotate_session()
                time.sleep(2)
            else:
                raise
    return requests.get(url, proxies=proxies, timeout=30)


print(fetch('https://api.ipify.org?format=json').json())

This script keeps the proxy URL constant. When the target blocks the current exit IP, the script calls the API to rotate and retries once.

Choosing a Rotation Mode

  • Use sticky for multi-step flows such as login sequences or checkout funnels. Changing IP mid-flow can invalidate server-side sessions or trigger security checks.
  • Use timed for batch jobs that run for minutes or hours. You get predictable IP changes without writing rotation logic.
  • Use per request for stateless API calls where each request should originate from a different address. This mode is available on the Business plan only.

SOCKS5 as an Alternative Tunnel

Pro and Business plans also provide a SOCKS5_PORT. Unlike the HTTP gateway, SOCKS5 forwards raw TCP and does not interpret HTTP. It is useful when your application speaks a non-HTTP protocol or when you want the client to handle the CONNECT step itself.

curl --socks5-hostname USERNAME:PASSWORD@GATEWAY_HOST:SOCKS5_PORT \
  https://api.ipify.org?format=json

Choose the HTTP gateway for browsers and standard HTTP clients. Choose SOCKS5 when your tool explicitly supports SOCKS5 or when you need UDP forwarding.

Debugging Authentication Failures

  • 407 Proxy Authentication Required: Verify USERNAME, PASSWORD, GATEWAY_HOST, and HTTP_PORT. Ensure the session is active and you are not mixing credentials from different sessions.
  • 401 Unauthorized from the API: Verify API_KEY and that the header is exactly Authorization: Bearer API_KEY.
  • 403 Forbidden from the API: The session ID may not exist, or your plan may not support the requested action.
  • Use curl -v to see request and response headers. It reveals whether the client is authenticating to the proxy or to the target.

Never put the API key in the proxy URL, and never put the session password in the Authorization header.

Conclusion

Rotating proxies work best when you treat the data plane and control plane as separate concerns. Use session username and password for the proxy tunnel, and use Authorization: Bearer API_KEY only for session management. Keep your proxy URL stable across rotations, and choose sticky, timed, or per-request mode based on whether your traffic is stateful or stateless.

RoProxy support is available on Telegram if you need help with session credentials or API keys.