> This is part 1 of 2 of the full documentation (pages 1–100 of 112).
> The content is paginated: fetch every part to see all of it.
> Next part: https://docs.helodata.com/llms-full.txt/1
> Page index: https://docs.helodata.com/llms.txt

# Welcome to helodata

Build with the helodata proxy network.

helodata is a proxy network built for engineers. We give developers ethically sourced IPs and the tools to run them at scale — without managing infrastructure.

## What you can build

* **Price monitoring** across thousands of e-commerce SKUs
* **Search-engine result tracking** for SEO and ad-intelligence
* **Brand and review monitoring** on marketplaces and social platforms
* **Localized QA** for ads, content, and pricing across countries
* **AI / LLM data pipelines** that need fresh, unblocked web content

## Where to start

| If you are…                     | Go to                                                 |
| ------------------------------- | ----------------------------------------------------- |
| New to proxies                  | [How proxies work](/getting-started/how-proxies-work) |
| Picking a product               | [Choose a product](/getting-started/choose-a-product) |
| Ready to send a request         | [Quick start](/getting-started/quick-start)           |
| Building the username string    | [Username format](/getting-started/username-format)   |
| Integrating an existing scraper | [Integrations](/integrations/integrations)            |
| Looking for the REST API        | [API reference](/api-reference/api-reference)         |

## Product lines

**Available now**

* [**Residential proxies**](/products/overview) — real-user IPs, pay per GB (`type-res`)
* [**Mobile proxies**](/products/overview-1) — 3G/4G/5G carrier IPs (`type-mob`)
* [**ISP proxies**](/products/overview-2) — static residential IPs delivered as `ip:port:user:pass` lists

**Coming soon**

* Datacenter proxies
* Web Unblocker
* Scraping API

## Support

* Email: <support@helodata.com>
* Live chat: 24/7 from the [dashboard](https://dashboard.helodata.com)
* Status page: [status.helodata.com](https://status.helodata.com)


# Introduction

helodata is a proxy platform built for engineers. We run the IP infrastructure, the rotation logic, and the geo-targeting — you make HTTP requests.

## What's in this documentation

* [**Products**](/products/overview) — per-SKU reference for residential, mobile, and ISP proxies
* [**Integrations**](/integrations/integrations) — drop-in guides for Selenium, Puppeteer, Scrapy, antidetect browsers, n8n, LangChain, and more
* [**API reference**](/api-reference/api-reference) — REST endpoints to manage orders, sub-users, traffic, and reports
* [**Troubleshooting**](/troubleshooting/troubleshooting) — error codes and recovery playbooks

## Conventions

Code samples use these placeholders. Replace them with values from your dashboard.

| Placeholder        | Meaning                                                                                                                                                        |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `USERNAME`         | Your sub-user name (e.g. `s1a2b3c4d5e`). The full proxy username is built by wrapping it in the [helodata username grammar](/getting-started/username-format). |
| `PASSWORD`         | The sub-user password                                                                                                                                          |
| `gate.helodata.io` | The proxy gateway host                                                                                                                                         |
| `7777`             | The default HTTP/SOCKS5 port                                                                                                                                   |
| `API_KEY`          | A REST API token issued from **Dashboard → API**                                                                                                               |

Every proxy username follows one shape:

```
helo_{username}-type-{type}-region-{region}[-state-{state}][-city-{city}][-asn-{asn}][-session-{session_id}-sesstime-{ttl}]
```

If a code sample shows `helo_s1a2b3c4d5e-type-res-region-us`, the `s1a2b3c4d5e` part is what you replace.

All examples assume a Unix-like shell. Windows users should adapt quoting accordingly.

## Versioning

The REST API is versioned in the URL (`/v1/...`). Breaking changes ship as a new major version with a 6-month deprecation window. See the [Changelog](/resources/changelog) for what shipped when.


# How proxies work

A proxy is an intermediate server that forwards your HTTP(S) requests to a target site. The target sees the proxy's IP, not yours. helodata operates pools of proxies sourced from real residential, mobile, and ISP networks worldwide.

## Request lifecycle

```
┌─────────┐         ┌──────────────────┐         ┌──────────────┐
│  Your   │  HTTP   │  helodata proxy  │  HTTP   │   Target     │
│  app    ├────────►│  gateway         ├────────►│   website    │
│         │◄────────┤                  │◄────────┤              │
└─────────┘         └──────────────────┘         └──────────────┘
```

1. Your application sends a request to `gate.helodata.io:7777` with credentials.
2. For **gateway products** (Residential, Mobile), the gateway parses your [username string](/getting-started/username-format), authenticates the sub-user, then selects a backend IP matching the requested `type`, `region`, `state`, `city`, `asn`, and `session`. For **ISP proxies**, your client connects directly to a specific IP from your purchased list — no gateway routing.
3. The target site responds. The body streams back through the gateway to you.

## Pool types at a glance

| Pool            | `type` code                       | IP source            | Best for                               |
| --------------- | --------------------------------- | -------------------- | -------------------------------------- |
| **Residential** | gateway, `type-res`               | Real residential IPs | Anti-bot sites, geo-restricted content |
| **Mobile**      | gateway, `type-mob`               | 3G/4G/5G carriers    | Mobile-only content, social platforms  |
| **ISP**         | per-IP list (`ip:port:user:pass`) | Static residential   | Account farming, long-lived sessions   |

If unsure, jump to [Choose a product](/getting-started/choose-a-product).

## Authentication models

helodata supports two authentication models on every product:

* **User + password** — pass credentials in the `Proxy-Authorization` header or `http://user:pass@host:port` URL form. Most flexible — works from any IP. The username carries all targeting parameters (see [Username format](/getting-started/username-format)).
* **IP whitelist** — register your egress IPs in the dashboard and authenticate by source IP. Useful for environments that can't store credentials. Targeting is selected from the dashboard rather than the username.

See [Authentication](/api-reference/authentication) for full details.

## Sessions and rotation

The two credential models handle session affinity very differently.

**Gateway products (Residential, Mobile)** — sessions are opt-in via the `session` + `sesstime` segments of the username:

```
helo_s1a2b3c4d5e-type-res-region-us-session-abc123-sesstime-30
```

* `session-abc123` — any string you choose; identifies the session.
* `sesstime-30` — keep this IP for up to 30 minutes (residential cap = 30, mobile cap = 60).

Drop both segments to get a new IP on every connection (rotating mode).

**ISP proxies** — each IP in your purchased list is permanently stable. Rotation is a client-side concern: your code picks the next entry from the list. See [Client-side rotation](/products/overview-2/rotation).


# Choose a product

Three products are currently live. Use the decision tree below, then click through for details.

## Decision tree

```
Need to look like a regular consumer in country X?
├── Yes (mobile-only content, social platform)   → Mobile proxies        (gateway, type-mob)
├── Yes, residential, IP stable indefinitely     → ISP proxies           (per-IP list, ip:port:user:pass)
└── Yes, residential, rotating IPs               → Residential proxies   (gateway, type-res)
```

If your use case calls for raw datacenter speed, a fully managed Web Unblocker, or a Scraping API that returns structured JSON — those products are **coming soon**. Email <support@helodata.com> to join the waitlist.

## Comparison matrix

|                             | Residential (`res`)                 | Mobile (`mob`)                      | ISP                                   |
| --------------------------- | ----------------------------------- | ----------------------------------- | ------------------------------------- |
| **Delivery model**          | Gateway, username-encoded           | Gateway, username-encoded           | Per-IP list, `ip:port:user:pass`      |
| **Billing**                 | per GB                              | per GB                              | per IP / month                        |
| **Speed**                   | medium                              | low                                 | high                                  |
| **Anti-bot strength**       | high                                | very high                           | high                                  |
| **Session length (sticky)** | up to 30 min                        | up to 60 min                        | unlimited (IP is permanent)           |
| **Rotation**                | per connection or by sticky session | per connection or by sticky session | client-side; pick next IP from list   |
| **Geo targeting**           | country, state, city — per request  | country, ASN — per request          | country / city — chosen at order time |
| **Pool size**               | shared 100M+ pool                   | shared 10M+ pool                    | the IPs you bought                    |

## Typical use cases

* **E-commerce price monitoring at scale** → Residential rotating, country-targeted
* **Ad verification across many countries** → Residential, short sticky sessions, city-level when relevant
* **Sneaker / drop monitoring** → ISP for the speed and stable session through checkout
* **Social platform research** → Mobile, long sticky sessions, ASN-pinned to a specific carrier
* **SEO rank tracking** → Residential, country + city targeting, rotating

Still unsure? Start a [free trial](/getting-started/free-trial) — you get 100 MB on every live product to test side-by-side.


# Quick start

Send your first proxied request in 5 minutes.

## 1. Get credentials

1. Sign in to the [dashboard](https://dashboard.helodata.com).
2. Open **Residential → Sub-users** and click **Create sub-user**.
3. Copy the sub-user name (e.g. `s1a2b3c4d5e`) and password.

The full proxy username you'll use in requests follows the [helodata username grammar](/getting-started/username-format). For this Quick start, the username is:

```
helo_s1a2b3c4d5e-type-res-region-us
```

Replace `s1a2b3c4d5e` with your actual sub-user name and `PASSWORD` with your password in the examples below.

## 2. Send a request

### curl

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

You should see an IP that is **not** your own — it's a US residential exit node.

### Python (requests)

```python
import requests

USER = "helo_s1a2b3c4d5e-type-res-region-us"
PASS = "PASSWORD"
proxy = f"http://{USER}:{PASS}@gate.helodata.io:7777"

r = requests.get(
    "https://ipv4.icanhazip.com",
    proxies={"http": proxy, "https": proxy},
    timeout=30,
)
print(r.text.strip())
```

### Node.js (fetch + undici)

```js
import { fetch, ProxyAgent } from "undici";

const user = "helo_s1a2b3c4d5e-type-res-region-us";
const pass = "PASSWORD";
const agent = new ProxyAgent(`http://${user}:${pass}@gate.helodata.io:7777`);

const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
console.log((await res.text()).trim());
```

### Go (net/http)

```go
package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "time"
)

func main() {
    proxyURL, _ := url.Parse("http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777")
    client := &http.Client{
        Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
        Timeout:   30 * time.Second,
    }
    resp, err := client.Get("https://ipv4.icanhazip.com")
    if err != nil { panic(err) }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}
```

### PHP (cURL)

```php
<?php
$ch = curl_init('https://ipv4.icanhazip.com');
curl_setopt_array($ch, [
    CURLOPT_PROXY          => 'http://gate.helodata.io:7777',
    CURLOPT_PROXYUSERPWD   => 'helo_s1a2b3c4d5e-type-res-region-us:PASSWORD',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 30,
]);
echo trim(curl_exec($ch));
curl_close($ch);
```

## 3. Target a different country

Change the `region` segment of the username to an ISO-3166 country code:

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-de:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

The response IP now geolocates to Germany. Full list of geo modifiers in [Locations](/products/overview/locations).

## 4. Reuse the same IP

Add `-session-XXX-sesstime-N` to keep the same exit IP for N minutes:

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-de-session-abc123-sesstime-30:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

Run twice — both responses should be the same IP.

## 5. Try a different product

For **Mobile proxies**, swap the `type` segment to `mob`:

```bash
curl -x http://helo_s1a2b3c4d5e-type-mob-region-jp-asn-2516:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

(That example pins KDDI in Japan via ASN 2516.)

For **ISP proxies**, there is no gateway and no username grammar. After purchase, the dashboard gives you a list of static IPs in `ip:port:user:pass` form. Each line is a standalone proxy:

```bash
curl -x http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 \
     https://ipv4.icanhazip.com
```

See [ISP Quick start](/products/overview-2/quick-start) for the full flow.

## What's next

* [Username format](/getting-started/username-format) — every segment, every option
* [Authentication](/products/overview/authentication) — switch from credentials to IP whitelist
* [Sessions & rotation](/products/overview/sessions-and-rotation) — sticky vs rotating strategies
* [Integrations](/integrations/integrations) — Selenium, Puppeteer, Scrapy, etc.
* [Troubleshooting](/troubleshooting/troubleshooting) — if your first request didn't work


# Username format

> **Applies to gateway products only — Residential and Mobile.** ISP proxies use a different credential model: each IP is delivered as a `ip:port:user:pass` line in a downloadable list. See [Proxy list format](/products/overview-2/proxy-list-format) for ISP details.

For the **gateway products**, helodata encodes all targeting parameters — sub-user, product type, geo, ASN, sticky session — into a single proxy **username** string. The password stays the same; only the username changes when you want to target a different country, a different sub-user, or open a sticky session.

## Full grammar

```
helo_{username}-type-{type}-region-{region}[-state-{state}][-city-{city}][-asn-{asn}][-session-{session_id}-sesstime-{ttl}]
```

| Segment                                | Required | Description                                                                                                                                                                                                                              |
| -------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `helo_`                                | yes      | Fixed prefix. Identifies the credential as helodata-format.                                                                                                                                                                              |
| `{username}`                           | yes      | Your sub-user name as shown in **Dashboard → Sub-users** (e.g. `s1a2b3c4d5e`).                                                                                                                                                           |
| `-type-{type}`                         | yes      | Product type. `res` (residential) or `mob` (mobile).                                                                                                                                                                                     |
| `-region-{region}`                     | yes      | ISO-3166 alpha-2 country code, lowercase (e.g. `us`, `de`, `jp`).                                                                                                                                                                        |
| `-state-{state}`                       | optional | State / province code for finer targeting. US states use 2-letter codes (`ca`, `ny`).                                                                                                                                                    |
| `-city-{city}`                         | optional | City slug (lowercase, no spaces — e.g. `la`, `newyork`, `tokyo`).                                                                                                                                                                        |
| `-asn-{asn}`                           | optional | AS number to pin the carrier or ISP (e.g. `2516` for KDDI).                                                                                                                                                                              |
| `-session-{session_id}-sesstime-{ttl}` | optional | Open a sticky session. `{session_id}` is any string you choose (max 32 chars, `[a-z0-9]`); `{ttl}` is the session lifetime in **minutes** (1–30 for residential/ISP, 1–60 for mobile). Both segments must appear together or not at all. |

Segments are concatenated with a single `-` and must appear in the order shown above. Unknown or out-of-order segments cause a `407` at the gateway — see [407 Proxy Authentication Required](/troubleshooting/407).

## Examples

| Goal                                    | Username                                                        |
| --------------------------------------- | --------------------------------------------------------------- |
| Basic proxy (any US residential exit)   | `helo_s1a2b3c4d5e-type-res-region-us`                           |
| US residential, California, Los Angeles | `helo_s1a2b3c4d5e-type-res-region-us-state-ca-city-la`          |
| Mobile, Japan, KDDI (ASN 2516)          | `helo_s1a2b3c4d5e-type-mob-region-jp-asn-2516`                  |
| Sticky residential session, US, 30 min  | `helo_s1a2b3c4d5e-type-res-region-us-session-abc-sesstime-30`   |
| Sticky mobile session, US, 60 min       | `helo_s1a2b3c4d5e-type-mob-region-us-session-job42-sesstime-60` |

## Rotation vs sticky

* **Omit the `session` segment** → every TCP connection gets a fresh exit IP (rotating mode).
* **Include `session-XXX-sesstime-N`** → connections that share the same `session_id` reuse the same exit IP for up to N minutes (sticky mode).

Two requests with the same session\_id but different other parameters will be treated as separate sessions. Treat the full username string — not just the session\_id — as the session key.

## Where the username goes

In a proxy URL:

```
http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777
```

In a `Proxy-Authorization` header (Basic auth, base64 of `username:password`):

```
Proxy-Authorization: Basic aGVsb19zMWEyYjNjNGQ1ZS10eXBlLXJlcy1yZWdpb24tdXM6UEFTU1dPUkQ=
```

## Validating your string

The dashboard ships a [Proxy tester](/resources/proxy-tester) that parses any username and tells you which segments it interpreted. Use it when a request returns an unexpected geo or a 407.


# Free trial

New users can request **100 MB** of trial bandwidth by contacting our support team or a sales representative, so you can benchmark products side-by-side before committing.

## What's included

| Product     | Trial allowance | Validity |
| ----------- | --------------- | -------- |
| Residential | 100 MB          | 7 days   |
| Mobile      | 100 MB          | 7 days   |

Once granted, unused credits expire at the end of the 7-day window and do not roll over.

## How to start

1. Sign up at [helodata.com](https://helodata.com).
2. Verify your email.
3. Contact us to request trial credits:
   * Email **<support@helodata.com>** or **<sales@helodata.com>**
   * Or use the live chat in the [dashboard](https://dashboard.helodata.com)
   * Tell us which products you'd like to test and your use case
4. Once credits are granted, generate credentials and follow the [Quick start](/getting-started/quick-start).

## Restrictions during the trial

* All trial traffic is subject to the [Fair-usage policy](/resources/fair-usage).
* Restricted-target lists are stricter on trial accounts — see each product's restricted targets page.
* Trial accounts cannot create sub-users beyond a single test account.
* Concurrent connections are capped at 25 across all products.

## Upgrading

When you're ready to remove trial limits, open **Billing → Plans** and pick a plan. Existing API credentials keep working — **no code changes needed**. Any unused trial credits are forfeited at upgrade.

## Frequently asked questions

**Can I extend the trial?** Email <support@helodata.com> with your use case; we typically grant a 7-day extension on request.

**Do I need a payment method to start the trial?** No. A payment method is only required when you upgrade or top up.

**Is KYC required for the trial?** No. KYC is not currently mandatory, though we may request verification depending on account risk. See [ID verification](/account-and-billing/kyc).


# Residential proxies

Residential proxies route your requests through real consumer devices on ISP networks worldwide. Target sites see them as ordinary household traffic, making them the most resilient choice against anti-bot defenses.

## At a glance

|                    |                                                    |
| ------------------ | -------------------------------------------------- |
| **Type code**      | `res`                                              |
| **Delivery**       | Gateway, username-encoded                          |
| **Endpoint**       | `gate.helodata.io:7777`                            |
| **Billing**        | Pay per GB                                         |
| **Pool size**      | 100M+ IPs                                          |
| **Geo targeting**  | Country, state, city, ASN                          |
| **Session length** | Rotating (per connection) or sticky (up to 30 min) |
| **Protocols**      | HTTP, HTTPS, SOCKS5                                |
| **Authentication** | User+password or IP whitelist                      |

## When to use residential

* Sites with strong anti-bot defenses (e-commerce, ticketing, social platforms)
* Geo-restricted content that requires a real consumer IP in a specific country
* Ad verification and SERP tracking that must blend with real user traffic
* LLM data pipelines that need fresh, unblocked web pages

If your target is unprotected, [Datacenter proxies](/products/overview-3) (coming soon) are cheaper. If you need the same IP for hours or days, [ISP proxies](/products/overview-2) are a better fit.

## Username grammar

```
helo_{username}-type-res-region-{region}[-state-{state}][-city-{city}][-asn-{asn}][-session-{session_id}-sesstime-{ttl}]
```

Full reference: [Username format](/getting-started/username-format).

## Five-second example

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

## On this page

* [Quick start](/products/overview/quick-start) — first request in five minutes
* [Authentication](/products/overview/authentication) — password vs IP whitelist
* [Endpoints & ports](/products/overview/endpoints-and-ports) — host, port, TLS
* [Locations](/products/overview/locations) — countries, states, cities
* [Protocols](/products/overview/protocols) — HTTP / HTTPS / SOCKS5
* [Sessions & rotation](/products/overview/sessions-and-rotation) — sticky vs rotating
* [ASN targeting](/products/overview/asn-targeting) — pin a specific ISP
* [Restricted targets](/products/overview/restricted-targets) — sites we don't route to
* [Error definitions](/products/overview/error-definitions) — what each status code means
* [Statistics](/products/overview/statistics) — dashboards and the traffic API


# Quick start

Send your first residential proxy request in 5 minutes.

## 1. Create a sub-user

1. Open the [dashboard](https://dashboard.helodata.com) → **Residential → Sub-users**.
2. Click **Create sub-user**, name it (e.g. `acme1`), and pick a password.
3. The dashboard issues a sub-user name like `s1a2b3c4d5e`. Copy both values.

## 2. Build your username

For the simplest case — any US residential IP — your proxy username is:

```
helo_s1a2b3c4d5e-type-res-region-us
```

Substitute your real sub-user where the placeholder shows `s1a2b3c4d5e`. See [Username format](/getting-started/username-format) for every available segment.

## 3. Send the request

### curl

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

### Python (requests)

```python
import requests

USER = "helo_s1a2b3c4d5e-type-res-region-us"
PASS = "PASSWORD"
proxy = f"http://{USER}:{PASS}@gate.helodata.io:7777"

r = requests.get(
    "https://ipv4.icanhazip.com",
    proxies={"http": proxy, "https": proxy},
    timeout=30,
)
print(r.text.strip())
```

### Node.js (undici)

```js
import { fetch, ProxyAgent } from "undici";

const user = "helo_s1a2b3c4d5e-type-res-region-us";
const pass = "PASSWORD";
const agent = new ProxyAgent(`http://${user}:${pass}@gate.helodata.io:7777`);

const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
console.log((await res.text()).trim());
```

You should see a US residential IP — not your own.

## 4. Common next steps

| Goal                            | Add to username                        |
| ------------------------------- | -------------------------------------- |
| Target Germany                  | replace `-region-us` with `-region-de` |
| Drill down to California, LA    | append `-state-ca-city-la`             |
| Keep the same IP for 10 minutes | append `-session-job1-sesstime-10`     |
| Pin a specific ISP              | append `-asn-7922` (Comcast example)   |

Combine freely — order matters; see [Username format](/getting-started/username-format).

## 5. Troubleshooting the first call

* **407 Proxy Authentication Required** — wrong username, password, or non-whitelisted source IP. See [407](/troubleshooting/407).
* **522 Connection timed out** — upstream IP was unreachable; retry. See [522](/troubleshooting/522).
* **SSL handshake errors** — your client may need an updated CA bundle. See [SSL/TLS errors](/troubleshooting/ssl-errors).

## What's next

* [Authentication](/products/overview/authentication) — switch to IP whitelist for keyless environments
* [Sessions & rotation](/products/overview/sessions-and-rotation) — sticky vs rotating strategies
* [Integrations](/integrations/integrations) — Selenium, Puppeteer, Scrapy, antidetect browsers


# Authentication

helodata residential proxies support two authentication models. You can switch between them per sub-user — they are not mutually exclusive at the account level.

## Model A — User + password (default)

Pass the proxy username and password with the request. The username carries all targeting parameters; the password is the static value from the sub-user.

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     https://example.com
```

Behind the scenes the client encodes `username:password` as Base64 and sends:

```
Proxy-Authorization: Basic aGVsb19zMWEyYjNjNGQ1ZS10eXBlLXJlcy1yZWdpb24tdXM6UEFTU1dPUkQ=
```

**Pros**

* Works from any source IP — no infrastructure setup
* Targeting changes per request (just change the username)
* Easy to scope per-customer with separate sub-users

**Cons**

* Credentials sit in your code or config — protect accordingly

### Where to put the credentials

* **URL form** `http://user:pass@host:port` — most HTTP clients understand it
* **`Proxy-Authorization` header** — best when the URL form is awkward (e.g. browser CDPs)
* **Environment variables** `HTTP_PROXY=http://user:pass@host:port HTTPS_PROXY=...` — works for curl, wget, requests, and most Unix tools

## Model B — IP whitelist

Register your egress IPs in the dashboard. Requests from those IPs are accepted with no `Proxy-Authorization` header. The sub-user is still required in the username so targeting and billing work.

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-us@gate.helodata.io:7777 \
     https://example.com
```

To enable:

1. Open **Dashboard → Residential → {Sub-user} → Whitelisted IPs**.
2. Add up to 50 IPv4 addresses or CIDR ranges per sub-user.
3. Changes take effect within 60 seconds.

**Pros**

* No secret to leak
* Cleaner code (no inline credentials)

**Cons**

* Useless for clients with dynamic egress (laptops, mobile, ephemeral CI)
* Targeting still has to be encoded in the username, so it's not fully credential-free

## Choosing between them

| Scenario                                     | Recommended                 |
| -------------------------------------------- | --------------------------- |
| Server with static egress IP                 | IP whitelist                |
| Container in autoscaling group (variable IP) | User + password             |
| Local development from a laptop              | User + password             |
| Headless browser farm behind a NAT           | IP whitelist for the NAT IP |

You can use both: whitelist your production servers and keep username/password active for dev machines.

## Rotating credentials

To rotate a sub-user password:

1. **Dashboard → Residential → {Sub-user} → Reset password**.
2. The old password keeps working for 60 seconds, then begins returning `407`.
3. Update your deployment.

## API

Whitelisted IPs and sub-user credentials are also managed via the [Whitelists API](/api-reference/whitelists) and [Sub-users API](/api-reference/sub-users).


# Endpoints & ports

## Production gateway

| Field                        | Value                           |
| ---------------------------- | ------------------------------- |
| Host                         | `gate.helodata.io`              |
| Port (HTTP / HTTPS / SOCKS5) | `7777`                          |
| Authentication port          | same — `7777` for all protocols |

A single host:port handles all three protocols. The protocol is auto-detected from the client handshake.

## Regional gateways (optional)

Latency-sensitive workloads can connect to a regional gateway that's closer to your servers. The pool of upstream IPs is identical; only the **first hop** changes.

| Region                   | Host                  |
| ------------------------ | --------------------- |
| Global anycast (default) | `gate.helodata.io`    |
| North America            | `na.gate.helodata.io` |
| Europe                   | `eu.gate.helodata.io` |
| Asia-Pacific             | `ap.gate.helodata.io` |

Use a regional gateway only when you measure a meaningful improvement; for most users the anycast host is faster because it routes to the nearest POP automatically.

## TLS

* Outbound TLS to the target site (HTTPS) is **always** end-to-end — helodata does not terminate it.
* Connecting to the gateway over TLS (wrapping the proxy hop itself) is supported on the same port `7777` — the gateway auto-detects plain vs TLS from the client handshake. Use it when your environment requires TLS-only egress.

## Connection limits

| Limit                               | Default   | Raise via              |
| ----------------------------------- | --------- | ---------------------- |
| Concurrent connections per sub-user | 600       | Dashboard plan upgrade |
| Requests per second per sub-user    | 1000      | Dashboard plan upgrade |
| Max request body                    | 100 MB    | Email support          |
| Max response body                   | unlimited | —                      |
| Idle timeout                        | 120 s     | —                      |

Trial accounts are capped at 25 concurrent connections.

## DNS

Resolve `gate.helodata.io` via your normal resolver. The record returns both IPv4 and IPv6; the gateway accepts either.

If your network blocks public DNS, the dashboard ships a static A/AAAA reference under **Settings → Network info**.

## Health check

A `GET /healthz` against the gateway returns `200 ok` and does **not** consume traffic credits:

```bash
curl https://gate.helodata.io:7777/healthz
```

Use it from monitoring jobs to verify the gateway is reachable without burning bandwidth.


# Locations

helodata residential proxies cover **195+ countries** with state and city-level targeting in the larger markets. All three geo segments — `region`, `state`, `city` — go into the proxy username.

## Country (region)

Always required. Use the lowercase **ISO-3166 alpha-2** code as the `region` value.

```
helo_s1a2b3c4d5e-type-res-region-{country}
```

Examples:

| Country        | Code | Example                               |
| -------------- | ---- | ------------------------------------- |
| United States  | `us` | `helo_s1a2b3c4d5e-type-res-region-us` |
| United Kingdom | `gb` | `helo_s1a2b3c4d5e-type-res-region-gb` |
| Germany        | `de` | `helo_s1a2b3c4d5e-type-res-region-de` |
| Japan          | `jp` | `helo_s1a2b3c4d5e-type-res-region-jp` |
| Brazil         | `br` | `helo_s1a2b3c4d5e-type-res-region-br` |
| India          | `in` | `helo_s1a2b3c4d5e-type-res-region-in` |

The full live list is exposed at:

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/residential/locations
```

## State / province

Optional. Adds finer targeting in countries that expose subdivisions. Use the lowercase **ISO-3166-2** suffix (the part after the dash).

| Country   | State code style | Example                                  |
| --------- | ---------------- | ---------------------------------------- |
| US        | 2-letter         | `state-ca`, `state-ny`, `state-tx`       |
| Canada    | 2-letter         | `state-on`, `state-bc`                   |
| Australia | 2–3 letter       | `state-nsw`, `state-vic`                 |
| Brazil    | 2-letter         | `state-sp`, `state-rj`                   |
| Germany   | 2-letter         | `state-by` (Bayern), `state-be` (Berlin) |

```
helo_s1a2b3c4d5e-type-res-region-us-state-ca
```

## City

Optional, requires `state` to be specified. City slugs are lowercase, no spaces, no diacritics.

| City          | Slug       |
| ------------- | ---------- |
| Los Angeles   | `la`       |
| New York      | `newyork`  |
| San Francisco | `sf`       |
| Toronto       | `toronto`  |
| London        | `london`   |
| Tokyo         | `tokyo`    |
| São Paulo     | `saopaulo` |

```
helo_s1a2b3c4d5e-type-res-region-us-state-ca-city-la
```

Not every city in every country has enough live IPs to be targetable — if a city has fewer than 50 active exits, the gateway falls back to state-level and emits a warning header `X-Helodata-Fallback: city`.

## Continent / region groups

If you don't care about the specific country but want to stay in a region, use the meta codes:

| Meta code      | Includes                                  |
| -------------- | ----------------------------------------- |
| `region-eu`    | EU member states                          |
| `region-eea`   | EEA (EU + Norway, Iceland, Liechtenstein) |
| `region-latam` | Mexico, Central & South America           |
| `region-mena`  | Middle East and North Africa              |
| `region-apac`  | Asia-Pacific                              |

These cannot be combined with `state` or `city`.

## How geo is resolved

1. helodata holds a per-IP geo database refreshed daily from MaxMind + carrier feeds.
2. When a request arrives, the gateway filters live IPs matching your geo segments.
3. From that filtered set, an IP is chosen by load balancer (rotating) or by session key (sticky).
4. The exit IP appears in your response, e.g. `X-Helodata-Exit-IP: 198.51.100.42` and `X-Helodata-Exit-Geo: us/ca/la`.

If your request gets routed to an IP whose live geo turned out to mismatch (e.g. user moved), it counts against the [IP location mismatch](/troubleshooting/ip-location-mismatch) tolerance.

## Coverage at a glance

| Coverage | Countries | State-level | City-level |
| -------- | --------- | ----------- | ---------- |
| Live IPs | 195+      | 60+         | 800+       |

Tier-1 markets (US, UK, DE, FR, JP, KR, BR, AU, CA) have the deepest pools and the broadest city support.


# Protocols

The residential gateway speaks HTTP, HTTPS, and SOCKS5 on the same port. The protocol is detected from the client handshake — you do not configure it server-side.

## HTTP / HTTPS

The most widely supported. Both `http://` and `https://` targets work through the same connection.

```bash
# HTTP target
curl -x http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     http://example.com

# HTTPS target (TLS to target is end-to-end)
curl -x http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     https://example.com
```

For HTTPS targets the client sends `CONNECT example.com:443` to the proxy, which opens a TCP tunnel. helodata never sees the request body.

## SOCKS5

SOCKS5 is required for non-HTTP traffic (raw TCP, some DB clients, custom protocols) and is preferred by some scraping stacks for slightly lower overhead.

```bash
curl --socks5-hostname helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

`--socks5-hostname` (not `--socks5`) makes the proxy do the DNS lookup, which is what you want for geo-targeting to work correctly — otherwise the client resolves locally and leaks DNS.

### Python SOCKS5

```python
import requests

USER = "helo_s1a2b3c4d5e-type-res-region-us"
PASS = "PASSWORD"
proxy = f"socks5h://{USER}:{PASS}@gate.helodata.io:7777"

r = requests.get(
    "https://ipv4.icanhazip.com",
    proxies={"http": proxy, "https": proxy},
    timeout=30,
)
```

Note `socks5h://` (with the `h`) — equivalent to `--socks5-hostname`, forces remote DNS.

### Node.js SOCKS5

```js
import { SocksProxyAgent } from "socks-proxy-agent";
import fetch from "node-fetch";

const agent = new SocksProxyAgent(
  "socks5://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
);
const res = await fetch("https://ipv4.icanhazip.com", { agent });
```

## When to prefer which

| Use case                                             | Pick                                                  |
| ---------------------------------------------------- | ----------------------------------------------------- |
| Standard HTTP/HTTPS scraping                         | HTTP/HTTPS                                            |
| Browser automation (Selenium, Playwright, Puppeteer) | HTTP/HTTPS                                            |
| Raw TCP, MQTT, custom protocols                      | SOCKS5                                                |
| Maximum throughput, minimum overhead                 | SOCKS5                                                |
| Easiest debugging                                    | HTTP/HTTPS — works with `mitmproxy`, browser devtools |

## What we do not support

* **SOCKS4 / SOCKS4a** — use SOCKS5
* **HTTP/3 (QUIC)** to the gateway — the target site can still serve HTTP/3 over the tunnel
* **Plain (non-TLS) authentication over the public internet** — credentials are always Base64-encoded by clients, but for sensitive workloads connect to the gateway over TLS (same port `7777`; gateway auto-detects)

## Common pitfalls

* **DNS leak in SOCKS5** — always use `socks5h://` / `--socks5-hostname` so the proxy resolves the target
* **HTTP-only library forced to talk HTTPS** — if your client requires `http://` proxy URL even for HTTPS targets, that's normal; only the target URL is HTTPS
* **`curl` ignoring `HTTPS_PROXY`** — make sure both `HTTP_PROXY` and `HTTPS_PROXY` are set, lowercase variants too on some Unixes


# Sessions & rotation

A "session" is a way to keep the same residential exit IP across multiple requests. helodata supports two modes; you pick per request via the username.

## Rotating mode (default)

Omit the `session` and `sesstime` segments. Every new TCP connection picks a fresh IP from the matching pool.

```
helo_s1a2b3c4d5e-type-res-region-us
```

When to use:

* One-off page fetches that don't need cookies
* Anti-bot evasion via IP diversity
* Maximum pool diversity for large parallel crawls

Caveat: connection reuse (keep-alive) within a single client may pin you to one IP until the connection closes. If you want literal-per-request rotation, force fresh connections (`Connection: close`).

## Sticky mode

Append both `session-{id}` and `sesstime-{minutes}`. Requests sharing the same combined username re-use the same exit IP for up to `sesstime` minutes.

```
helo_s1a2b3c4d5e-type-res-region-us-session-job1-sesstime-30
```

| Constraint                   | Value                                                            |
| ---------------------------- | ---------------------------------------------------------------- |
| Max `sesstime` (residential) | 30 minutes                                                       |
| Session ID character set     | `[a-z0-9]`, max 32 chars                                         |
| IP guarantee                 | best-effort — sessions can be re-pinned if the upstream IP drops |
| Session expiry               | first request starts the timer                                   |

When to use:

* Multi-step flows (login → search → checkout)
* Sites that bind anti-bot signals to IP across a few minutes
* Tests where you need response reproducibility

## Concurrency under sticky

Two requests with the same `session_id` but **different** other parameters (different geo, different sub-user) are treated as **separate sessions**. The whole username string is the session key, not just the ID.

So this:

```
helo_s1a2b3c4d5e-type-res-region-us-session-abc-sesstime-30
helo_s1a2b3c4d5e-type-res-region-de-session-abc-sesstime-30
```

…opens two unrelated sessions, one in the US and one in DE.

## Recovering from a dropped session

If your session's upstream IP drops mid-flight, the next request with the same username returns a fresh IP and the timer restarts. You'll see this in the response header:

```
X-Helodata-Session-Repinned: true
```

Watch for this header in long-running flows and consider re-establishing client state (cookies, fingerprint) when it appears.

## Patterns

### Pool of N concurrent sessions

```python
import requests, itertools, random, string

BASE = "helo_s1a2b3c4d5e-type-res-region-us"

def session_user(n):
    sid = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
    return f"{BASE}-session-{sid}-sesstime-10"

users = [session_user(i) for i in range(20)]      # 20 sticky sessions

def get(url, user):
    proxy = f"http://{user}:PASSWORD@gate.helodata.io:7777"
    return requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
```

Rotate `users` round-robin across your workers.

### Burn-and-replace

Track failed requests per session ID. When error rate per session crosses a threshold (e.g. 3 failures in 60s), discard that session ID and mint a new one.

## What sessions do **not** do

* They don't extend traffic credits — bandwidth is still billed per byte.
* They don't carry cookies, headers, or TLS fingerprints — that's your client's job.
* They don't guarantee a specific IP — only IP **stability** for the session window.

## Related

* [ASN targeting](/products/overview/asn-targeting) — combine with sessions to keep the same ISP throughout
* [Locations](/products/overview/locations) — geo segments must match across requests to share a session
* [407 errors](/troubleshooting/407) — common if session syntax is malformed


# ASN targeting

ASN (Autonomous System Number) targeting lets you pin an exit IP to a specific ISP — useful when a target site treats certain carriers differently, or when you want IPs that share a network reputation.

## How to specify

Append `-asn-{number}` to the username. The ASN is a decimal integer with no `AS` prefix.

```
helo_s1a2b3c4d5e-type-res-region-us-asn-7922
```

Common US residential ASNs:

| ASN   | ISP                |
| ----- | ------------------ |
| 7922  | Comcast            |
| 22773 | Cox                |
| 7018  | AT\&T              |
| 11427 | Charter / Spectrum |
| 20115 | Verizon            |
| 209   | CenturyLink        |

For other countries, look up the carrier on a public registry like bgp.tools or query our location API:

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/asns?region=de"
```

## Combining with other parameters

ASN narrows the pool **after** country/state/city filtering. Compatible combinations:

```
helo_s1a2b3c4d5e-type-res-region-us-asn-7922                          # US, Comcast
helo_s1a2b3c4d5e-type-res-region-us-state-ca-asn-7922                 # California Comcast only
helo_s1a2b3c4d5e-type-res-region-us-state-ca-city-la-asn-7922         # LA-area Comcast
helo_s1a2b3c4d5e-type-res-region-us-asn-7922-session-x-sesstime-15    # Sticky Comcast session
```

If the combined filter produces fewer than 10 live IPs, the gateway returns a `522` with response header `X-Helodata-Insufficient-Pool: true` rather than fall back silently. Loosen the filter (drop `city`, then `state`, then `asn`) to find a viable combination.

## When to use ASN targeting

* **Carrier-locked content** — services that geo-restrict by ISP (some streaming, banking, telco portals)
* **Detection bypass** — sites that score carrier diversity as a bot signal
* **Reproducibility** — pinning the same carrier across a long-running test
* **Quality control** — some residential pools are higher-quality on premium carriers

## When **not** to use ASN

* General scraping where carrier diversity is a *feature*, not a bug — leave ASN unset
* Very small markets — even tier-1 ASNs may have <100 live IPs at a moment
* Combined with `city` in non-tier-1 countries — almost always insufficient pool

## Inspecting the chosen ASN

The gateway echoes the exit IP's ASN back in a response header:

```
X-Helodata-Exit-ASN: 7922
X-Helodata-Exit-ASN-Name: Comcast Cable Communications, LLC
```

Use this to verify your targeting and to debug `IP location mismatch` errors when the ASN's geo doesn't match the city you requested.

## Mobile vs residential ASNs

ASNs in the residential pool belong to fixed-line ISPs. For mobile carrier ASNs (Verizon Wireless, KDDI, Vodafone…), use the [Mobile proxies](/products/overview-1/asn-targeting) `type-mob` product.


# Restricted targets

helodata blocks a small set of destinations at the gateway. Requests to a restricted target return `403 Forbidden` from the gateway (not the target) with header `X-Helodata-Block-Reason`.

## Permanently blocked

These categories are blocked **on every account** with no exception path:

* **Child sexual abuse material (CSAM)** and any domain on the IWF or NCMEC lists
* **Sanctioned-jurisdiction services** as required by US, EU, and UK sanctions law
* **Distribution of malware, ransomware, or exploit kits**
* **Phishing kits and credential-harvesting infrastructure**
* **DDoS-for-hire services**
* **Government identity-fraud services** (forged passports, driver's licenses)

If you operate threat-intel research that legitimately needs to fetch from blocked categories, contact <security@helodata.com> — we can route specific domains through an audited research channel after a compliance review.

## Restricted by default (per-account exception possible)

Blocked unless you explicitly request an exception in writing:

* **Major banking and brokerage logins** (account-aggregation use cases)
* **Healthcare patient portals** (HIPAA scope)
* **Government tax-filing portals**
* **Streaming services' premium endpoints** (when ToS forbids automated access)
* **Sneaker drops and ticket-resale sites** *during* drop windows on Trial plans

To request an exception:

1. Email <support@helodata.com> with subject `Restricted-target exception: {your domain list}`.
2. Include your sub-user, the domains, and a brief description of the use case.
3. Expect a response within 2 business days. Exceptions are scoped to specific sub-users.

## Trial-only restrictions

Trial accounts have stricter limits — these additional categories are blocked until you upgrade:

* All social media DMs and posting endpoints (read-only public pages are fine)
* Account-creation flows on any major site
* Payment-processor (Stripe, PayPal, Adyen) test or live endpoints

## Operational blocks (temporary)

The gateway also enforces short-lived blocks for operational reasons:

* A target whose abuse complaints exceed our threshold gets a 24–72h cool-off
* A target with active legal action against helodata gets a block until resolved
* A target experiencing an outage that's causing high error rates may get a 5-minute back-off

Operational blocks are listed live in **Dashboard → Status → Active blocks**.

## Detecting a block

```
HTTP/1.1 403 Forbidden
X-Helodata-Block-Reason: restricted-category-banking
X-Helodata-Block-Detail: account-aggregation requires per-account exception
X-Helodata-Block-Contact: support@helodata.com
```

Blocked requests do **not** consume traffic credits.

## Reporting abuse

If you receive an abuse complaint involving helodata IPs, forward it to <abuse@helodata.com> with the offending IP, timestamp (UTC), and full headers. We respond to abuse reports within 1 business day.


# Error definitions

The gateway returns errors with the HTTP status code, a numeric **helodata code**, and a `X-Helodata-Error-Code` header that pinpoints the exact cause. Pair them before retrying.

## Gateway-originated errors (helodata layer)

| Status | helodata code | `X-Helodata-Error-Code` | Meaning                                                                          | What to do                                                   |
| ------ | ------------- | ----------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `400`  | `20002`       | `bad-username`          | Username doesn't match the grammar                                               | Re-check [Username format](/getting-started/username-format) |
| `400`  | `20002`       | `unknown-region`        | Region code not recognized                                                       | Use ISO-3166 alpha-2                                         |
| `400`  | `20002`       | `state-without-region`  | `state` segment without `region`                                                 | Add `region`                                                 |
| `400`  | `20002`       | `city-without-state`    | `city` segment without `state`                                                   | Add `state`                                                  |
| `400`  | `20002`       | `session-incomplete`    | `session` without `sesstime` (or vice versa)                                     | Always pair them                                             |
| `400`  | `20002`       | `sesstime-out-of-range` | TTL > 30 (residential)                                                           | Reduce `sesstime`                                            |
| `401`  | `20003`       | `unknown-subuser`       | Sub-user doesn't exist                                                           | Check dashboard                                              |
| `403`  | —             | `restricted-category-*` | Target blocked — see [Restricted targets](/products/overview/restricted-targets) | Request exception                                            |
| `403`  | —             | `account-suspended`     | Account suspended for billing or abuse                                           | Contact support                                              |
| `407`  | `20003`       | `auth-failed`           | Bad password                                                                     | Verify, then rotate if leaked                                |
| `407`  | `20003`       | `not-whitelisted`       | Source IP not in whitelist                                                       | Add IP in dashboard                                          |
| `429`  | `30002`       | `concurrent-limit`      | Too many concurrent connections                                                  | Throttle client or upgrade plan                              |
| `429`  | `30002`       | `rps-limit`             | Too many requests per second                                                     | Backoff and retry with jitter                                |
| `502`  | `40001`       | `pool-empty`            | No matching IPs in pool                                                          | Loosen geo/ASN filter                                        |
| `502`  | `40001`       | `upstream-tls-error`    | Target TLS handshake failed                                                      | See [SSL errors](/troubleshooting/ssl-errors)                |
| `522`  | —             | `upstream-timeout`      | Backend IP unreachable                                                           | Retry — usually transient                                    |
| `522`  | —             | `insufficient-pool`     | Combined filter has <10 IPs                                                      | Loosen filter                                                |
| `524`  | —             | `target-timeout`        | Target site didn't respond in time                                               | Retry or extend client timeout                               |

## Target-originated errors (pass-through)

When the target site responds with an error, helodata passes it through unchanged. The presence of these headers tells you the proxy succeeded; the issue is downstream:

```
X-Helodata-Exit-IP: 198.51.100.42
X-Helodata-Exit-Geo: us/ca/la
X-Helodata-Exit-ASN: 7922
```

If you see those headers alongside e.g. a `403` from the target, the target itself is rejecting the IP — not helodata.

## Common pass-through statuses to expect

| Status                 | Common cause                     | Mitigation                                      |
| ---------------------- | -------------------------------- | ----------------------------------------------- |
| `403` from target      | Anti-bot system rejected this IP | Rotate IP, add headers, slow down               |
| `429` from target      | Rate-limited by target           | Slow down per-IP, increase rotation             |
| `503` from target      | Target overloaded                | Retry with backoff                              |
| Cloudflare `1020` page | Geo or fingerprint failed        | Add headers, use sticky session, switch country |

## Retry strategy

Build retries from this table — different codes need different responses:

```python
RETRYABLE_GATEWAY = {429, 502, 522, 524}

def should_retry(status, err_code):
    if status in (400, 401, 403, 407):
        return False                          # config issue, no point retrying
    if status in RETRYABLE_GATEWAY:
        return True
    return False
```

For 429s with `rps-limit`, jitter for 1–5 seconds. For `pool-empty`, retry only after broadening the username filter.

## Logging recommendation

Log the trio `status`, `X-Helodata-Error-Code`, `X-Helodata-Exit-IP` on every request. It's enough to answer most "what went wrong" questions without re-running the call.


# Statistics

You can read residential traffic and request data in three places: the dashboard, the REST API, and per-response headers.

## Dashboard

**Dashboard → Residential → Statistics** shows:

* **Traffic over time** — bytes per minute / hour / day, stacked by sub-user
* **Top destinations** — domains by traffic and request count
* **Top countries** — exit IP geos used
* **Error breakdown** — by HTTP status and `X-Helodata-Error-Code`
* **Session count** — concurrent sticky sessions
* **Concurrency** — peak parallel connections

Filter by sub-user, by date range, and by tag (if you set `X-Helodata-Tag` on requests — see below).

Data refreshes every minute. Granularity downsamples after 14 days (1-min → 5-min) and 90 days (5-min → 1-hour).

## REST API

For programmatic access — useful for billing pipelines, custom dashboards, and alerting.

### Get aggregate traffic

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/traffic?from=2026-05-01&to=2026-05-28&group_by=subuser"
```

Response:

```json
{
  "from": "2026-05-01T00:00:00Z",
  "to":   "2026-05-28T00:00:00Z",
  "rows": [
    { "subuser": "s1a2b3c4d5e", "bytes_in": 12345678, "bytes_out": 9876543, "requests": 4210 },
    { "subuser": "s9z8y7x6w5v", "bytes_in": 222345,   "bytes_out": 134567,  "requests": 88 }
  ]
}
```

`group_by` accepts `subuser`, `region`, `asn`, `tag`, or `day`.

### Get per-request log (sample)

For debugging only — costs API quota. Returns the last 1,000 sampled requests.

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/requests?subuser=s1a2b3c4d5e&limit=100"
```

Each entry includes status, exit IP, geo, ASN, bytes, and your tag.

Full schema: [Traffic & usage](/api-reference/traffic).

## Per-response headers

Every successful request includes these usage hints:

```
X-Helodata-Bytes-In: 1248
X-Helodata-Bytes-Out: 38745
X-Helodata-Exit-IP: 198.51.100.42
X-Helodata-Exit-Geo: us/ca/la
X-Helodata-Exit-ASN: 7922
X-Helodata-Request-Id: req_01HX2K3PQ4M5
```

Use `X-Helodata-Request-Id` when contacting support — we can pull the full request record from it.

## Tagging requests

To slice analytics by your own categories (campaign, customer, job), send a `X-Helodata-Tag` request header (max 64 chars, `[a-z0-9_-]`):

```bash
curl -x http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777 \
     -H "X-Helodata-Tag: campaign-acme-q2" \
     https://example.com
```

Tags appear in dashboard filters and as a `group_by` value in the traffic API.

## Billing reconciliation

Bandwidth shown in the dashboard is what you're billed for. To reconcile against your own logs:

1. Pull the traffic API at day granularity for the billing period.
2. Sum `bytes_in + bytes_out` across sub-users.
3. Match against the invoice line on **Billing → Invoices**.

Discrepancies > 1% are rare; if you see one, open a support ticket with the `X-Helodata-Request-Id` range and we'll investigate.

## Alerts

Set up usage alerts under **Dashboard → Alerts**:

* Daily traffic exceeds X GB
* Error rate exceeds Y% over Z minutes
* Sub-user concurrency near plan limit

Alerts deliver via email and optional Slack webhook.


# Mobile proxies

Mobile proxies route your requests through real 3G/4G/5G devices on carrier networks. Target sites see them as ordinary phones on cellular data — the highest-trust signal on the public internet.

## At a glance

|                    |                                                                             |
| ------------------ | --------------------------------------------------------------------------- |
| **Type code**      | `mob`                                                                       |
| **Delivery**       | Gateway, username-encoded                                                   |
| **Endpoint**       | `gate.helodata.io:7777`                                                     |
| **Billing**        | Pay per GB                                                                  |
| **Pool size**      | 10M+ mobile IPs                                                             |
| **Geo targeting**  | Country, ASN (carrier) — most countries do not expose state/city for mobile |
| **Session length** | Rotating (per connection) or sticky (up to 60 min)                          |
| **Protocols**      | HTTP, HTTPS, SOCKS5                                                         |
| **Authentication** | User+password or IP whitelist                                               |
| **KYC required**   | Not by default (may be requested based on risk)                             |

## When to use mobile

* Sites that score mobile-vs-desktop or carrier IP as a strong trust signal
* Social platforms (DM flows, account integrity checks)
* Apps and APIs that gate features behind carrier networks
* Anti-bot bypass when residential is still being detected

Mobile is the **slowest** and **most expensive** of the helodata products. Use it where residential has already failed — not as a default.

## Username grammar

```
helo_{username}-type-mob-region-{region}[-asn-{asn}][-session-{session_id}-sesstime-{ttl}]
```

`state` and `city` are accepted but, for most countries, the resulting pool is too small to be useful. Carrier targeting via `asn` is the primary geo refinement on mobile.

Full reference: [Username format](/getting-started/username-format).

## Five-second example

```bash
curl -x http://helo_s1a2b3c4d5e-type-mob-region-jp-asn-2516:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

(That username pins KDDI in Japan.)

## On this page

* [Quick start](/products/overview-1/quick-start) — first request in five minutes
* [Authentication](/products/overview-1/authentication) — password vs IP whitelist
* [Endpoints & ports](/products/overview-1/endpoints-and-ports) — host, port, TLS
* [Locations & carriers](/products/overview-1/locations) — country coverage and major carriers
* [Protocols](/products/overview-1/protocols) — HTTP / HTTPS / SOCKS5
* [Sessions & rotation](/products/overview-1/sessions-and-rotation) — sticky up to 60 minutes
* [ASN targeting](/products/overview-1/asn-targeting) — the most important mobile filter
* [Error definitions](/products/overview-1/error-definitions) — what each status code means
* [Statistics](/products/overview-1/statistics) — dashboards and traffic API


# Quick start

Send your first mobile proxy request in 5 minutes. The flow mirrors residential — the main difference is the `type` segment.

No KYC is required to start. We may request verification later if your account triggers a risk review (see [ID verification](/account-and-billing/kyc)); if that happens, requests can return `403` with `X-Helodata-Error-Code: kyc-required` until you complete it.

## 1. Create a mobile sub-user

1. Open **Dashboard → Mobile → Sub-users → Create sub-user**.
2. Name it and pick a password.
3. Copy the issued sub-user name (e.g. `s1a2b3c4d5e`).

Mobile sub-users are billed and rate-limited separately from residential sub-users.

## 2. Build your username

Simplest case — any US mobile IP:

```
helo_s1a2b3c4d5e-type-mob-region-us
```

To pin a specific carrier, append the ASN — strongly recommended on mobile, see [ASN targeting](/products/overview-1/asn-targeting):

```
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167   # Verizon Wireless
```

## 3. Send the request

### curl

```bash
curl -x http://helo_s1a2b3c4d5e-type-mob-region-us:PASSWORD@gate.helodata.io:7777 \
     https://ipv4.icanhazip.com
```

### Python (requests)

```python
import requests

USER = "helo_s1a2b3c4d5e-type-mob-region-us-asn-6167"
PASS = "PASSWORD"
proxy = f"http://{USER}:{PASS}@gate.helodata.io:7777"

r = requests.get(
    "https://ipv4.icanhazip.com",
    proxies={"http": proxy, "https": proxy},
    timeout=60,                                    # mobile is slower — bump the timeout
)
print(r.text.strip())
```

### Node.js (undici)

```js
import { fetch, ProxyAgent } from "undici";

const user = "helo_s1a2b3c4d5e-type-mob-region-us-asn-6167";
const pass = "PASSWORD";
const agent = new ProxyAgent(`http://${user}:${pass}@gate.helodata.io:7777`);

const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
console.log((await res.text()).trim());
```

Expect \~1–3 seconds latency on the first hop — mobile networks are slower than wired. Bump your client timeout to at least 30s.

## 4. Common next steps

| Goal                          | Add to username                                       |
| ----------------------------- | ----------------------------------------------------- |
| Pin a carrier                 | `-asn-2516` (KDDI), `-asn-6167` (Verizon Wireless), … |
| Keep the same IP up to 1 hour | `-session-job1-sesstime-60`                           |
| Switch country                | replace `-region-us`                                  |

## What's next

* [ASN targeting](/products/overview-1/asn-targeting) — recommended for every mobile workload
* [Sessions & rotation](/products/overview-1/sessions-and-rotation) — mobile sticky sessions go up to 60 min
* [ID verification](/account-and-billing/kyc) — not required to start; risk-based only


# Authentication

Mobile uses the same two authentication models as residential — pick whichever fits your runtime. The mechanics are identical; only the `type` segment in the username changes.

## Model A — User + password (default)

```bash
curl -x http://helo_s1a2b3c4d5e-type-mob-region-us:PASSWORD@gate.helodata.io:7777 \
     https://example.com
```

The password is the mobile sub-user password. Mobile and residential sub-users have independent passwords even if the names look similar.

## Model B — IP whitelist

```bash
curl -x http://helo_s1a2b3c4d5e-type-mob-region-us@gate.helodata.io:7777 \
     https://example.com
```

To register IPs: **Dashboard → Mobile → {Sub-user} → Whitelisted IPs**. Up to 50 IPv4 addresses or CIDR ranges per sub-user. Changes take effect within 60 seconds.

> Note: residential and mobile maintain **separate whitelists**. Adding an IP to your residential sub-user does not authorize it for mobile.

## Mobile-specific: risk-based KYC

KYC is **not required by default**. If your account is flagged for a risk review, requests may return the following until verification is completed:

```
HTTP/1.1 403 Forbidden
X-Helodata-Error-Code: kyc-required
```

This is the exception, not the norm. See [ID verification (KYC)](/account-and-billing/kyc).

## Choosing between them

The same trade-offs as residential — see [Residential authentication](/products/overview/authentication#choosing-between-them). The mobile-specific consideration: if your client is a mobile device or NAT egress that itself uses cellular/dynamic IPs, IP whitelisting is impractical; default to user+password.

## Rotating credentials

To rotate a mobile sub-user password:

1. **Dashboard → Mobile → {Sub-user} → Reset password**.
2. Old password works for 60 seconds, then begins returning `407`.
3. Update your deployment.

## API

Whitelisted IPs and sub-user credentials are managed via the [Whitelists API](/api-reference/whitelists) and [Sub-users API](/api-reference/sub-users). Scope the `product` parameter to `mobile`.


# Endpoints & ports

Mobile shares the gateway infrastructure with residential — same host, same ports, same protocol detection. The differences are connection limits and timeouts.

## Production gateway

| Field                                      | Value              |
| ------------------------------------------ | ------------------ |
| Host                                       | `gate.helodata.io` |
| Port (HTTP / HTTPS / SOCKS5 / TLS-wrapped) | `7777`             |

For regional gateways and backwards-compatible ports, see [Residential endpoints](/products/overview/endpoints-and-ports) — the same hosts and ports apply.

## Connection limits (mobile-specific)

Mobile pools are smaller, so per-sub-user limits are lower:

| Limit                               | Mobile default                      | Raise via              |
| ----------------------------------- | ----------------------------------- | ---------------------- |
| Concurrent connections per sub-user | 100                                 | Dashboard plan upgrade |
| Requests per second per sub-user    | 200                                 | Dashboard plan upgrade |
| Idle timeout                        | 180 s (mobile networks need longer) | —                      |
| Max session lifetime                | 60 min                              | —                      |

Trial accounts: 10 concurrent connections.

## Timeouts to expect

Mobile networks have higher and more variable latency than residential. Plan for:

| Stage                     | Typical      | P99       |
| ------------------------- | ------------ | --------- |
| Connect to gateway        | 50 ms        | 500 ms    |
| Gateway → mobile device   | 200 ms       | 2 s       |
| Mobile device → target    | 300 ms       | 3 s       |
| **End-to-end first byte** | **\~550 ms** | **\~5 s** |

Set client timeouts to **at least 30 s** for mobile workloads.

## Health check

Same as residential:

```bash
curl https://gate.helodata.io:7777/healthz
```

Does not consume traffic credits.


# Locations & carriers

Mobile coverage spans **80+ countries** with at least one major carrier per country. Unlike residential, mobile is **carrier-first, geography-second** — `asn` (the carrier) is usually a bigger lever than `state` or `city`.

## Country (region)

Required. Lowercase ISO-3166 alpha-2.

```
helo_s1a2b3c4d5e-type-mob-region-us
```

Top markets by pool size: US, GB, DE, FR, JP, KR, BR, IN, ID, MX, CA, AU.

For the live list:

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/mobile/locations
```

## Carrier (ASN)

The mobile carrier you want to look like. Highly recommended on every mobile workload — see [ASN targeting](/products/overview-1/asn-targeting) for the full table and rationale.

```
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167   # Verizon Wireless
helo_s1a2b3c4d5e-type-mob-region-jp-asn-2516   # KDDI
helo_s1a2b3c4d5e-type-mob-region-de-asn-3209   # Vodafone DE
```

## State / city (limited)

`state` and `city` are accepted but the live pool is usually small. Use only in tier-1 markets and only when geo precision is critical:

```
helo_s1a2b3c4d5e-type-mob-region-us-state-ca-city-la-asn-6167
```

If the combined filter has fewer than 10 live IPs, the gateway returns `522` + `X-Helodata-Insufficient-Pool: true`. Drop `city` first, then `state`.

## Major carriers per top market

| Country | Carrier                       | ASN   |
| ------- | ----------------------------- | ----- |
| US      | Verizon Wireless              | 6167  |
| US      | T-Mobile                      | 21928 |
| US      | AT\&T Mobility                | 20057 |
| GB      | EE / BT                       | 12576 |
| GB      | Vodafone UK                   | 12969 |
| DE      | Deutsche Telekom / Telekom DE | 3320  |
| DE      | Vodafone DE                   | 3209  |
| DE      | Telefónica O2 DE              | 6805  |
| FR      | Orange                        | 3215  |
| FR      | SFR                           | 15557 |
| JP      | KDDI                          | 2516  |
| JP      | NTT Docomo                    | 9605  |
| JP      | SoftBank                      | 17676 |
| KR      | SK Telecom                    | 9318  |
| BR      | Vivo / Telefónica BR          | 26599 |
| IN      | Reliance Jio                  | 55836 |
| IN      | Airtel                        | 24560 |

The ASNs listed are the public BGP ASNs; some carriers route mobile traffic through multiple ASNs. The API endpoint above lists every routable ASN per country.

## Why `state`/`city` is often empty on mobile

Mobile devices move. The geo data we have for a SIM is at best metro-area accuracy and at worst country-only (when the carrier doesn't expose finer info). When city granularity is needed, residential is the better tool.

## Coverage at a glance

| Coverage                         | Count |
| -------------------------------- | ----- |
| Countries with live IPs          | 80+   |
| Countries with multiple carriers | 30+   |
| Tier-1 markets (deep pools)      | 12    |


# Protocols

Mobile speaks the same protocols as residential — HTTP, HTTPS, SOCKS5 on port `7777`, with the protocol auto-detected from the client handshake. See [Residential protocols](/products/overview/protocols) for full curl/Python/Node samples and the SOCKS5 DNS-leak warning.

## Mobile-specific notes

* **Connection reuse is more important.** Mobile first-hop latency is high — re-using TCP/TLS connections (HTTP keep-alive, HTTP/2 multiplexing) significantly improves throughput.
* **HTTP/2 to the target works through the SOCKS5 / `CONNECT` tunnel.** helodata does not strip or downgrade.
* **WebSockets work** via standard `CONNECT` / SOCKS5 upgrade. Useful for chat apps and real-time APIs that gate behind mobile networks.
* **Mobile carrier transparent proxies** sometimes mutate HTTP headers (e.g. add `Via`, `X-Forwarded-For`). helodata does not strip these — the target sees them as the carrier would. Use HTTPS to avoid mutation.

## Quick reference

```bash
# HTTP/HTTPS
curl -x http://helo_s1a2b3c4d5e-type-mob-region-us-asn-6167:PASSWORD@gate.helodata.io:7777 \
     https://example.com

# SOCKS5 (note socks5h:// for remote DNS)
curl --socks5-hostname helo_s1a2b3c4d5e-type-mob-region-us-asn-6167:PASSWORD@gate.helodata.io:7777 \
     https://example.com
```

## When to prefer which on mobile

| Use case                         | Pick                             |
| -------------------------------- | -------------------------------- |
| Native HTTP scraping             | HTTP/HTTPS                       |
| Mobile app emulation with HTTP/2 | HTTP/HTTPS                       |
| Long-lived WebSocket             | HTTP/HTTPS (`CONNECT`) or SOCKS5 |
| Lower per-request overhead       | SOCKS5                           |
| Debugging with mitmproxy         | HTTP/HTTPS                       |


# Sessions & rotation

Mobile sessions follow the same model as residential, with one key difference: **`sesstime` can go up to 60 minutes** (residential cap = 30). This matches how long a real mobile device typically holds the same carrier-assigned IP.

For the full session model, syntax, and best-practice patterns, see [Residential sessions & rotation](/products/overview/sessions-and-rotation). The mobile-specific notes are below.

## Mobile-specific behavior

### 60-minute cap

```
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167-session-job1-sesstime-60
```

Specifying `sesstime` > 60 returns `400` + `X-Helodata-Error-Code: sesstime-out-of-range`.

### Higher repinning rate

Mobile devices change cells, lose signal, or switch IPs at the carrier's discretion. Expect the `X-Helodata-Session-Repinned: true` header more often than on residential — plan for 5–15 % of long sessions to be repinned mid-flight.

### Pair sessions with ASN

A session without ASN can drift between carriers if the chosen device drops and a different-carrier device picks up. **Always combine `session` with `asn`** when you need carrier consistency:

```
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167-session-job1-sesstime-60
```

### Rotation pattern for high-throughput mobile

Mobile pools are smaller; rotating per connection can exhaust a small filtered pool. A workable pattern:

* Open N sticky sessions, each pinned to one carrier
* Round-robin requests across them
* When a session returns more than 3 target errors in a row, mint a new session ID

```python
import requests, random, string

BASE = "helo_s1a2b3c4d5e-type-mob-region-us-asn-6167"

def new_session():
    sid = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
    return f"{BASE}-session-{sid}-sesstime-30"

pool = [new_session() for _ in range(10)]
```

## What sessions do **not** do (mobile-specific)

* They don't bypass carrier-side rate limits. Each mobile IP shares the carrier's per-IP quota — sticky sessions concentrate your traffic on one IP, which can trigger the carrier's throttling sooner. Spread across more sessions, not fewer.

## Related

* [ASN targeting](/products/overview-1/asn-targeting) — pair with sessions for carrier consistency
* [Residential sessions & rotation](/products/overview/sessions-and-rotation) — full model and code patterns


# ASN targeting

For mobile workloads, ASN targeting is the **single most important parameter**. Many sites detect "this user is on cellular *and* on carrier X" — pinning the carrier is what turns a generic mobile IP into a trusted one.

## How to specify

Same syntax as residential:

```
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167
```

ASN is a decimal integer, no `AS` prefix.

## Carrier ASN reference (top markets)

| Country | Carrier                       | ASN    |
| ------- | ----------------------------- | ------ |
| US      | Verizon Wireless              | 6167   |
| US      | T-Mobile US                   | 21928  |
| US      | AT\&T Mobility                | 20057  |
| GB      | EE                            | 12576  |
| GB      | Vodafone UK                   | 12969  |
| GB      | Three UK                      | 60339  |
| DE      | Deutsche Telekom (Telekom DE) | 3320   |
| DE      | Vodafone DE                   | 3209   |
| DE      | Telefónica O2 DE              | 6805   |
| FR      | Orange                        | 3215   |
| FR      | SFR                           | 15557  |
| FR      | Bouygues Telecom              | 5410   |
| JP      | KDDI                          | 2516   |
| JP      | NTT Docomo                    | 9605   |
| JP      | SoftBank                      | 17676  |
| KR      | SK Telecom                    | 9318   |
| KR      | KT                            | 4766   |
| KR      | LG U+                         | 17858  |
| BR      | Vivo / Telefónica BR          | 26599  |
| BR      | TIM                           | 26615  |
| BR      | Claro                         | 28573  |
| IN      | Reliance Jio                  | 55836  |
| IN      | Bharti Airtel                 | 24560  |
| IN      | Vodafone Idea                 | 38266  |
| MX      | Telcel                        | 8151   |
| MX      | AT\&T MX                      | 32098  |
| CA      | Bell Mobility                 | 577    |
| CA      | Rogers                        | 812    |
| CA      | Telus                         | 6327   |
| AU      | Telstra                       | 1221   |
| AU      | Optus                         | 4804   |
| AU      | TPG / Vodafone AU             | 133612 |

For the complete live list per country, query:

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/mobile/asns?region=us"
```

## Combining with other parameters

```
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167                              # US, Verizon
helo_s1a2b3c4d5e-type-mob-region-us-state-ca-asn-6167                     # California Verizon
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167-session-x-sesstime-60        # Sticky Verizon session
```

`state` and `city` on top of `asn` often produce insufficient pool — see [Locations](/products/overview-1/locations).

## When to use ASN

For mobile, **almost always**. Specifically:

* Apps that show different content based on detected carrier
* Sites that bind anti-bot fingerprints to ASN
* A/B testing carrier-specific features
* Compliance testing (ensure feature works on all carriers in a market)

## When **not** to use ASN

Only when you genuinely need carrier diversity — e.g. measuring how a site behaves across all carriers in a country. Even then, prefer rotating the ASN per request rather than leaving it empty, so you can attribute results.

## Inspecting the chosen ASN

```
X-Helodata-Exit-ASN: 6167
X-Helodata-Exit-ASN-Name: Cellco Partnership d/b/a Verizon Wireless
```

The name field follows the operator's BGP registration — useful for log analysis when you don't memorize ASN-to-carrier mappings.


# Error definitions

Mobile uses the same error envelope as residential — HTTP status + helodata code + `X-Helodata-Error-Code`. The full table is documented in [Residential error definitions](/products/overview/error-definitions). The mobile-specific codes are listed below.

## Mobile-specific gateway codes

| Status | helodata code | `X-Helodata-Error-Code` | Meaning                                                            | What to do                             |
| ------ | ------------- | ----------------------- | ------------------------------------------------------------------ | -------------------------------------- |
| `403`  | —             | `kyc-required`          | Account flagged for a risk review; KYC pending                     | See [KYC](/account-and-billing/kyc)    |
| `400`  | `20002`       | `sesstime-out-of-range` | TTL > 60 (mobile cap)                                              | Reduce `sesstime`                      |
| `502`  | `40001`       | `carrier-blackout`      | Carrier-level outage on the requested ASN                          | Switch ASN or retry in 5 min           |
| `502`  | `40001`       | `pool-empty-mobile`     | No matching mobile IPs (typically state+city+ASN over-constrained) | Drop `city`, then `state`              |
| `522`  | —             | `device-offline`        | Selected device went offline mid-request                           | Retry — gateway will pick a new device |

## More retries, with backoff

Mobile devices come and go. The transient-error rate is roughly 2–3× residential. A reasonable retry policy:

```python
RETRYABLE_GATEWAY = {429, 502, 522, 524}
MAX_RETRIES = 5

import time, random
def request_with_retry(fn):
    for attempt in range(MAX_RETRIES):
        r = fn()
        if r.status_code not in RETRYABLE_GATEWAY:
            return r
        time.sleep(2 ** attempt + random.random())   # exponential + jitter
    return r                                          # last response
```

## Target-originated errors

Pass-through, same as residential. Always check for the `X-Helodata-Exit-IP` header — if it's present alongside a `403` from the target, the target is rejecting the IP, not helodata.

## Carrier rate limiting

Mobile carriers throttle their own IPs. A `429` from the target without `X-Helodata-Error-Code` likely means the target hit your IP's carrier-imposed limit. Rotate ASN, increase session diversity, or slow down per-IP.

## Related

* [Residential error definitions](/products/overview/error-definitions) — full code table
* [407](/troubleshooting/407) — auth issues
* [522](/troubleshooting/522) — timeouts


# Statistics

Same dashboards, headers, and API as residential — switch the path from `/v1/residential/...` to `/v1/mobile/...`. See [Residential statistics](/products/overview/statistics) for the full reference. The mobile-specific dimensions are noted below.

## Dashboard

**Dashboard → Mobile → Statistics** adds these views on top of the standard ones:

* **Top ASNs (carriers)** — most-used carrier IPs
* **Carrier repinning rate** — % of sticky sessions that got a new IP mid-flight
* **Per-carrier error rate** — when a single carrier is degraded

## REST API

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/mobile/traffic?from=2026-05-01&to=2026-05-28&group_by=asn"
```

`group_by` adds `asn` and `carrier_name` on top of the residential options.

## Per-response headers (mobile-only fields)

```
X-Helodata-Exit-IP: 198.51.100.42
X-Helodata-Exit-Geo: us
X-Helodata-Exit-ASN: 6167
X-Helodata-Exit-ASN-Name: Cellco Partnership d/b/a Verizon Wireless
X-Helodata-Exit-Connection: lte           # one of: 3g / lte / 5g / wifi-handoff
X-Helodata-Session-Repinned: true
X-Helodata-Bytes-In: 1248
X-Helodata-Bytes-Out: 38745
X-Helodata-Request-Id: req_01HX2K3PQ4M5
```

`X-Helodata-Exit-Connection` reports the technology layer of the IP at request time — useful when a site treats 5G and LTE differently.

## Tagging requests

Same as residential — `X-Helodata-Tag` header, max 64 chars, `[a-z0-9_-]`. Tags appear in dashboard filters and the `group_by` parameter of the traffic API.

## Billing reconciliation

Mobile traffic is invoiced separately from residential. Pull the mobile traffic API at day granularity for the billing period and compare against **Billing → Invoices**.

## Alerts

Mobile-specific alerts available under **Dashboard → Alerts**:

* Mobile traffic exceeds X GB / day
* Per-ASN error rate exceeds Y%
* Carrier repinning rate exceeds Z% over Z minutes (signals a regional outage)


# ISP proxies

ISP proxies are **static residential IPs** sourced from ISP-owned subnets — they look residential to target sites but are hosted on premium datacenter infrastructure, so they're fast like datacenter and trusted like residential.

Unlike Residential and Mobile, ISP proxies are **not** delivered through a gateway. After purchase, you get a list of static IPs that you address directly.

## At a glance

|                    |                                                                                  |
| ------------------ | -------------------------------------------------------------------------------- |
| **Delivery model** | Per-IP list, `ip:port:user:pass`                                                 |
| **Billing**        | Per IP, per month                                                                |
| **Pool size**      | The IPs you purchased — fixed, dedicated to your account                         |
| **IP stability**   | Permanent — same IP across reboots, weeks, months                                |
| **Geo targeting**  | Country (and city in select markets) — **chosen at order time, not per request** |
| **Rotation**       | Client-side — you pick the next IP from your list                                |
| **Speed**          | High (1 Gbps+ per IP)                                                            |
| **Protocols**      | HTTP, HTTPS, SOCKS5                                                              |
| **Authentication** | User+password (per-IP credentials) or IP whitelist                               |
| **KYC required**   | Not by default (may be requested based on risk)                                  |

## Credential format

Each IP in your list is delivered in the standard four-tuple:

```
ip:port:user:pass
```

Example:

```
198.51.100.42:8000:helo_s1a2b3c4d5e:PASSWORD
198.51.100.43:8000:helo_s1a2b3c4d5e:PASSWORD
198.51.100.44:8000:helo_s1a2b3c4d5e:PASSWORD
```

Same user/password across the whole batch you ordered — the IP+port pair is what distinguishes them. Full reference: [Proxy list format](/products/overview-2/proxy-list-format).

> The `helo_` prefix in the username is **not** the [gateway grammar](/getting-started/username-format) — ISP usernames carry no `-type-` / `-region-` segments. They are just user identifiers.

## When to use ISP

* **Account-bound workflows** — login flows where the site binds anti-bot signals to IP for hours/days
* **Sneaker / drop / ticket purchases** — speed + residential reputation
* **Long-running monitors** — checkers that must stay on the same IP for stability
* **High-bandwidth scraping** where residential per-GB billing gets expensive
* **Geo-locked services** where IP and city must remain consistent

If you need to rotate IPs aggressively per request, residential is a better fit. ISP shines when stability is the requirement.

## Five-second example

```bash
curl -x http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 \
     https://ipv4.icanhazip.com
```

(Substitute one of the IPs from your list.)

## On this page

* [Quick start](/products/overview-2/quick-start) — order, download list, first request
* [Proxy list format](/products/overview-2/proxy-list-format) — the four-tuple in detail
* [Authentication](/products/overview-2/authentication) — password vs IP whitelist
* [Locations](/products/overview-2/locations) — country and city coverage at order time
* [Protocols](/products/overview-2/protocols) — HTTP / HTTPS / SOCKS5
* [Client-side rotation](/products/overview-2/rotation) — patterns for picking the next IP
* [IP management](/products/overview-2/ip-management) — replace dead IPs, refresh batches
* [Error definitions](/products/overview-2/error-definitions) — what each status code means
* [Statistics](/products/overview-2/statistics) — per-IP usage and the API


# Quick start

## 1. Order ISP IPs

> No KYC is required to start. We may request verification later if your account triggers a risk review — see [ID verification](/account-and-billing/kyc).

1. Open **Dashboard → ISP → Order**.
2. Pick **country** (and **city** if the country supports it).
3. Pick **quantity** — minimum 1 IP, max 5,000 per order.
4. Pick **billing period** — monthly or annual.
5. Click **Order**. IPs are provisioned within 1–5 minutes for tier-1 countries, up to 24 hours for long-tail.

You'll receive an email when the batch is ready.

## 2. Download the list

1. Open **Dashboard → ISP → My IPs → {Batch}**.
2. Click **Download** and pick a format:
   * **`ip:port:user:pass`** (default) — one IP per line
   * **CSV** — columns `ip,port,user,pass,country,city,asn`
   * **JSON** — full metadata including ASN and city
   * **Proxifier / FoxyProxy / SwitchyOmega** — ready-to-import files

Or grab the list via API:

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/batches/{batch_id}/ips?format=plain"
```

Full reference: [Proxy list format](/products/overview-2/proxy-list-format).

## 3. Send a request

Use any single IP from your list:

### curl

```bash
curl -x http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 \
     https://ipv4.icanhazip.com
```

The response should be exactly `198.51.100.42` — it's a static IP.

### Python (requests)

```python
import requests

PROXY = "http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000"

r = requests.get(
    "https://ipv4.icanhazip.com",
    proxies={"http": PROXY, "https": PROXY},
    timeout=30,
)
print(r.text.strip())
```

### Node.js (undici)

```js
import { fetch, ProxyAgent } from "undici";

const agent = new ProxyAgent(
  "http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000"
);
const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
console.log((await res.text()).trim());
```

### Go (net/http)

```go
package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
)

func main() {
    proxyURL, _ := url.Parse("http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000")
    client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}}
    resp, _ := client.Get("https://ipv4.icanhazip.com")
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}
```

### PHP (cURL)

```php
<?php
$ch = curl_init('https://ipv4.icanhazip.com');
curl_setopt_array($ch, [
    CURLOPT_PROXY          => 'http://198.51.100.42:8000',
    CURLOPT_PROXYUSERPWD   => 'helo_s1a2b3c4d5e:PASSWORD',
    CURLOPT_RETURNTRANSFER => true,
]);
echo trim(curl_exec($ch));
curl_close($ch);
```

## 4. Use the whole batch (rotation)

Load the list and pick the next IP per request. Simple round-robin:

```python
import itertools, requests

with open("isp.txt") as f:
    proxies = [line.strip() for line in f if line.strip()]

pool = itertools.cycle(proxies)                                  # ip:port:user:pass lines

def get(url):
    line = next(pool)
    ip, port, user, password = line.split(":", 3)
    p = f"http://{user}:{password}@{ip}:{port}"
    return requests.get(url, proxies={"http": p, "https": p}, timeout=30)
```

See [Client-side rotation](/products/overview-2/rotation) for more robust patterns.

## What's next

* [Proxy list format](/products/overview-2/proxy-list-format) — every format variant
* [Authentication](/products/overview-2/authentication) — switch to IP whitelist
* [IP management](/products/overview-2/ip-management) — replace dead IPs, refresh batches


# Proxy list format

Every ISP IP is delivered as a colon-separated four-tuple. This is the standard format that most proxy management tools, antidetect browsers, and scrapers consume directly.

## Canonical format

```
ip:port:user:pass
```

| Field  | Example            | Notes                                                                         |
| ------ | ------------------ | ----------------------------------------------------------------------------- |
| `ip`   | `198.51.100.42`    | IPv4. IPv6 available on request — see below.                                  |
| `port` | `8000`             | Same port for HTTP, HTTPS, and SOCKS5 — auto-detected.                        |
| `user` | `helo_s1a2b3c4d5e` | Sub-user identifier, prefixed with `helo_`. Shared across all IPs in a batch. |
| `pass` | `PASSWORD`         | Sub-user password. Same across the batch.                                     |

## Example list

A batch of 5 US ISP IPs renders as:

```
198.51.100.42:8000:helo_s1a2b3c4d5e:Mz4yQv8R
198.51.100.43:8000:helo_s1a2b3c4d5e:Mz4yQv8R
198.51.100.44:8000:helo_s1a2b3c4d5e:Mz4yQv8R
198.51.100.45:8000:helo_s1a2b3c4d5e:Mz4yQv8R
198.51.100.46:8000:helo_s1a2b3c4d5e:Mz4yQv8R
```

Same user/password across the batch — `ip:port` is what differentiates them.

## URL form

To plug into HTTP clients that expect a proxy URL:

```
http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000
```

For SOCKS5:

```
socks5h://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000
```

## Download formats

`Dashboard → ISP → My IPs → {Batch} → Download` offers:

### `ip:port:user:pass` (default plain text)

One line per IP. Drop-in compatible with most antidetect browsers (AdsPower, Multilogin, GoLogin), proxy managers (Proxifier), and scrapers.

### CSV

```csv
ip,port,user,pass,country,city,asn
198.51.100.42,8000,helo_s1a2b3c4d5e,Mz4yQv8R,US,New York,7922
198.51.100.43,8000,helo_s1a2b3c4d5e,Mz4yQv8R,US,New York,7922
```

Use when you want metadata alongside the credentials — analytics, regional sharding, etc.

### JSON

```json
[
  {
    "ip": "198.51.100.42",
    "port": 8000,
    "user": "helo_s1a2b3c4d5e",
    "pass": "Mz4yQv8R",
    "country": "US",
    "city": "New York",
    "asn": 7922,
    "carrier": "Comcast",
    "created_at": "2026-05-01T12:00:00Z",
    "expires_at": "2026-06-01T12:00:00Z"
  }
]
```

Best for programmatic consumption. Includes per-IP lifecycle dates.

### Tool-specific exports

Direct import files for:

* **Proxifier** `.ppx`
* **FoxyProxy** `.json`
* **SwitchyOmega** `.bak`
* **Bit Browser / AdsPower** bulk-import CSV

## API access

Get the same lists programmatically. Useful for automated provisioning into your scraper farm:

```bash
# Plain text
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/batches/bat_01HX/ips?format=plain"

# JSON
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/batches/bat_01HX/ips?format=json"
```

Full schema: [API → Orders](/api-reference/orders).

## IPv6

IPv6 ISP IPs are available on request. The format is identical but the `ip` field uses bracketed notation in URLs:

```
[2001:db8::42]:8000:helo_s1a2b3c4d5e:PASSWORD
```

URL form:

```
http://helo_s1a2b3c4d5e:PASSWORD@[2001:db8::42]:8000
```

To request IPv6, email <support@helodata.com>.

## Rotating the password

If you suspect a password leak, you can rotate it for the **whole batch** in one click:

1. **Dashboard → ISP → My IPs → {Batch} → Rotate password**.
2. New password takes effect within 60 seconds; old one stops working immediately.
3. Re-download the list — the IPs are unchanged but the `pass` field is new.

Do not edit the password manually; the gateway side won't accept it.


# Authentication

ISP supports the same two authentication models as the gateway products, but the wiring is different — there's no gateway to authenticate against, so credentials go to each individual IP.

## Model A — User + password (default)

Each IP in your list ships with the same user/password. Pass them as you would to any HTTP proxy:

```bash
curl -x http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 \
     https://example.com
```

Client encodes `helo_s1a2b3c4d5e:PASSWORD` as Base64 and sends:

```
Proxy-Authorization: Basic aGVsb19zMWEyYjNjNGQ1ZTpQQVNTV09SRA==
```

**Pros**

* Works from any source IP — no infrastructure setup
* Same credentials across the entire batch — easy to load and rotate

**Cons**

* Credentials sit in your code or config

## Model B — IP whitelist

Register your egress IPs in the dashboard once. Requests from those IPs to your ISP proxies are accepted with no `Proxy-Authorization` header.

```bash
curl -x http://198.51.100.42:8000 https://example.com
```

(Note: no username/password at all — the source IP is the identity.)

To enable:

1. **Dashboard → ISP → My IPs → {Batch} → Whitelisted IPs**.
2. Add up to 50 IPv4 addresses or CIDR ranges. **The whitelist is per batch**, not per IP — adding one entry authorizes that source for every IP in the batch.
3. Changes take effect within 60 seconds.

> Each batch has its own whitelist. Adding an IP to batch A does not authorize it for batch B.

**Pros**

* No secret in code
* Simpler request — bare URL

**Cons**

* Useless for dynamic-egress clients (laptops, autoscaling containers without a NAT gateway)

## Choosing between them

| Scenario                             | Recommended                                    |
| ------------------------------------ | ---------------------------------------------- |
| Server with static egress IP         | IP whitelist                                   |
| Container fleet behind a NAT gateway | IP whitelist (the NAT IP)                      |
| Laptop / dynamic egress              | User + password                                |
| Antidetect browser farm              | User + password (the tools store creds anyway) |

## Per-batch credential rotation

To rotate the password for an entire batch:

1. **Dashboard → ISP → My IPs → {Batch} → Rotate password**.
2. New password effective in 60 seconds.
3. Old password stops working immediately.
4. Re-download the list.

You cannot rotate the username — it's the sub-user identifier and is permanent for the batch.

## API

Whitelist and rotation are also exposed via:

* [Whitelists API](/api-reference/whitelists) (scope `product=isp`, `batch_id=...`)
* [Orders API](/api-reference/orders) (rotation endpoint)


# Locations

ISP geo targeting works differently from the gateway products: country (and city, in select markets) is **picked at order time**. Each IP in your list has a fixed location for its lifetime.

## Coverage

| Tier                            | Countries                                      | City selection |
| ------------------------------- | ---------------------------------------------- | -------------- |
| Tier 1 (deep pools, city-level) | US, GB, DE, FR, JP, KR, BR, CA, AU, IN, NL, SG | Yes            |
| Tier 2 (country only)           | 30+ additional countries                       | No             |

Live coverage and per-city availability are visible on the order page; the API returns the same data:

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/isp/inventory
```

Returns per-country quantity available, monthly price, and the cities offered.

## How to pick

At order time you choose:

1. **Country** — required
2. **City** — optional, only in tier-1 countries
3. **Quantity** — min 1, max 5,000 per order

Mixed batches (e.g. 50 US + 50 DE) require placing two orders. Each batch tracks its country independently.

## Once an IP is provisioned, its location is fixed

The IP keeps the same country, city, ASN, and carrier until it's released. The metadata is visible in the dashboard and the CSV/JSON downloads:

```json
{
  "ip": "198.51.100.42",
  "country": "US",
  "city": "New York",
  "asn": 7922,
  "carrier": "Comcast"
}
```

If you need different cities later, place a new order. There is no "change location" operation.

## City fallback when ordering

If a city is over-subscribed at order time, the dashboard offers two choices:

* **Wait** — your order queues until inventory frees up (typically <24h)
* **Substitute** — automatically pick the next-closest city with inventory

Substitutions are flagged in the order email and in the dashboard so you can audit them.

## ASN at order time (tier-1 only)

In tier-1 countries you can additionally request a specific ASN — Comcast vs Charter, Deutsche Telekom vs Vodafone, etc. Pricing varies by ASN: premium carriers cost \~20% more.

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/inventory?country=us&asn=7922"
```

ASNs available per country are returned by the inventory endpoint above.

## What about subnets?

helodata avoids issuing more than 4 IPs from the same `/24` subnet to a single customer's batch. This keeps the IPs harder for target sites to correlate. If you specifically want a contiguous subnet (e.g. for testing CDN edge behavior), contact <support@helodata.com> — `/29` blocks can be reserved on annual contracts.


# Protocols

Each ISP IP supports HTTP, HTTPS, and SOCKS5 on the same port (`8000`). The protocol is auto-detected from your client handshake — there's nothing to configure per IP.

## HTTP / HTTPS

```bash
# HTTP target
curl -x http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 http://example.com

# HTTPS target (end-to-end TLS)
curl -x http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 https://example.com
```

For HTTPS targets the client sends `CONNECT example.com:443` to the proxy, which opens a TCP tunnel. helodata never sees the request body.

## SOCKS5

```bash
curl --socks5-hostname helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 \
     https://ipv4.icanhazip.com
```

Use `--socks5-hostname` (curl) or `socks5h://` (Python `requests`, others) to make the proxy do DNS — otherwise your local resolver leaks DNS and breaks geo-based services.

### Python

```python
import requests
proxy = "socks5h://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000"
r = requests.get("https://ipv4.icanhazip.com",
                 proxies={"http": proxy, "https": proxy}, timeout=30)
```

### Node.js

```js
import { SocksProxyAgent } from "socks-proxy-agent";
import fetch from "node-fetch";
const agent = new SocksProxyAgent("socks5://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000");
const res = await fetch("https://ipv4.icanhazip.com", { agent });
```

## When to prefer which

The trade-offs are the same as residential. See [Residential protocols](/products/overview/protocols#when-to-prefer-which) for the full table.

## ISP-specific notes

* **HTTP/2 to the target** works through the `CONNECT` tunnel. helodata does not downgrade.
* **Long-lived connections** — ISP IPs are stable, so connection pools (HTTP keep-alive, HTTP/2 multiplexing) pay off handsomely. Hold connections; don't reconnect per request.
* **WebSocket and gRPC** work; treat the IP as a normal HTTP proxy for these.

## What we do not support

* SOCKS4 / SOCKS4a — use SOCKS5
* Username-encoded targeting (no `-type-`, `-region-`, etc.) — ISP credentials are plain `user:pass`. Anything you try to add to the username will be sent to the gateway as-is and rejected with `407 auth-failed`.


# Client-side rotation

ISP IPs are permanently stable — there's no `session` parameter and no gateway rotation. If you want different IPs across requests, your client picks them. This page documents the common patterns.

## Round-robin

The simplest pattern: cycle through the list in order.

```python
import itertools, requests

with open("isp.txt") as f:
    lines = [l.strip() for l in f if l.strip()]

cycle = itertools.cycle(lines)

def get(url):
    ip, port, user, password = next(cycle).split(":", 3)
    proxy = f"http://{user}:{password}@{ip}:{port}"
    return requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
```

Pros: trivial. Cons: failed IPs keep getting picked.

## Round-robin + health tracking

Track failures and skip dead IPs for a cool-down window:

```python
import time, itertools, requests
from collections import defaultdict

class IPPool:
    def __init__(self, lines, cooldown=300):
        self.lines = lines
        self.cooldown = cooldown
        self.bad_until = defaultdict(float)
        self._iter = itertools.cycle(lines)

    def next_alive(self):
        for _ in range(len(self.lines)):
            line = next(self._iter)
            if self.bad_until[line] < time.time():
                return line
        raise RuntimeError("All IPs are in cool-down")

    def mark_bad(self, line):
        self.bad_until[line] = time.time() + self.cooldown

pool = IPPool(open("isp.txt").read().splitlines())

def get(url):
    line = pool.next_alive()
    ip, port, user, password = line.split(":", 3)
    proxy = f"http://{user}:{password}@{ip}:{port}"
    try:
        r = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
        if r.status_code >= 500:
            pool.mark_bad(line)
        return r
    except requests.RequestException:
        pool.mark_bad(line)
        raise
```

## Sticky-per-task

If your unit of work needs a stable IP (login flow, multi-step purchase), pin one IP per task:

```python
import hashlib

def ip_for_task(task_id, lines):
    h = int(hashlib.sha256(task_id.encode()).hexdigest(), 16)
    return lines[h % len(lines)]
```

Consistent hashing means restarts and re-runs of the same `task_id` hit the same IP.

## Concurrency caps

Each ISP IP can handle high throughput (1 Gbps+), but most target sites will rate-limit you per IP. A reasonable starting point: **max 5 concurrent requests per IP** to a single target site. Sharded across the batch, this scales linearly.

## Coordinating across processes

If multiple workers share one batch, use a shared store (Redis, etcd) to coordinate rotation and health tracking. A simple Redis-backed pool:

```python
import redis, time, json

r = redis.Redis()

def pick(batch_key):
    lines = json.loads(r.get(f"isp:{batch_key}:lines"))
    idx = r.incr(f"isp:{batch_key}:counter") % len(lines)
    return lines[idx]
```

For health tracking, use a sorted set keyed on `cooldown_until` and skip entries above `now()`.

## When **not** to rotate

If your use case is account-bound (logged-in scraping, posting, monitoring a logged-in dashboard), **do not rotate**. Pin one IP per account. The whole reason to use ISP is IP stability — rotating defeats it.

## Tools that handle rotation for you

* **Antidetect browsers** (AdsPower, Multilogin, GoLogin) — assign a different ISP IP per browser profile
* **Proxifier** — round-robin across imported list at the OS level
* **Scrapy + scrapy-rotating-proxies** — middleware reads a list and rotates
* **Apify SDK proxy configuration** — handles rotation automatically when given a list

For most production stacks, your scraper framework's built-in proxy rotator is enough. The code above is for when you need custom logic.


# IP management

This page covers the operational lifecycle of your ISP IPs: replacing dead ones, refreshing batches, extending billing periods, and releasing IPs you no longer need.

## Replace a dead IP

Sometimes an IP gets flagged by a specific target site (e.g. Cloudflare reputation drop). helodata's policy:

* **First 14 days after purchase**: free replacement, no questions asked.
* **After 14 days**: up to **10% of the batch** can be replaced per billing period at no extra cost. Beyond that, replacements are billed at the per-IP rate.

To replace:

1. **Dashboard → ISP → My IPs → {Batch}** — click the **Replace** button on the row.
2. Pick a reason (optional but helps us improve pool quality).
3. New IP is provisioned in 1–5 minutes; the dead IP is released immediately.

Via API:

```bash
curl -X POST \
     -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"reason": "target-blocked"}' \
     "https://api.helodata.com/v1/isp/ips/{ip_id}/replace"
```

## Refresh an entire batch

If a large fraction of a batch has become unusable for a target, request a **batch refresh** — every IP gets replaced in one operation. This counts as the full batch's 14-day allowance, so use sparingly:

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/batches/{batch_id}/refresh"
```

Available once per batch per billing period.

## Extend / renew

Subscriptions auto-renew at the end of each billing period if you have a payment method and sufficient balance. Without auto-renew:

* 7 days before expiry: warning email
* 3 days before: second warning
* On expiry: IPs become unreachable; batch enters a 7-day grace where you can renew and recover the same IPs
* After grace: IPs are released back to the pool and given to other customers

To renew manually:

**Dashboard → ISP → My IPs → {Batch} → Renew**

Or via API:

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"months": 1}' \
     "https://api.helodata.com/v1/isp/batches/{batch_id}/renew"
```

## Release IPs

When you no longer need IPs:

1. **Dashboard → ISP → My IPs → {Batch} → Release** — releases the entire batch
2. **Per-IP release** — click the trash icon on individual IP rows (the batch keeps the rest)

Released IPs are **not** refunded mid-period. They are removed from your account immediately and re-enter the inventory pool.

## Transferring IPs between sub-users

ISP IPs are owned by the **account**, not by a specific sub-user. To re-assign:

1. **Dashboard → ISP → My IPs → {Batch} → Settings → Sub-user** — pick a different sub-user
2. Re-download the list — the password changes (it's the new sub-user's password); the IP+port stays the same

Useful when reorganizing teams or migrating workloads.

## Monitoring batch health

The dashboard shows a per-batch health score derived from:

* Recent error rates per IP
* Replacement count
* Concurrent connection peaks
* Time since last successful request

Health < 80 triggers an alert (configurable under **Dashboard → Alerts**). A consistently-low score over multiple billing periods usually means the workload is over-saturating the batch — consider buying more IPs.

## API summary

| Action              | Endpoint                            |
| ------------------- | ----------------------------------- |
| List batches        | `GET /v1/isp/batches`               |
| List IPs in a batch | `GET /v1/isp/batches/{id}/ips`      |
| Replace one IP      | `POST /v1/isp/ips/{id}/replace`     |
| Refresh whole batch | `POST /v1/isp/batches/{id}/refresh` |
| Renew               | `POST /v1/isp/batches/{id}/renew`   |
| Release             | `DELETE /v1/isp/batches/{id}`       |

Full schemas: [Orders API](/api-reference/orders).


# Error definitions

ISP errors are returned by the individual IP, not by a central gateway. The envelope is similar to the gateway products — HTTP status + helodata code + `X-Helodata-Error-Code` — but the set of possible codes is smaller because there's no username grammar to parse.

## ISP error codes

| Status | helodata code | `X-Helodata-Error-Code` | Meaning                                             | What to do                                                                    |
| ------ | ------------- | ----------------------- | --------------------------------------------------- | ----------------------------------------------------------------------------- |
| `401`  | `20003`       | `unknown-subuser`       | Sub-user doesn't exist                              | Check dashboard                                                               |
| `403`  | —             | `kyc-required`          | Account flagged for a risk review; KYC pending      | See [KYC](/account-and-billing/kyc)                                           |
| `403`  | —             | `batch-expired`         | Batch's billing period ended                        | See [IP management → Renew](/products/overview-2/ip-management#extend--renew) |
| `403`  | —             | `batch-grace-period`    | Batch is in 7-day grace; functional but warning you | Renew soon                                                                    |
| `403`  | —             | `ip-released`           | IP was released back to the pool                    | Remove from your client's list                                                |
| `403`  | —             | `restricted-category-*` | Target is on the restricted list                    | See [Restricted targets](/products/overview/restricted-targets)               |
| `403`  | —             | `account-suspended`     | Account suspended                                   | Contact support                                                               |
| `407`  | `20003`       | `auth-failed`           | Wrong password                                      | Verify, then rotate if leaked                                                 |
| `407`  | `20003`       | `not-whitelisted`       | Source IP not in this batch's whitelist             | Add IP in dashboard                                                           |
| `429`  | `30002`       | `concurrent-limit`      | Too many concurrent connections to this IP          | Spread load across more IPs                                                   |
| `429`  | `30002`       | `rps-limit`             | Too many requests per second to this IP             | Add jitter, spread load                                                       |
| `502`  | `40001`       | `upstream-tls-error`    | Target TLS handshake failed                         | See [SSL errors](/troubleshooting/ssl-errors)                                 |
| `522`  | —             | `upstream-timeout`      | Target unreachable from this IP                     | Retry on a different IP from your batch                                       |
| `524`  | —             | `target-timeout`        | Target didn't respond in time                       | Retry or extend timeout                                                       |

## Health-aware retries

Because ISP IPs are stable, an IP that just returned `502`/`522`/`429` is likely to fail again on the next attempt. Always **retry on a different IP** from your batch — never on the same one:

```python
def request_with_failover(url, pool):
    for attempt in range(5):
        line = pool.next_alive()
        try:
            r = call(url, line)
            if r.status_code in (200, 301, 302, 304, 404):
                return r
            if r.status_code in (502, 522, 524, 429):
                pool.mark_bad(line)                    # cool this IP down
                continue
            return r                                    # other 4xx/5xx — pass through
        except Exception:
            pool.mark_bad(line)
    raise RuntimeError(f"All 5 attempts failed for {url}")
```

## Target-originated errors

Pass-through, same as the gateway products. You won't see helodata's `X-Helodata-Exit-IP` header because each IP **is** the exit — but the target's response headers reach you unchanged.

A target `403` or Cloudflare `1020` typically means **that specific IP** got reputation-flagged. Either:

* Mark it dead and request a [replacement](/products/overview-2/ip-management#replace-a-dead-ip)
* Wait out the cool-down (reputation often recovers in a few hours)

## When to suspect helodata vs the target

| Symptom                                              | Likely cause                              |
| ---------------------------------------------------- | ----------------------------------------- |
| `407` on every request                               | helodata — auth wrong                     |
| `522` on a single IP, others work                    | helodata — that IP is degraded; mark dead |
| `522` across many IPs simultaneously                 | helodata — incident in that region        |
| Target `403` on a few IPs, others work               | Target — IP reputation, replace           |
| Target `403` on every IP in a country                | Target — country block, switch country    |
| `429` from target — same IP, same target, repeatedly | Target — rate limit, spread load          |

## Related

* [Residential error definitions](/products/overview/error-definitions) — full pass-through error semantics
* [407 troubleshooting](/troubleshooting/407)
* [IP management](/products/overview-2/ip-management) — how to replace bad IPs


# Statistics

ISP statistics are dimensioned **per IP** rather than per request flow. Each IP is a billable resource you own; the dashboard and API help you see which ones are paying off.

## Dashboard

**Dashboard → ISP → Statistics** shows:

* **Per-IP usage** — bytes, request count, error rate
* **Per-batch health score** — composite (errors + replacements + saturation)
* **Top targets** — which sites you hit through ISP
* **IP utilization** — which IPs in a batch are idle (candidates for release)
* **Replacement history** — IPs replaced over time
* **Renewal calendar** — upcoming billing dates

Granularity: 1-minute for the last 24 hours, 5-minute for 7 days, hourly beyond.

## REST API

Per-IP traffic over time:

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/traffic?batch_id=bat_01HX&from=2026-05-01&to=2026-05-28&group_by=ip"
```

Response:

```json
{
  "rows": [
    { "ip": "198.51.100.42", "bytes_in": 1234567, "bytes_out": 9876543, "requests": 4210, "error_rate": 0.012 },
    { "ip": "198.51.100.43", "bytes_in": 222345,  "bytes_out": 134567,  "requests": 88,   "error_rate": 0.0 }
  ]
}
```

`group_by` accepts `ip`, `batch`, `target_domain`, `day`.

Per-IP detail (most-recent N requests):

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/ips/{ip_id}/requests?limit=100"
```

Returns the last 1,000 sampled requests with status, target, bytes, and timestamp.

## Per-response headers

Each ISP IP returns these on every response:

```
X-Helodata-Bytes-In: 1248
X-Helodata-Bytes-Out: 38745
X-Helodata-IP-Health: 96
X-Helodata-Request-Id: req_01HX2K3PQ4M5
```

`X-Helodata-IP-Health` is the live 0–100 score for the IP — drop below 80 and the dashboard alert (if configured) fires.

## Tagging requests

Same mechanism as residential — `X-Helodata-Tag` request header. Tags surface in dashboard filters and traffic API `group_by`.

```bash
curl -x http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000 \
     -H "X-Helodata-Tag: campaign-acme-q2" \
     https://example.com
```

## Spotting idle IPs

The dashboard flags IPs that haven't been used in the last 7 days. Two options:

* **Reassign** — start sending traffic through them
* **Release** — drop them at the next renewal to save cost

## Health-based alerts

**Dashboard → Alerts** offers ISP-specific alerts:

* IP health drops below threshold X
* Batch health drops below X
* Replacement count exceeds Y per period
* Batch is in grace (renewal warning)
* Per-IP error rate exceeds Z% over Z minutes

Alerts deliver via email and optional Slack webhook.

## Billing reconciliation

ISP is billed per IP per month — flat fee, regardless of usage. The invoice line on **Billing → Invoices** is `qty × monthly_price`. There's no bandwidth charge to reconcile.

If you replace IPs mid-period, the count on your invoice stays the same — replacement is included in your monthly fee up to the 10% allowance.


# Datacenter proxies — coming soon

Datacenter proxies are not yet generally available. We are testing pricing, geo coverage, and dedicated-IP allocation.

## What to expect

* Pay-per-GB shared pool and pay-per-IP dedicated tiers
* 1 Gbps+ per IP, 99.9% uptime SLA
* US, EU, and APAC POPs at launch
* Same [username grammar](/getting-started/username-format) as residential/mobile (`type-dc` reserved)

## Join the waitlist

Email **<support@helodata.com>** with subject `Datacenter waitlist` and a one-line use case. We will notify you when private beta opens.

## In the meantime

* [Residential proxies](/products/overview) cover most scraping needs without datacenter detection issues
* [ISP proxies](/products/overview-2) offer static IPs with residential reputation if you need stable session affinity


# Web Unblocker — coming soon

Web Unblocker is a managed proxy that handles JavaScript rendering, CAPTCHA solving, header rotation, and retries — you point a single endpoint at a URL and get back the rendered HTML.

## What to expect

* One endpoint, any target site
* Built-in headless browser with JS rendering
* Auto CAPTCHA handling
* Per-request billing
* Drop-in replacement for the residential proxy endpoint — no SDK required

## Join the waitlist

Email **<support@helodata.com>** with subject `Web Unblocker waitlist` and a one-line use case.

## In the meantime

* Use [Residential proxies](/products/overview) + a headless browser (Playwright, Puppeteer) for the same shape with more glue code. Integration guides: [Playwright](/integrations/scraping-tools/playwright), [Puppeteer](/integrations/scraping-tools/puppeteer).


# Scraping API — coming soon

Scraping API returns structured JSON for common targets (search engines, e-commerce, social) — no proxy management, no parsing, no retries.

## What to expect

* Per-target schemas for Google Search, Amazon, Walmart, TikTok, YouTube, Reddit, Bing, ChatGPT (more on the roadmap)
* Real-time and async (push-pull) request modes
* Per-request billing
* Built-in localization (country + device + language)

## Join the waitlist

Email **<support@helodata.com>** with subject `Scraping API waitlist` and tell us which target you most need. We prioritize the launch list by demand.

## In the meantime

* Use [Residential proxies](/products/overview) + your own parser for the same data with custom field selection


# Overview

The helodata dashboard at [`dashboard.helodata.com`](https://dashboard.helodata.com) is where you order, configure sub-users, manage IP batches, and watch usage. Everything here is also accessible via the [API](/api-reference/api-reference) — pick whichever fits the team workflow.

## Layout

```
┌──────────────────────────────────────────────────────────────┐
│  Top bar:   Balance   |   KYC status   |   Profile           │
├──────────────┬───────────────────────────────────────────────┤
│              │                                               │
│  Left nav:   │   Main content (per-product or per-section)   │
│  Residential │                                               │
│  Mobile      │                                               │
│  ISP         │                                               │
│  ─────────   │                                               │
│  Dashboard   │                                               │
│  Sub-users   │                                               │
│  Whitelists  │                                               │
│  Statistics  │                                               │
│  Trials      │                                               │
│  ─────────   │                                               │
│  Billing     │                                               │
│  API         │                                               │
│  Settings    │                                               │
└──────────────┴───────────────────────────────────────────────┘
```

\[screenshot: dashboard landing page after login]

## What's on each section

| Section                        | What you do here                                                                                |
| ------------------------------ | ----------------------------------------------------------------------------------------------- |
| **Residential / Mobile / ISP** | Per-product order, sub-users, whitelists, traffic                                               |
| **Trials**                     | View trial credits granted by support/sales and their remaining balance                         |
| **Billing**                    | Plans, invoices, payment methods, auto top-up, refunds, KYC                                     |
| **API**                        | Issue and rotate API tokens; configure webhooks                                                 |
| **Settings**                   | Profile, security (MFA), notification preferences                                               |
| **Status**                     | Live status of the gateway and API — also at [status.helodata.com](https://status.helodata.com) |

## Top-bar essentials

* **Balance** — prepaid USD credit. Top up via **Billing → Payment methods**, or enable [Auto top-up](/dashboard/auto-topup).
* **KYC status** — `not-submitted`, `pending`, `approved`, `rejected`. KYC is not required by default; this only matters if a risk review requests it — see [ID verification](/account-and-billing/kyc).
* **Profile menu** — quick links to API tokens, account settings, logout.

## Roles and teams

A helodata account can have multiple users with different roles:

| Role         | Permits                                            |
| ------------ | -------------------------------------------------- |
| **Owner**    | Everything, including billing and account deletion |
| **Admin**    | Everything except billing and account deletion     |
| **Operator** | Manage sub-users, whitelists, orders; no billing   |
| **Viewer**   | Read-only                                          |

Invite team members in **Settings → Team**. Roles map to API scopes — see [Sub-users & teams](/dashboard/sub-users).

## First-time setup checklist

If you just created the account:

1. ✅ Verify email
2. ⏳ [KYC](/account-and-billing/kyc) — not required to start; only if a risk review requests it
3. ⏳ Request trial credits — contact support or sales (see [Free trial](/getting-started/free-trial))
4. ⏳ Create a first sub-user — see [Sub-users & teams](/dashboard/sub-users)
5. ⏳ Add a payment method — **Billing → Payment methods**
6. ⏳ (Optional) Enable [Auto top-up](/dashboard/auto-topup) so requests don't fail when balance hits zero

## Status page

For real-time service health, bookmark:

```
https://status.helodata.com
```

Subscribe by email or RSS for incident notifications.


# Placing an order

You can buy more bandwidth (residential / mobile) or new ISP IPs in two ways — from the dashboard, or via the [Orders API](/api-reference/orders). This page is the dashboard flow.

## Residential / Mobile — buying bandwidth

1. Left nav → **Residential** (or **Mobile**) → **Buy traffic**.
2. Pick a **plan**:
   * **Pay-as-you-go** — per-GB rate, no commitment
   * **Starter / Pro / Business / Enterprise** — bigger upfront commit, lower per-GB rate
3. Enter quantity in GB. The dashboard shows the unit price and total.
4. Click **Order** → confirmation modal → **Confirm**.
5. Bandwidth is added to your account **immediately**.

\[screenshot: residential order form]

The bandwidth is shared across all sub-users of that product. To split usage between teams, use separate sub-users with per-sub-user usage caps.

## ISP — buying static IPs

1. Left nav → **ISP** → **Order new IPs**.
2. Pick **country**.
3. (Optional) Pick **city** — available only in tier-1 markets ([Locations](/products/overview-2/locations)).
4. (Optional) Pick a specific **ASN** — Comcast vs Charter etc. Premium ASNs cost more.
5. Pick **quantity** — minimum 1, maximum 5,000 per order.
6. Pick **billing period** — monthly or annual.
7. Pick which **sub-user** the IPs are bound to.
8. **Order**.

\[screenshot: ISP order form with inventory preview]

After confirmation, the dashboard transitions to a **provisioning** state. T1 countries typically deliver in 1–5 minutes; long-tail countries can take up to 24 hours. You receive an email when ready.

## What if inventory isn't enough?

The order form shows live inventory per city/ASN. If you request more than available, two options:

* **Wait** — the order queues until inventory frees up
* **Substitute** — the dashboard picks the closest city/ASN with inventory. Flagged in the email so you can audit.

## Order from the API

```bash
# Residential
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"product":"residential","gb":50}' \
     https://api.helodata.com/v1/orders

# ISP
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"product":"isp","country":"us","city":"newyork","quantity":20,"months":1}' \
     https://api.helodata.com/v1/orders
```

Full API reference: [Orders](/api-reference/orders).

## Payment

Orders use your account **balance** first, then your default payment method for the shortfall. To pre-fund, top up the balance under **Billing → Payment methods**.

If a payment is needed and your card is declined, the order fails with `insufficient_balance`. The dashboard shows the rejection reason — fix the payment method and re-order.

## Cancellation

Bandwidth orders can't be cancelled once placed — the credits are non-refundable. For ISP batches you can release IPs anytime (no refund for the remaining period); see [IP management](/products/overview-2/ip-management).

## Common pitfalls

* **KYC during a risk review** — ordering is open by default. If your account is under a risk review, an order may be held until KYC is completed; you'll be notified if so.
* **Wrong sub-user for ISP** — once a batch is bound to a sub-user, the IPs use **that** sub-user's password. Re-binding requires re-downloading the list.
* **City over-subscribed** — if your monitor depends on a specific city, set the dashboard alert for low inventory under **Settings → Alerts**.


# Extending an order

For ISP batches, "extending" means **renewing** the billing period so the IPs stay yours. Bandwidth orders (residential / mobile) don't extend — they're consumed.

## Renew an ISP batch

1. Left nav → **ISP** → **My IPs** → click a batch.
2. **Renew** button at top right.
3. Pick period (monthly or annual). Annual is 10% off.
4. Confirm.

\[screenshot: ISP batch detail with Renew button]

Payment is taken from balance first, then card. The renewal date shifts by the chosen period; same IPs, same password.

## Auto-renew

By default, ISP batches **auto-renew** at the end of each period if balance covers the cost. Toggle in **My IPs → {Batch} → Settings → Auto-renew**.

If balance is insufficient and no card is on file:

* 7 days before expiry: email warning
* 3 days before: second warning
* On expiry: IPs become unreachable (`403 batch-expired`); 7-day grace begins
* After grace: IPs released to other customers

See [IP management → Renew](/products/overview-2/ip-management#extend--renew).

## Recover a batch in grace

If a batch expired and you missed it, you have 7 days to recover the **same** IPs:

1. **My IPs → Expired** tab → find the batch.
2. **Recover** button.
3. Pay the same monthly rate.
4. IPs become active within \~60 seconds.

Recovery is **not guaranteed** — if all IPs happened to be reassigned during grace (rare but possible for high-demand cities), recovery returns equivalent IPs in the same country/city but not identical.

## Extend via API

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"months":1}' \
     "https://api.helodata.com/v1/isp/batches/bat_01HX/renew"
```

Returns the new `expires_at`.

## Resizing a batch

If you want **more** IPs in the same batch, that's a separate flow — open the batch, **Add IPs**. New IPs come from the same country/city pool when possible; the batch's `expires_at` is unchanged so the new IPs are prorated to align.

## Bandwidth orders — top up, not extend

For residential and mobile, you can't "extend" but you can **top up**:

* **Residential → Buy traffic → +N GB** — adds GB to the existing pool
* Same pricing as the original order

Unused GB on pay-as-you-go does not expire. On plan tiers (Starter, Pro, …), unused GB rolls into the next month up to 50% of the plan; beyond that it's lost.

## Common pitfalls

* **Auto-renew off but no manual renewal** — batches expire silently. Always set up the dashboard alert under **Settings → Alerts**.
* **Card expired** — if your card on file expires near the renewal date, auto-renew fails. Check **Billing → Payment methods** for the expiry warning.
* **Quantity reduction** — `PATCH` quantity *down* doesn't take effect until the next renewal. Releasing IPs immediately means no refund for unused days.


# Auto top-up

Auto top-up automatically charges your default payment method when balance drops below a threshold, so traffic requests never fail with `insufficient_balance`. This page is the dashboard setup. For the billing-side concept and refund implications, see [Account → Auto top-up](/account-and-billing/auto-topup).

## Enable

1. **Billing → Auto top-up → Enable**.
2. Fill in:
   * **Threshold** — when balance drops below this USD value, trigger
   * **Top-up amount** — how much to charge per trigger
   * **Payment method** — pick from your saved cards
   * **Daily cap** — maximum spend per day (sanity guard)
3. **Save**.

\[screenshot: Auto top-up settings]

### Recommended values

| Workload size     | Threshold | Top-up amount | Daily cap |
| ----------------- | --------- | ------------- | --------- |
| Hobby (<10 GB/mo) | $5        | $20           | $20       |
| Small team        | $50       | $200          | $500      |
| Production        | $500      | $2,000        | $5,000    |

The daily cap is a safety net — without it, an abuse loop (e.g. a buggy scraper hitting traffic limits) could rack up runaway charges.

## What triggers it

Auto top-up is checked **before every billed event**:

* Gateway: each request that would consume traffic
* API: each `POST /orders`
* Renewals: when a subscription renews

If balance < threshold, the gateway pauses **for that request only** while it charges the card, then proceeds. The latency cost is one extra \~2-second card charge on the triggering request, which is why we keep the threshold above zero.

## What if the card fails?

The original request fails with `insufficient_balance`. We:

1. Email you immediately.
2. Mark the payment method as **failing**; the next traffic request triggers a retry with the same card.
3. After 3 failed attempts, the auto top-up is paused until you take action.

If you have a second payment method, mark it **default** while you sort out the first.

## Stop / pause

* **Pause** — temporarily turn off without losing config. Useful while testing.
* **Disable** — remove the config entirely. Re-enable via the same form.

## Top up manually one-shot

If you don't want recurring charges, top up manually:

**Billing → Payment methods → Top up** → pick amount → confirm. The balance is added instantly.

## Auto top-up + subscriptions

Auto top-up exists for **prepaid** balance. Subscriptions (monthly plans) bill directly to your card on the renewal date and don't touch balance unless balance is enough to cover the renewal first.

So an account with a $500/mo subscription **and** auto top-up enabled at $50 threshold will:

1. Have $500 charged to the card on renewal day (subscription)
2. Have +$200 charged separately whenever per-request usage drains balance to $50

## API

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "threshold_usd":   50,
       "topup_amount_usd": 200,
       "payment_method_id": "pm_01HX...",
       "daily_cap_usd":    500
     }' \
     https://api.helodata.com/v1/billing/auto-topup
```

## Common pitfalls

* **Threshold too low (e.g. $1)** — by the time the trigger fires, you may already be at zero. Keep the threshold above your worst-case one-second spend.
* **Daily cap too tight** — production workloads can blow through it during a load test. Match the cap to your max acceptable daily spend, not your average.
* **No backup card** — if your only card fails near a renewal, the subscription also fails. Add a backup in **Payment methods**.


# Sub-users & teams

The dashboard exposes two related concepts:

* **Sub-users** — credentials your traffic uses against the proxy gateway. Per-product.
* **Team members** — humans with dashboard / API access. Per-account.

They serve different purposes — read both sections.

## Sub-users (proxy credentials)

Sub-users are how you slice quota and isolate workloads. Each is an independent set of credentials and limits.

### Create

1. Left nav → **{Product} → Sub-users → Create sub-user**.
2. Fill in **label** (your name for it, e.g. `acme-staging`), then **concurrent** and **RPS** caps.
3. Click **Create**.
4. **Copy the issued name and password immediately** — we show the password only once.

\[screenshot: sub-user creation success modal showing one-time password]

### Configure

Each sub-user has:

| Setting                  | Notes                                                              |
| ------------------------ | ------------------------------------------------------------------ |
| **Label**                | Editable any time                                                  |
| **Status**               | `active` or `disabled`. Disabled = all proxy requests return `403` |
| **Concurrent max**       | Per-product cap, ≤ plan limit                                      |
| **RPS max**              | Per-product cap, ≤ plan limit                                      |
| **Whitelisted IPs**      | See [Whitelisting IPs](/dashboard/whitelisting-ips)                |
| **Sticky session count** | Read-only — how many active sticky sessions                        |

### Rotate password

**{Sub-user} → Reset password** → new password shown once. Old keeps working for 60 seconds.

### Delete

**{Sub-user} → Settings → Delete**. All bandwidth attribution stops; any open connections drain within 60 seconds.

> Deleting a sub-user with an ISP batch bound to it requires unbinding the batch first.

### Multiple sub-users per product?

Use them to **segment** workloads:

* One per customer (if you're reselling)
* One per environment (dev / staging / prod)
* One per scraping target (so a noisy target doesn't impact others' quotas)
* One per team

Bandwidth still pools across sub-users of the same product, but the per-sub-user concurrent and RPS caps give you isolation.

## Team members (dashboard / API access)

Distinct from sub-users — these are people who log into the dashboard or issue API tokens.

### Invite

1. **Settings → Team → Invite member**.
2. Email + role.
3. Send. They get an invite link valid for 7 days.

### Roles

| Role         | Permits                                                                               |
| ------------ | ------------------------------------------------------------------------------------- |
| **Owner**    | Everything, including billing changes and account deletion (one per account)          |
| **Admin**    | Sub-users, orders, whitelists, traffic, API tokens, team invites — no billing changes |
| **Operator** | Sub-users, orders, whitelists, traffic — no team or API token management              |
| **Viewer**   | Read-only on everything except billing                                                |
| **Billing**  | Read all + manage billing (invoices, payment methods, subscriptions)                  |

You can assign multiple roles to one member (e.g. `Operator` + `Billing`).

### Transfer ownership

Owner: **Settings → Team → Transfer ownership → {new owner}**. The new owner accepts via email; you become Admin. Useful when the founding account holder is leaving.

### Single sign-on (SSO)

Enterprise plan only. **Settings → SSO** supports OIDC and SAML 2.0. Map IdP groups to roles in the SSO config.

## Audit log

Every action a team member takes (create sub-user, place order, rotate password, change billing) appears in **Settings → Audit log**. Filterable by member, action type, and date.

Retained 365 days. Export to CSV from the same page.

## Sub-user vs team member at a glance

|                    | Sub-user                      | Team member                  |
| ------------------ | ----------------------------- | ---------------------------- |
| What it identifies | A traffic source / credential | A human (or service account) |
| Used by            | Proxy gateway                 | Dashboard + API              |
| Has password?      | Yes (one-time shown)          | Email + own password         |
| Per-product?       | Yes                           | No (account-level)           |
| MFA support?       | n/a                           | Yes (TOTP)                   |


# Whitelisting IPs

IP whitelisting lets requests from registered source IPs skip `Proxy-Authorization` — easier for servers in fixed-egress environments. This page is the dashboard flow; for the API see [Whitelists API](/api-reference/whitelists).

## Where the whitelist lives

The scope depends on the product:

| Product     | Whitelist scope  |
| ----------- | ---------------- |
| Residential | Per **sub-user** |
| Mobile      | Per **sub-user** |
| ISP         | Per **batch**    |

A single source IP can be whitelisted in many places independently. There's no "global" whitelist.

## Add an IP

### Sub-user whitelist (gateway products)

1. Left nav → **{Product} → Sub-users → {Sub-user} → Whitelisted IPs**.
2. **Add IP**.
3. Enter:
   * **IP** or **CIDR** (max `/24` for IPv4, `/64` for IPv6)
   * **Label** (your description, e.g. `prod-eu-west-1`)
4. **Save**.

Active within 60 seconds.

\[screenshot: Whitelisted IPs panel with one entry]

### ISP batch whitelist

1. Left nav → **ISP → My IPs → {Batch} → Whitelisted IPs**.
2. **Add IP** — same form.

The batch whitelist applies to **every IP** in that batch.

## Remove

Click the trash icon next to an entry. Effective within 60 seconds.

## CIDR vs single IP

Whitelist supports `/24` (IPv4) or `/64` (IPv6) at most.

| Source pattern                                    | Use                                          |
| ------------------------------------------------- | -------------------------------------------- |
| One static server                                 | Single `203.0.113.5`                         |
| Container fleet behind NAT                        | The NAT IP (single entry)                    |
| Kubernetes egress                                 | The NAT IP of the cluster's egress gateway   |
| Multiple ASN-issued IPs from a single ISP         | Several singles (or a /24 if contiguous)     |
| Dynamic egress (laptops, autoscaling without NAT) | Don't use whitelist — use user/password auth |

## Limits

| Scope                      | Max entries |
| -------------------------- | ----------- |
| Per sub-user (per product) | 50          |
| Per ISP batch              | 50          |

A `/24` counts as one entry; the gateway expands it server-side.

## Finding your egress IP

To find what to whitelist, run from the source machine (without the proxy):

```bash
curl https://ipv4.icanhazip.com
```

That's your source IP. Note that if your machine is behind a NAT or a load balancer, this returns the **egress** IP, which is what the gateway sees.

## Mix whitelist and password

You can run both modes simultaneously per sub-user — whitelisted IPs skip auth, anyone else gets prompted for credentials. Useful when migrating a fleet to whitelisting in stages.

## API alternative

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{
       "subuser":"sub_01HX...",
       "product":"residential",
       "ip":"203.0.113.5",
       "label":"prod-eu-west-1"
     }' \
     https://api.helodata.com/v1/whitelists
```

Full schema: [Whitelists API](/api-reference/whitelists).

## Common pitfalls

* **Whitelisted the wrong IP** — when you `curl ipv4.icanhazip.com` from your laptop you get the laptop's egress, not the production server's. SSH to the actual production machine first.
* **CIDR too wide** — `/16` is rejected. Split into multiple `/24` entries or rethink whether you need that much.
* **Forgot batch whitelist after a sub-user re-bind** — moving an ISP batch to a new sub-user doesn't carry over the batch whitelist (it's per-batch, not per-sub-user, so it stays).


# Statistics & usage

The Statistics tab shows what you used, where it went, and what failed. Available per-product; the metrics differ slightly by product.

## Where to find it

| Path                         | Shows                                   |
| ---------------------------- | --------------------------------------- |
| **Residential → Statistics** | Residential traffic + errors            |
| **Mobile → Statistics**      | Mobile traffic + carrier breakdown      |
| **ISP → Statistics**         | Per-IP usage + batch health             |
| **Dashboard → Statistics**   | Account-wide rollup across all products |

\[screenshot: Statistics view with charts]

## Common views

### Traffic over time

Stacked area chart, sliceable by sub-user / region / ASN / tag. Hover for exact values.

Default range: last 24h at 1-min granularity. Stretch the range and the chart downsamples — see [Granularity](/api-reference/traffic#granularity).

### Top destinations

Table of domains by traffic and request count. Click a row to drill into:

* Per-IP traffic to that domain
* Status code histogram (200 vs 4xx vs 5xx)
* Recent sample requests

Useful for spotting which targets dominate your bill or which return the most errors.

### Top countries (gateway products)

Where your traffic exits. Stacked by sub-user if a request had targeting.

### Top ASNs / carriers (mobile)

Carrier distribution for mobile traffic. A skewed carrier mix can be a sign your `asn` targeting isn't doing what you think.

### Error breakdown

Status code histogram + `X-Helodata-Error-Code` breakdown. Click a slice to see sample failing requests.

### Concurrency

Time-series of peak concurrent connections vs your sub-user limit. If this is hitting the ceiling regularly, you need either more sub-users or a plan upgrade.

### ISP per-IP table

For ISP, the headline view is a sortable table of every IP in your batches with columns:

* IP
* Country / city / ASN
* Requests today
* Error rate
* Health score (0–100)
* Last used

Sort by health ascending to spot bad apples; by "Last used" to spot idle IPs you could release.

## Filters

The top of every Statistics page has:

* **Date range** — presets or custom
* **Sub-user** — multi-select
* **Tag** — match `X-Helodata-Tag` values you set on requests

Filters apply to all panels on the page.

## Exporting

The **Export** button on any panel downloads a CSV / JSON of the data currently rendered. For scheduled exports to S3 or email, use [Reports](/api-reference/reports).

## Alerts

Pair stats with alerts under **Settings → Alerts**. Common rules:

* Daily traffic > X GB
* Error rate > Y% in Z minutes
* Concurrency at 90% of limit
* ISP IP health < 80
* Carrier repinning rate > 15%

Delivered by email and (optional) Slack webhook.

## Reconciling with the API

Everything in the UI is also available via the [Traffic API](/api-reference/traffic). The numbers must match — if they don't, open a ticket; that's a bug.

## Common pitfalls

* **Comparing trial vs paid traffic** — trial bandwidth is metered separately and shown in a different panel. Don't accidentally read trial-only data when sizing production usage.
* **Tag filter sticking** — if your dashboard mysteriously shows tiny numbers, check the tag filter at the top isn't accidentally set.
* **Downsampling artefacts** — for views older than 14 days, 5-min buckets can hide short-lived spikes. Use the API for high-fidelity historical analysis.


# Overview

helodata proxies are standards-compliant HTTP / HTTPS / SOCKS5 — so anything that speaks those protocols will work. This section is a recipe book: for each common tool we show **exactly where the proxy goes**, with copy-paste-ready snippets for the three live products.

## Quick reference — what credentials to use

The credential shape depends on the product:

| Product         | Host                                             | Username                                            | Password          |
| --------------- | ------------------------------------------------ | --------------------------------------------------- | ----------------- |
| **Residential** | `gate.helodata.io:7777`                          | `helo_{user}-type-res-region-{cc}[-...optional...]` | sub-user password |
| **Mobile**      | `gate.helodata.io:7777`                          | `helo_{user}-type-mob-region-{cc}[-asn-...]`        | sub-user password |
| **ISP**         | per IP from your list, e.g. `198.51.100.42:8000` | `helo_{user}` (no extra segments)                   | sub-user password |

If a tool only accepts a single `ip:port:user:pass` line, that's the **ISP** shape — gateway products need their targeting segments inside the username.

## What's in this section

* **Browsers** — Chrome, Firefox, Edge, Safari (manual or extension-based)
* **Antidetect browsers** — AdsPower, GoLogin, Multilogin, Dolphin {anty}, Octo
* **Scraping tools** — Python `requests`, Scrapy, Selenium, Playwright, Puppeteer, Node `fetch`/`axios`
* **Operating systems** — system-wide proxy on Windows, macOS, Linux
* **Mobile** — Android Wi-Fi proxy, iOS Wi-Fi proxy, Shadowrocket
* **AI & workflow** — Postman, LangChain, n8n, MCP server

## Conventions used in this section

* Placeholder sub-user: `s1a2b3c4d5e`
* Placeholder password: `PASSWORD`
* Placeholder ISP IP: `198.51.100.42`
* All examples use US residential as the default target — change `region-us` to any ISO code

When a screenshot would help, the placeholder `[screenshot: …]` marks where one belongs. Real screenshots will live in `.gitbook/assets/` once the dashboard UI is final.


# Browsers


# Chrome

Chrome itself does not have a built-in per-tab proxy UI — by default it follows the OS proxy. Three approaches in increasing order of flexibility:

1. **OS-level proxy** (simplest, affects all apps): see [Windows](/integrations/operating-systems/windows), [macOS](/integrations/operating-systems/macos), [Linux](/integrations/operating-systems/linux).
2. **Per-Chrome-profile via command-line flag** — different proxies per Chrome user data dir.
3. **Extension** like SwitchyOmega for per-tab / per-domain rules.

## Approach 2 — `--proxy-server` flag

Launch Chrome with a dedicated user data dir and the `--proxy-server` flag. Best for scraping rigs and headless automation.

```bash
# Residential
google-chrome \
  --user-data-dir=/tmp/chrome-helo-res \
  --proxy-server="http://gate.helodata.io:7777"

# ISP
google-chrome \
  --user-data-dir=/tmp/chrome-helo-isp \
  --proxy-server="http://198.51.100.42:8000"
```

Chrome's `--proxy-server` flag does **not** accept inline credentials. When Chrome prompts for the proxy login, enter:

* Username: the full proxy username (e.g. `helo_s1a2b3c4d5e-type-res-region-us`)
* Password: your sub-user password

To suppress the prompt, use an extension that pre-fills credentials, or set the OS-level proxy instead.

## Approach 3 — SwitchyOmega (most flexible)

1. Install **SwitchyOmega** from the Chrome Web Store.
2. **New profile → Proxy profile**.
3. Fill in:
   * Protocol: `HTTP` or `SOCKS5`
   * Server: `gate.helodata.io` (or your ISP IP)
   * Port: `7777`
   * Authentication: click the lock → username + password
4. Click **Apply changes**.
5. From the SwitchyOmega icon, switch between profiles per browsing context.

\[screenshot: SwitchyOmega "New profile" form filled in for residential]

## Verify the connection

Visit `https://ipv4.icanhazip.com` — the IP shown should be from helodata, not your own.

## Common pitfalls

* **Auth dialog every time you restart** — Chrome forgets proxy creds across sessions. Use SwitchyOmega or an OS-level config.
* **`net::ERR_TUNNEL_CONNECTION_FAILED`** — the proxy URL syntax is wrong, or your credentials are. Check [407 troubleshooting](/troubleshooting/407).
* **WebRTC IP leak** — install the SwitchyOmega/WebRTC-Leak-Shield extension if your use case demands hiding the local IP from `getUserMedia`.

## DevTools network tab still works

helodata is a forwarding proxy; DevTools sees the same requests it would normally see. You can still inspect headers, status codes, and timing.


# Firefox

Firefox has a **built-in per-instance proxy UI** — no extension or OS-level config required.

## Settings UI

1. Menu → **Settings → General → Network Settings → Settings…**
2. Pick **Manual proxy configuration**.
3. Fill in:
   * **HTTP Proxy**: `gate.helodata.io`, Port `7777`
   * Check **Also use this proxy for HTTPS**
   * (Or use **SOCKS Host** with the same host:port; pick SOCKS v5; check **Proxy DNS when using SOCKS v5**)
4. Click **OK**. Firefox will prompt for credentials on the first request:
   * Username: full proxy username (e.g. `helo_s1a2b3c4d5e-type-res-region-us`)
   * Password: your sub-user password
5. Optional: tick **Remember password** to skip the prompt next time.

\[screenshot: Firefox Connection Settings dialog with helodata host filled in]

## ISP variant

For an ISP IP, replace the HTTP Proxy host with your IP, e.g. `198.51.100.42`. The port is still `8000`.

## Use profiles for parallel identities

Firefox supports multiple profiles, each with its own proxy:

```bash
firefox --no-remote --profile ~/.helo-profiles/us
firefox --no-remote --profile ~/.helo-profiles/de
```

Configure each profile's proxy independently from its own **Network Settings**.

## Container tabs (advanced)

Combine with the **Multi-Account Containers** extension and **Container Proxy** extension to assign a different proxy per container — useful for testing geo-localized variants of a site in one window.

## Verify

Navigate to `https://ipv4.icanhazip.com` — the IP shown should be from helodata, not your own.

## Common pitfalls

* **`HTTPS Proxy` checkbox unchecked** — HTTPS sites then bypass the proxy. Always enable it.
* **SOCKS5 without DNS-over-proxy** — the local resolver leaks DNS and geo targeting breaks. Always tick **Proxy DNS when using SOCKS v5**.
* **Saved password becomes stale after rotation** — re-enter when you rotate sub-user password.


# Edge

Edge is Chromium-based — same proxy behaviour as Chrome. It follows the OS proxy by default and supports the `--proxy-server` flag for per-profile launches.

## Recommended: OS-level proxy

Edge does not have its own proxy UI. Set the OS proxy and Edge follows:

* [Windows](/integrations/operating-systems/windows) — Settings → Network → Proxy
* [macOS](/integrations/operating-systems/macos) — System Settings → Network → Proxies
* [Linux](/integrations/operating-systems/linux) — depends on desktop environment

## Alternative: `--proxy-server` flag

```bash
# Residential
msedge \
  --user-data-dir=%TEMP%\edge-helo-res \
  --proxy-server="http://gate.helodata.io:7777"

# ISP
msedge \
  --user-data-dir=%TEMP%\edge-helo-isp \
  --proxy-server="http://198.51.100.42:8000"
```

Credentials are entered in the proxy-auth dialog Edge pops up on first request. The flag itself does not accept embedded creds.

## Extension: SwitchyOmega-Edge

Edge accepts most Chrome extensions. Install **SwitchyOmega** from the Edge add-ons store; configuration is identical to [Chrome](/integrations/browsers/chrome#approach-3--switchyomega-most-flexible).

## Verify

Visit `https://ipv4.icanhazip.com`. Expect a helodata IP.

## Common pitfalls

* **Edge running as Microsoft account-bound app** — corporate Edge installations may enforce a proxy via group policy that overrides yours.
* **Auth dialog every restart** — same as Chrome; SwitchyOmega or OS-level proxy avoids it.


# Safari

Safari on macOS has no built-in proxy UI — it follows the macOS system proxy. Configuration is at the OS level.

## Configure

1. **System Settings → Network → {Active interface, e.g. Wi-Fi} → Details… → Proxies**.
2. Tick **Web Proxy (HTTP)** and **Secure Web Proxy (HTTPS)**.
3. For each:
   * Server: `gate.helodata.io`, Port `7777` (or your ISP IP)
   * Tick **Proxy server requires password**
   * Username: full proxy username (e.g. `helo_s1a2b3c4d5e-type-res-region-us`)
   * Password: sub-user password
4. Click **OK** → **Apply**.

\[screenshot: macOS Proxies pane with HTTP and HTTPS configured]

## Scope

This affects **all apps** on the active network interface — not just Safari. Switch networks (e.g. plug in Ethernet) and the proxy doesn't follow. Configure each interface you use, or scope by network location:

* **System Settings → Network → ⋯ → Locations → Edit Locations…** — create a "helodata" location and switch via the Apple menu.

## SOCKS5 instead

In the Proxies pane, tick **SOCKS Proxy**, fill in `gate.helodata.io:7777` (or your ISP IP), enable authentication. Safari will route through SOCKS5; DNS is proxied by default on macOS.

## Verify

Visit `https://ipv4.icanhazip.com` in Safari. Expect a helodata IP.

## Common pitfalls

* **VPN active** — macOS VPN typically bypasses the proxy. Disconnect VPN or scope the proxy to a different location.
* **Bypass list** — `*.local`, `169.254/16`, and `localhost` are excluded by default. Useful, but if you've added entries don't accidentally exclude what you wanted to proxy.
* **Captive portals** — macOS proxy applies even during captive-portal detection; that may break the portal flow. Temporarily uncheck the proxy boxes during initial Wi-Fi onboarding.


# Antidetect browsers


# AdsPower

AdsPower lets you assign a different proxy per browser profile — a natural fit for ISP proxies (one IP per profile) and residential sticky sessions (one session per profile).

## Per-profile setup

1. **New profile** → **Proxy settings**.
2. Pick **Custom** (not "AdsPower built-in").
3. Fill in:

### Residential / Mobile (gateway)

| Field      | Value                                                            |
| ---------- | ---------------------------------------------------------------- |
| Proxy type | HTTP, HTTPS, or SOCKS5                                           |
| Proxy host | `gate.helodata.io`                                               |
| Proxy port | `7777`                                                           |
| Account    | `helo_s1a2b3c4d5e-type-res-region-us-session-prof01-sesstime-30` |
| Password   | sub-user password                                                |

Use a unique `session-XXX` per profile so each profile holds its own IP.

### ISP

| Field      | Value                                |
| ---------- | ------------------------------------ |
| Proxy type | HTTP, HTTPS, or SOCKS5               |
| Proxy host | `198.51.100.42` (one IP per profile) |
| Proxy port | `8000`                               |
| Account    | `helo_s1a2b3c4d5e`                   |
| Password   | sub-user password                    |

4. Click **Check proxy** — AdsPower hits `ipify` through the proxy and reports the exit IP.
5. **Save** → launch the profile.

\[screenshot: AdsPower proxy settings dialog]

## Bulk import (ISP)

For batches of profiles each tied to an ISP IP:

1. **My profiles → Import → Proxies**.
2. Upload an `ip:port:user:pass` file — exactly the format helodata ships.
3. Map each line to a profile.

AdsPower also accepts the CSV export (`ip,port,user,pass,country,city,asn`) for richer metadata.

## Profile templates

* **Browser fingerprint**: pick a fingerprint matching the proxy's country/city (Mac UA for a US IP, etc.). AdsPower's "Match country" toggle auto-aligns the fingerprint to the proxy geo.
* **Time zone**: set to match the proxy country.
* **WebRTC**: set to **Replace** to avoid leaking the local IP.

## Verify

In the launched browser, visit `https://ipv4.icanhazip.com` and `https://browserleaks.com/ip` — confirm IP matches the proxy and no WebRTC leak.

## Common pitfalls

* **Same session ID across many profiles** — sticks all profiles to one IP. Each profile must have a unique `session-XXX`.
* **ISP IP reused across profiles** — defeats the per-profile isolation. One IP, one profile.
* **Fingerprint timezone misaligned** — a US IP with Asia/Shanghai locale is a strong bot signal. Use "Match country".


# GoLogin

GoLogin assigns a proxy per profile. Configuration is similar to AdsPower; the field labels differ.

## Per-profile setup

1. **Create profile** → **Proxy** tab.
2. Pick **Your proxy** (not "Free GoLogin proxy").
3. Fill in:

### Residential / Mobile (gateway)

| Field    | Value                                                            |
| -------- | ---------------------------------------------------------------- |
| Type     | HTTP or SOCKS5                                                   |
| Host     | `gate.helodata.io`                                               |
| Port     | `7777`                                                           |
| Username | `helo_s1a2b3c4d5e-type-res-region-us-session-prof01-sesstime-30` |
| Password | sub-user password                                                |

### ISP

| Field    | Value              |
| -------- | ------------------ |
| Type     | HTTP or SOCKS5     |
| Host     | `198.51.100.42`    |
| Port     | `8000`             |
| Username | `helo_s1a2b3c4d5e` |
| Password | sub-user password  |

4. Click **Check proxy** — confirms the exit IP.
5. **Create profile** → **Run**.

\[screenshot: GoLogin Proxy tab filled in]

## Bulk import

**Settings → Proxy management → Import** accepts `ip:port:user:pass` lines or a CSV file. Each row becomes a reusable proxy entry that you can assign to multiple profiles. For best practice, **one row → one profile** for ISP IPs.

## Verify

In the launched browser, hit `https://browserleaks.com/ip` and confirm the exit IP plus no WebRTC leak.

## Common pitfalls

* **GoLogin's "Auto-replace IP" feature** can override your settings — disable it for residential sticky sessions or ISP IPs.
* **Free proxy fallback** — if the proxy check fails GoLogin sometimes falls back to its own free proxies. Disable that toggle.


# Multilogin

Multilogin is the most fingerprint-fidelity-focused option among antidetect browsers. Its proxy support covers all helodata products.

## Per-profile setup

1. **New profile** → **Proxy** section.
2. **Connection type**: HTTP or SOCKS5.
3. Fill in:

### Residential / Mobile (gateway)

| Field    | Value                                                            |
| -------- | ---------------------------------------------------------------- |
| Host     | `gate.helodata.io`                                               |
| Port     | `7777`                                                           |
| Login    | `helo_s1a2b3c4d5e-type-res-region-us-session-prof01-sesstime-30` |
| Password | sub-user password                                                |

### ISP

| Field    | Value              |
| -------- | ------------------ |
| Host     | `198.51.100.42`    |
| Port     | `8000`             |
| Login    | `helo_s1a2b3c4d5e` |
| Password | sub-user password  |

4. Click **Check proxy** — Multilogin probes and shows the exit IP, country, and timezone.
5. Multilogin will offer to **auto-match fingerprint** to the proxy's country. Accept it.

\[screenshot: Multilogin profile creation with helodata proxy]

## Bulk via API

Multilogin's Local API can create profiles programmatically — useful for spinning up N profiles, each bound to one ISP IP:

```bash
curl -X POST http://127.0.0.1:35000/api/v1/profile \
  -H "Content-Type: application/json" \
  -d '{
    "name": "isp-us-042",
    "os_type": "windows",
    "proxy": {
      "type": "http",
      "host": "198.51.100.42",
      "port": 8000,
      "username": "helo_s1a2b3c4d5e",
      "password": "PASSWORD"
    }
  }'
```

Loop over your ISP list to provision profiles in seconds.

## Fingerprint match

Multilogin's strength is matching the browser fingerprint to the proxy's locale. Enable **Match fingerprint to proxy country**:

* Language headers align with the country
* Timezone derives from the proxy IP
* WebGL / Canvas fingerprints are randomized within country norms

## Verify

Hit `https://browserleaks.com/ip` + `https://browserleaks.com/javascript` and check IP, timezone, navigator.language all align.

## Common pitfalls

* **`MultiloginProxyError: AUTHENTICATION_FAILED`** — gateway products require the full username (`helo_...-type-res-region-us`), not just `helo_s1a2b3c4d5e`.
* **Profile launched before proxy check** — Multilogin will sometimes hold a stale proxy in memory; restart the profile after editing.


# Dolphin {anty}

Dolphin {anty} supports per-profile proxies and bulk import of `ip:port:user:pass` lists — both helodata-friendly.

## Per-profile setup

1. **Create profile** → **Proxy → Add proxy**.
2. Pick **Custom**.
3. Fill in:

### Residential / Mobile (gateway)

| Field         | Value                                                            |
| ------------- | ---------------------------------------------------------------- |
| Type          | HTTP or SOCKS5                                                   |
| IP            | `gate.helodata.io`                                               |
| Port          | `7777`                                                           |
| Login         | `helo_s1a2b3c4d5e-type-res-region-us-session-prof01-sesstime-30` |
| Password      | sub-user password                                                |
| Change IP URL | (leave blank — handled by username `session`)                    |

### ISP

| Field    | Value              |
| -------- | ------------------ |
| Type     | HTTP or SOCKS5     |
| IP       | `198.51.100.42`    |
| Port     | `8000`             |
| Login    | `helo_s1a2b3c4d5e` |
| Password | sub-user password  |

4. **Check proxy** confirms the exit IP.
5. **Save → launch**.

\[screenshot: Dolphin {anty} proxy configuration]

## Bulk import

**Proxies → Import → Mass import**. Paste lines in either format:

```
gate.helodata.io:7777:helo_s1a2b3c4d5e-type-res-region-us-session-p1-sesstime-30:PASSWORD
gate.helodata.io:7777:helo_s1a2b3c4d5e-type-res-region-us-session-p2-sesstime-30:PASSWORD
```

or paste the ISP list directly:

```
198.51.100.42:8000:helo_s1a2b3c4d5e:PASSWORD
198.51.100.43:8000:helo_s1a2b3c4d5e:PASSWORD
```

Dolphin reads each line, ips IP/port/login/password by colon, and creates one proxy entry per line.

## Fingerprint sync

In **Profile → Fingerprint**, enable **Sync with proxy** so language, timezone, and geolocation match the proxy country.

## Verify

`https://browserleaks.com/ip` and `https://browserleaks.com/webrtc`.

## Common pitfalls

* **`Change IP URL` set when it shouldn't be** — Dolphin will try to call that URL between sessions and break helodata's stateful rotation. Leave it blank.
* **SOCKS5 with DNS leak** — Dolphin's SOCKS5 path defaults to remote DNS, but verify with `browserleaks.com/dns`.


# Octo Browser

Octo Browser is similar in capability to Dolphin and AdsPower — per-profile proxies, fingerprint sync, bulk import.

## Per-profile setup

1. **New profile** → **Proxy → Add new**.
2. Pick **HTTP** or **SOCKS5** as type.
3. Fill in:

### Residential / Mobile (gateway)

| Field    | Value                                                            |
| -------- | ---------------------------------------------------------------- |
| Host     | `gate.helodata.io`                                               |
| Port     | `7777`                                                           |
| Login    | `helo_s1a2b3c4d5e-type-res-region-us-session-prof01-sesstime-30` |
| Password | sub-user password                                                |

### ISP

| Field    | Value              |
| -------- | ------------------ |
| Host     | `198.51.100.42`    |
| Port     | `8000`             |
| Login    | `helo_s1a2b3c4d5e` |
| Password | sub-user password  |

4. **Verify** — Octo probes the proxy and shows IP, country, ASN.
5. Octo offers **Use proxy timezone and locale**; enable it.

\[screenshot: Octo Browser proxy form]

## Bulk import

**Proxies → Import file**. Accepts:

* `ip:port:user:pass` (one per line)
* CSV with header `host,port,login,password`

For ISP IPs, the helodata-default plain text export is a drop-in.

## API

Octo exposes a local HTTP API for scripted profile management. The `proxy` object format mirrors the dashboard:

```json
{
  "name": "isp-us-042",
  "proxy": {
    "type": "http",
    "host": "198.51.100.42",
    "port": 8000,
    "login": "helo_s1a2b3c4d5e",
    "password": "PASSWORD"
  }
}
```

## Verify

Hit `https://browserleaks.com/ip` and `https://browserleaks.com/webrtc` in the launched browser.

## Common pitfalls

* **Octo "smart connection" mode** retries on a different IP if a request fails — for sticky sessions, disable smart retry per profile.
* **Saved proxies without check** — Octo doesn't auto-revalidate on profile launch. After a password rotation, re-check the proxy explicitly.


# Scraping tools


# Python Requests

The simplest way to add helodata to a Python scraper.

## Install

```bash
pip install requests
# For SOCKS5 support:
pip install requests[socks]
```

## Residential / Mobile (gateway)

```python
import requests

USER = "helo_s1a2b3c4d5e-type-res-region-us"      # change type-res to type-mob for Mobile
PASS = "PASSWORD"
proxy = f"http://{USER}:{PASS}@gate.helodata.io:7777"

r = requests.get(
    "https://ipv4.icanhazip.com",
    proxies={"http": proxy, "https": proxy},
    timeout=30,
)
print(r.text.strip())
```

## ISP

```python
PROXY = "http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000"

r = requests.get(
    "https://ipv4.icanhazip.com",
    proxies={"http": PROXY, "https": PROXY},
    timeout=30,
)
```

## SOCKS5 with remote DNS

Crucial: use `socks5h://` (with the `h`) so the proxy resolves DNS — otherwise the local resolver leaks DNS and geo-targeting breaks.

```python
proxy = f"socks5h://{USER}:{PASS}@gate.helodata.io:7777"
```

## Session (HTTP keep-alive)

Reusing connections across requests is faster, especially for ISP (stable IPs):

```python
s = requests.Session()
s.proxies = {"http": proxy, "https": proxy}

for url in urls:
    r = s.get(url, timeout=30)
```

## Rotating residential sessions

Mint a fresh `session-XXX` for each batch of N requests:

```python
import random, string

def make_user(country="us"):
    sid = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
    return f"helo_s1a2b3c4d5e-type-res-region-{country}-session-{sid}-sesstime-10"
```

## ISP round-robin

```python
import itertools
with open("isp.txt") as f:
    pool = itertools.cycle([l.strip() for l in f if l.strip()])

def fetch(url):
    ip, port, user, password = next(pool).split(":", 3)
    proxy = f"http://{user}:{password}@{ip}:{port}"
    return requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=30)
```

See [Client-side rotation](/products/overview-2/rotation) for production-quality patterns.

## Retries

Use `urllib3.Retry` for transient errors:

```python
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(total=5, backoff_factor=0.5, status_forcelist=[429, 502, 522, 524])
s.mount("https://", HTTPAdapter(max_retries=retry))
```

## Verify

```python
r = s.get("https://ipv4.icanhazip.com")
print(r.text.strip(), r.headers.get("X-Helodata-Exit-IP"))
```

## Common pitfalls

* **Forgetting `https` key in proxies dict** — Python `requests` requires both `http` and `https` entries even when only HTTPS targets are used.
* **`socks5://` instead of `socks5h://`** — DNS leak. Always use `socks5h://` for SOCKS5.
* **No timeout** — requests can hang forever on a flaky upstream. Always pass `timeout`.


# Scrapy

Scrapy plugs into helodata through its built-in `HTTP_PROXY` envvar support and the `HttpProxyMiddleware`. For per-request rotation, add `scrapy-rotating-proxies`.

## Single static proxy

In `settings.py`:

```python
HTTPPROXY_AUTH_ENCODING = "latin-1"

DOWNLOADER_MIDDLEWARES = {
    "scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": 750,
}
```

Then in each request:

```python
def start_requests(self):
    proxy = "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
    for url in self.start_urls:
        yield scrapy.Request(url, meta={"proxy": proxy})
```

Or set the env vars before launching:

```bash
export HTTP_PROXY="http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
export HTTPS_PROXY="$HTTP_PROXY"
scrapy crawl myspider
```

## ISP rotation with `scrapy-rotating-proxies`

```bash
pip install scrapy-rotating-proxies
```

`settings.py`:

```python
ROTATING_PROXY_LIST_PATH = "/path/to/isp.txt"      # one ip:port:user:pass per line
# or:
ROTATING_PROXY_LIST = [
    "http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000",
    "http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.43:8000",
]

DOWNLOADER_MIDDLEWARES = {
    "rotating_proxies.middlewares.RotatingProxyMiddleware": 610,
    "rotating_proxies.middlewares.BanDetectionMiddleware": 620,
}

# Optional tuning
ROTATING_PROXY_BAN_POLICY = "rotating_proxies.policy.BanDetectionPolicy"
ROTATING_PROXY_BACKOFF_BASE = 300                  # seconds
```

The middleware tracks failures per proxy and skips banned ones. Combine with `RETRY_HTTP_CODES = [429, 502, 522, 524]` for transient gateway errors.

## Residential dynamic session rotation

You don't need an external list — generate sessions on the fly:

```python
import random, string

def session_proxy(country="us"):
    sid = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
    user = f"helo_s1a2b3c4d5e-type-res-region-{country}-session-{sid}-sesstime-10"
    return f"http://{user}:PASSWORD@gate.helodata.io:7777"

class MySpider(scrapy.Spider):
    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(url, meta={"proxy": session_proxy()})
```

## Adding the helodata tag

```python
yield scrapy.Request(
    url,
    meta={"proxy": proxy},
    headers={"X-Helodata-Tag": "campaign-acme-q2"},
)
```

The tag shows up in your traffic analytics — see [Statistics](/products/overview/statistics).

## Verify

```python
def parse(self, response):
    self.logger.info("Exit IP: %s", response.headers.get(b"X-Helodata-Exit-IP", b"").decode())
```

## Common pitfalls

* **`HttpProxyMiddleware` disabled** — Scrapy enables it by default, but custom `DOWNLOADER_MIDDLEWARES` dicts must keep it.
* **CONCURRENT\_REQUESTS too high for a small ISP batch** — your batch caps at N IPs; concurrency above N just queues. Match `CONCURRENT_REQUESTS_PER_DOMAIN` to your pool size.


# Selenium

Selenium drives a real browser, so proxy configuration depends on the underlying driver (Chrome / Firefox). Two approaches:

1. **No auth** — `--proxy-server` flag (Chrome) or profile setting (Firefox). The browser still prompts for credentials.
2. **With auth** — install a tiny on-the-fly extension that pre-fills the proxy auth dialog. This is the standard pattern for scraping.

## Chrome — with auth via Selenium-Wire

The cleanest option is `selenium-wire`, a Selenium wrapper that handles proxy auth transparently:

```bash
pip install selenium-wire
```

```python
from seleniumwire import webdriver

USER = "helo_s1a2b3c4d5e-type-res-region-us"
PASS = "PASSWORD"

options = {
    "proxy": {
        "http":  f"http://{USER}:{PASS}@gate.helodata.io:7777",
        "https": f"http://{USER}:{PASS}@gate.helodata.io:7777",
        "no_proxy": "localhost,127.0.0.1",
    }
}

driver = webdriver.Chrome(seleniumwire_options=options)
driver.get("https://ipv4.icanhazip.com")
print(driver.page_source)
driver.quit()
```

For ISP, swap to `http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000`.

## Chrome — without selenium-wire (manual auth extension)

If you can't add selenium-wire (e.g. corporate restrictions), build a manifest-v3 extension on the fly:

```python
import os, tempfile, zipfile, json
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

HOST, PORT = "gate.helodata.io", 7777
USER = "helo_s1a2b3c4d5e-type-res-region-us"
PASS = "PASSWORD"

manifest = {
    "manifest_version": 3,
    "name": "helo-proxy",
    "version": "1.0.0",
    "permissions": ["proxy", "webRequest", "webRequestAuthProvider", "<all_urls>"],
    "background": {"service_worker": "bg.js"},
}
bg = f"""
chrome.proxy.settings.set({{ value: {{ mode: "fixed_servers", rules: {{
  singleProxy: {{ scheme: "http", host: "{HOST}", port: {PORT} }}, bypassList: ["localhost"]
}}}}, scope: "regular" }});
chrome.webRequest.onAuthRequired.addListener(
  () => ({{ authCredentials: {{ username: "{USER}", password: "{PASS}" }} }}),
  {{ urls: ["<all_urls>"] }},
  ["blocking"]
);
"""

tmp = tempfile.mkdtemp()
with open(f"{tmp}/manifest.json", "w") as f: json.dump(manifest, f)
with open(f"{tmp}/bg.js", "w") as f: f.write(bg)
ext_path = f"{tmp}/ext.zip"
with zipfile.ZipFile(ext_path, "w") as z:
    z.write(f"{tmp}/manifest.json", "manifest.json")
    z.write(f"{tmp}/bg.js", "bg.js")

opts = Options()
opts.add_extension(ext_path)
driver = webdriver.Chrome(options=opts)
driver.get("https://ipv4.icanhazip.com")
```

## Firefox

Firefox accepts proxy settings directly via `FirefoxProfile` plus a credential helper. With selenium-wire the same pattern as Chrome works.

## Verify

```python
print(driver.execute_script("return navigator.userAgent"))
driver.get("https://browserleaks.com/ip")
```

## Common pitfalls

* **Native proxy-auth dialog** blocks headless runs. Always use selenium-wire or the extension pattern for headless.
* **WebRTC leak** — add `--disable-features=WebRtcHideLocalIpsWithMdns` and/or block media via `chrome_options.add_argument("--disable-webrtc")` (Brave/Vivaldi flag).
* **Proxy not actually used in headless** — pre-Chrome-100 used `--headless`; modern Chrome requires `--headless=new` for the extension model to work.


# Playwright

Playwright has first-class proxy support — pass credentials directly to `launch()` or `new_context()`, no extension hacks required.

## Python

```python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(proxy={
        "server":   "http://gate.helodata.io:7777",
        "username": "helo_s1a2b3c4d5e-type-res-region-us",
        "password": "PASSWORD",
    })
    page = browser.new_page()
    page.goto("https://ipv4.icanhazip.com")
    print(page.content())
    browser.close()
```

For SOCKS5 use `"server": "socks5://gate.helodata.io:7777"` (DNS is resolved by the proxy automatically).

## Node.js

```js
import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: {
    server:   "http://gate.helodata.io:7777",
    username: "helo_s1a2b3c4d5e-type-res-region-us",
    password: "PASSWORD",
  },
});
const page = await browser.newPage();
await page.goto("https://ipv4.icanhazip.com");
console.log(await page.content());
await browser.close();
```

## Per-context proxy

Use a different proxy per browser context — handy for parallel sessions:

```python
ctx_us = browser.new_context(proxy={"server": "http://gate.helodata.io:7777",
                                    "username": "helo_s1a2b3c4d5e-type-res-region-us",
                                    "password": "PASSWORD"})
ctx_de = browser.new_context(proxy={"server": "http://gate.helodata.io:7777",
                                    "username": "helo_s1a2b3c4d5e-type-res-region-de",
                                    "password": "PASSWORD"})
```

The browser process is shared; contexts are isolated like separate browser profiles.

## ISP

```python
browser = p.chromium.launch(proxy={
    "server":   "http://198.51.100.42:8000",
    "username": "helo_s1a2b3c4d5e",
    "password": "PASSWORD",
})
```

## Rotating sessions in Playwright

Mint a new sub-user string per context, with a unique `session-XXX`:

```python
import random, string
def session_user(country="us"):
    sid = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
    return f"helo_s1a2b3c4d5e-type-res-region-{country}-session-{sid}-sesstime-10"

ctx = browser.new_context(proxy={
    "server": "http://gate.helodata.io:7777",
    "username": session_user(),
    "password": "PASSWORD",
})
```

## Verify

```python
ip = page.evaluate("fetch('https://ipv4.icanhazip.com').then(r => r.text())")
print(ip)
```

## Common pitfalls

* **No headless flag needed** for proxy auth — Playwright handles it natively in both head and headless modes.
* **WebRTC leak** — by default Playwright doesn't expose the local IP via WebRTC, but verify with `browserleaks.com/webrtc` if your target sniffs for it.
* **Bypass list** — use `proxy.bypass = "localhost,127.0.0.1"` to avoid proxying internal targets.


# Puppeteer

Puppeteer needs two pieces: `--proxy-server` at launch, plus `page.authenticate()` for credentials.

## Basic setup

```js
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://gate.helodata.io:7777"],
});
const page = await browser.newPage();

await page.authenticate({
  username: "helo_s1a2b3c4d5e-type-res-region-us",
  password: "PASSWORD",
});

await page.goto("https://ipv4.icanhazip.com");
console.log(await page.content());
await browser.close();
```

`page.authenticate()` handles the proxy-auth dialog transparently.

## ISP

```js
const browser = await puppeteer.launch({
  args: ["--proxy-server=http://198.51.100.42:8000"],
});
await page.authenticate({ username: "helo_s1a2b3c4d5e", password: "PASSWORD" });
```

## Per-page proxy via `puppeteer-extra-plugin-proxy`

The official Puppeteer can't change proxy mid-session — once Chromium launches with `--proxy-server`, that's it. To rotate per-page, use the `puppeteer-page-proxy` plugin:

```bash
npm i puppeteer-extra puppeteer-extra-plugin-stealth puppeteer-page-proxy
```

```js
import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";
import useProxy from "puppeteer-page-proxy";

puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch();
const page = await browser.newPage();

await useProxy(page, "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777");
await page.goto("https://ipv4.icanhazip.com");
```

`useProxy()` can be called repeatedly to switch the page's proxy between requests.

## ISP rotation example

```js
import fs from "node:fs";
const list = fs.readFileSync("isp.txt", "utf8").trim().split("\n");
let i = 0;

async function fetchOne(url) {
  const line = list[i++ % list.length];
  const [ip, port, user, pass] = line.split(":");
  const p = `http://${user}:${pass}@${ip}:${port}`;
  await useProxy(page, p);
  await page.goto(url);
}
```

## Verify

```js
const ip = await page.evaluate(() =>
  fetch("https://ipv4.icanhazip.com").then(r => r.text())
);
console.log(ip.trim());
```

## Common pitfalls

* **`page.authenticate()` not set** before `page.goto()` — the first request will block on the auth dialog. Always call `authenticate()` immediately after `newPage()`.
* **`puppeteer-extra-plugin-stealth` masks the browser fingerprint** but does NOT change IP — combine with helodata for full coverage.
* **Same proxy across all pages** when using `--proxy-server` alone — for per-page proxies use the plugin above.


# Node fetch / axios

For plain HTTP calls in Node (without a headless browser), two stacks dominate.

## Modern: `undici` (built-in fetch)

Node 18+ ships `fetch` from `undici`. For HTTP-CONNECT proxies you need `undici`'s `ProxyAgent`:

```bash
npm i undici
```

```js
import { fetch, ProxyAgent } from "undici";

const agent = new ProxyAgent(
  "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
);

const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
console.log((await res.text()).trim());
console.log("Exit IP:", res.headers.get("x-helodata-exit-ip"));
```

For ISP, swap the URL to `http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000`.

## SOCKS5 with undici

```bash
npm i socks-proxy-agent
```

```js
import { SocksProxyAgent } from "socks-proxy-agent";
import { fetch } from "undici";

const agent = new SocksProxyAgent(
  "socks5://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
);
const res = await fetch("https://ipv4.icanhazip.com", { dispatcher: agent });
```

## axios

```bash
npm i axios https-proxy-agent
```

```js
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent(
  "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
);

const res = await axios.get("https://ipv4.icanhazip.com", {
  httpAgent: agent,
  httpsAgent: agent,
  proxy: false,                            // disable axios's own proxy resolution
});
console.log(res.data.trim());
```

> Set `proxy: false` on the axios call — otherwise axios will second-guess your agent using its own `env`-based proxy logic.

## ISP rotation

```js
import fs from "node:fs";
import { fetch, ProxyAgent } from "undici";

const lines = fs.readFileSync("isp.txt", "utf8").trim().split("\n");
let i = 0;

async function fetchOne(url) {
  const [ip, port, user, pass] = lines[i++ % lines.length].split(":");
  const agent = new ProxyAgent(`http://${user}:${pass}@${ip}:${port}`);
  return fetch(url, { dispatcher: agent });
}
```

## Sticky sessions (residential)

```js
import crypto from "node:crypto";

function sessionUrl(country = "us") {
  const sid = crypto.randomBytes(4).toString("hex");
  const user = `helo_s1a2b3c4d5e-type-res-region-${country}-session-${sid}-sesstime-10`;
  return `http://${user}:PASSWORD@gate.helodata.io:7777`;
}
```

## Verify

```js
const ip = (await (await fetch("https://ipv4.icanhazip.com", { dispatcher: agent })).text()).trim();
console.log(ip);
```

## Common pitfalls

* **`HTTP_PROXY` envvar mode in `undici`** — pre-1.x undici didn't honor env vars. Use `ProxyAgent` explicitly.
* **axios + `proxy: { host, port }`** — that syntax does **not** use HTTP-CONNECT, it tries a request-rewrite that breaks HTTPS. Always use an `httpAgent`/`httpsAgent`.
* **Connection reuse** — `undici.Agent` (the default dispatcher) pools connections; if you mint a new `ProxyAgent` per request you lose the pool. For ISP, share one agent per IP.


# Operating systems


# Windows

System-wide HTTP/HTTPS proxy — all apps that honor Windows proxy settings (Edge, Chrome by default, .NET apps, most CLI tools) will route through helodata.

## Settings UI (Windows 10/11)

1. **Settings → Network & Internet → Proxy → Manual proxy setup**.
2. Toggle **Use a proxy server** on.
3. Address: `gate.helodata.io` (or your ISP IP). Port: `7777`.
4. Click **Save**.
5. The next outbound request prompts for credentials:
   * Username: full proxy username (e.g. `helo_s1a2b3c4d5e-type-res-region-us`)
   * Password: sub-user password

The Settings UI does **not** persist proxy credentials — they're stored per app. For unattended use, prefer PowerShell or `netsh`.

\[screenshot: Windows 11 Proxy settings pane]

## PowerShell (programmatic)

```powershell
$proxy = "http://gate.helodata.io:7777"
Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' `
                 -Name ProxyServer -Value $proxy
Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' `
                 -Name ProxyEnable -Value 1
```

To remove:

```powershell
Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' `
                 -Name ProxyEnable -Value 0
```

## CLI tools

curl on Windows reads `HTTP_PROXY` / `HTTPS_PROXY`:

```cmd
set HTTP_PROXY=http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777
set HTTPS_PROXY=%HTTP_PROXY%
curl https://ipv4.icanhazip.com
```

For permanent envvars use **System Properties → Environment Variables**.

## WinHTTP (system services, .NET)

For Windows services and .NET HttpClient (which uses WinHTTP, not the user proxy), set the system-level proxy:

```cmd
netsh winhttp set proxy "gate.helodata.io:7777"
netsh winhttp show proxy
```

To revert:

```cmd
netsh winhttp reset proxy
```

WinHTTP cannot embed credentials in the proxy URL — the calling app must supply them via `IWebProxy.Credentials`.

## Verify

```cmd
curl https://ipv4.icanhazip.com
```

Or browse to that URL in Edge.

## Common pitfalls

* **Two proxy systems** (WinINet for user apps vs WinHTTP for services) — you may need both.
* **VPN clients** often install themselves as a higher-priority proxy hook. Disconnect VPN to test.
* **`.local` and corporate suffixes** bypass automatically — useful but watch the bypass list.


# macOS

System-wide proxy for any app honoring macOS network settings — Safari, Mail, App Store, most third-party apps.

## System Settings UI

1. **System Settings → Network → {active interface, e.g. Wi-Fi} → Details… → Proxies**.
2. Tick **Web Proxy (HTTP)** and **Secure Web Proxy (HTTPS)**.
3. For each:
   * Server: `gate.helodata.io` (or ISP IP), Port: `7777`
   * Tick **Proxy server requires password**, fill in username and password
4. **OK → Apply**.

\[screenshot: macOS Proxies pane]

See [Safari](/integrations/browsers/safari) for the same flow with screenshots.

## CLI tools

curl, wget, brew, npm, and most Unix tools read environment variables. Add to `~/.zshrc` or `~/.bash_profile`:

```bash
export HTTP_PROXY="http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
export HTTPS_PROXY="$HTTP_PROXY"
export NO_PROXY="localhost,127.0.0.1,*.local"
```

Reload shell, then `curl https://ipv4.icanhazip.com` should report a helodata IP.

To set only for the current shell, omit the `export` from rc files and run them directly.

## `networksetup` (scripted)

```bash
# Turn on
sudo networksetup -setwebproxy "Wi-Fi" gate.helodata.io 7777 on \
     "helo_s1a2b3c4d5e-type-res-region-us" "PASSWORD"
sudo networksetup -setsecurewebproxy "Wi-Fi" gate.helodata.io 7777 on \
     "helo_s1a2b3c4d5e-type-res-region-us" "PASSWORD"

# Turn off
sudo networksetup -setwebproxystate "Wi-Fi" off
sudo networksetup -setsecurewebproxystate "Wi-Fi" off
```

Replace `Wi-Fi` with your active interface name (find it via `networksetup -listallnetworkservices`).

## Per-app vs system

GUI apps honor the System Settings proxy. CLI apps honor envvars. They're two systems — make sure both are configured for unified behavior.

## Verify

```bash
curl https://ipv4.icanhazip.com
```

## Common pitfalls

* **`.local` bypass** — macOS bypasses `.local` and link-local IPs by default. Useful for Bonjour but watch your bypass list.
* **`networksetup` requires sudo** to modify proxies on a shared system.
* **Big Sur+ requires you to "Apply" in System Settings** after editing — without it the change doesn't take effect.


# Linux

Linux doesn't have one universal proxy setting — there's the shell environment, the desktop environment (GNOME / KDE), and per-app config. This page covers the patterns you actually need.

## Environment variables (cli + most apps)

The de-facto standard. Works for curl, wget, apt, pip, npm, git, and most server software.

```bash
export http_proxy="http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
export https_proxy="$http_proxy"
export no_proxy="localhost,127.0.0.1,.local"
# Also set the uppercase variants — some apps only honor those:
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
export NO_PROXY="$no_proxy"
```

Put it in `~/.bashrc`, `~/.zshrc`, or `/etc/environment` for persistence.

## GNOME

```bash
gsettings set org.gnome.system.proxy mode 'manual'
gsettings set org.gnome.system.proxy.http  host 'gate.helodata.io'
gsettings set org.gnome.system.proxy.http  port 7777
gsettings set org.gnome.system.proxy.https host 'gate.helodata.io'
gsettings set org.gnome.system.proxy.https port 7777
```

GNOME's proxy dialog does not store credentials — set them via the secret-tool or per-app. Easier: just use envvars from a terminal-launched session.

## KDE

**System Settings → Network → Proxy → Use manually specified proxy**. Fill the same host/port. KDE stores creds in KWallet.

## apt (Debian / Ubuntu)

Create `/etc/apt/apt.conf.d/99helo-proxy`:

```
Acquire::http::Proxy "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777";
Acquire::https::Proxy "http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777";
```

## yum / dnf (RHEL / Fedora)

Add to `/etc/yum.conf` (yum) or `/etc/dnf/dnf.conf` (dnf):

```
proxy=http://gate.helodata.io:7777
proxy_username=helo_s1a2b3c4d5e-type-res-region-us
proxy_password=PASSWORD
```

## Docker

Daemon-level (affects image pulls):

```bash
mkdir -p /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/proxy.conf <<EOF
[Service]
Environment="HTTP_PROXY=http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
Environment="HTTPS_PROXY=http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
EOF
systemctl daemon-reload
systemctl restart docker
```

Container-level — pass `--env http_proxy=…` per `docker run` or build.

## systemd services

For a service to pick up a proxy, add to its unit file:

```
[Service]
Environment="HTTP_PROXY=http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
Environment="HTTPS_PROXY=http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
```

then `systemctl daemon-reload && systemctl restart <service>`.

## Verify

```bash
curl https://ipv4.icanhazip.com
```

## Common pitfalls

* **Case-sensitive envvars** — many apps only respect lowercase `http_proxy`, some only uppercase. Set both.
* **`localhost` bypass missing** — without `no_proxy=localhost`, local dev servers route through helodata and fail.
* **Special characters in password** — URL-encode (`@` → `%40`, `:` → `%3A`) when embedding in `http_proxy`.


# Mobile


# Android

Android lets you set a per-Wi-Fi proxy. The catch: native authentication for HTTP proxies is limited — modern Android prompts for credentials in-app rather than at OS level, so apps that don't honor the system proxy auth dialog will fail.

## Per-Wi-Fi proxy (Android 10+)

1. **Settings → Wi-Fi → {your network} → Modify network → Advanced options**.
2. Proxy: **Manual**.
3. Hostname: `gate.helodata.io` (or ISP IP).
4. Port: `7777`.
5. Bypass: `localhost,127.0.0.1`.
6. **Save**.

\[screenshot: Android Wi-Fi proxy form]

Android **does not** have a field for proxy auth — credentials must be entered when the first authenticated app prompts.

## When the OS-level proxy isn't enough

Apps that ship their own networking stack (e.g. many games, some social apps) ignore the Wi-Fi proxy entirely. For those, use either:

* **Drony** (a free VPN-style app that intercepts traffic and routes via proxy) — works for non-rooted devices and supports auth
* **NetGuard** or **Tasker + AutoTools** for app-level rules
* **Rooted device + ProxyDroid** for true global proxy with auth

These tools route all device traffic through helodata; configure them with:

| Field    | Value                                                     |
| -------- | --------------------------------------------------------- |
| Type     | HTTP or SOCKS5                                            |
| Host     | `gate.helodata.io` (or ISP IP)                            |
| Port     | `7777`                                                    |
| Username | full proxy username (gateway) or `helo_s1a2b3c4d5e` (ISP) |
| Password | sub-user password                                         |

## ADB for testing

```bash
adb shell settings put global http_proxy "gate.helodata.io:7777"
```

To clear:

```bash
adb shell settings put global http_proxy :0
```

## Verify

Open the device browser to `https://ipv4.icanhazip.com`.

## Common pitfalls

* **App ignoring system proxy** — common with apps that pin SSL or roll their own HTTP stack. Use Drony or rooted tools.
* **HTTPS targets failing in Chrome on Android** — typically a captive portal or DNS issue. Verify with `chrome://net-internals/#proxy`.
* **Per-network setting** — moving to mobile data drops the proxy. Re-configure per Wi-Fi network.


# iOS

iOS lets you configure a per-Wi-Fi HTTP proxy with authentication built into the UI — easier than Android.

## Per-Wi-Fi proxy

1. **Settings → Wi-Fi → {your network → ⓘ}**.
2. Scroll to **HTTP Proxy → Configure Proxy → Manual**.
3. Fill in:
   * Server: `gate.helodata.io` (or ISP IP)
   * Port: `7777`
   * **Authentication**: on
   * Username: full proxy username
   * Password: sub-user password
4. **Save**.

\[screenshot: iOS HTTP Proxy form]

This affects all apps on the device when connected to that Wi-Fi — including Safari, Mail, App Store, and any app that uses the system HTTP stack.

## What doesn't work

* **Cellular data** — there's no system proxy field for cellular. Use Wi-Fi or [Shadowrocket](/integrations/mobile/shadowrocket).
* **Apps with their own network stack** — some games, some banking apps. Use Shadowrocket to force OS-level routing.
* **SOCKS5** — iOS native UI doesn't support SOCKS proxies. Use Shadowrocket.

## Auto / PAC config

In the Configure Proxy screen, pick **Automatic** and supply a PAC URL — useful for switching between targets without re-typing creds:

```javascript
function FindProxyForURL(url, host) {
  if (host === "example.com")
    return "PROXY gate.helodata.io:7777";
  return "DIRECT";
}
```

Host a PAC file on any HTTPS URL and point iOS at it.

## Verify

In Safari, visit `https://ipv4.icanhazip.com` — expect a helodata IP, not your home network's.

## Common pitfalls

* **Per-network only** — different Wi-Fi networks each need their own proxy config.
* **Captive portals** — iOS shows the captive portal regardless of proxy, but post-login traffic uses the proxy. If portal pages don't render, briefly disable the proxy.
* **App-bound certs** — apps that pin their own TLS certs won't accept any proxy intercepting their connections. helodata doesn't intercept (it's a forwarder), so this rarely matters — but if you also run a MITM debug proxy alongside, that breaks pinning.


# Shadowrocket

Shadowrocket (iOS) routes **all** device traffic — including cellular and apps with their own network stack — through a configured proxy. It's the most flexible iOS proxy client.

## Add a proxy

1. Open Shadowrocket → **+** in the top right.
2. Type: **HTTPS** (for our HTTP CONNECT proxy on TLS-aware port) or **HTTP** for plain.
3. Fill in:

### Residential / Mobile (gateway)

| Field    | Value                                 |
| -------- | ------------------------------------- |
| Type     | HTTP                                  |
| Host     | `gate.helodata.io`                    |
| Port     | `7777`                                |
| User     | `helo_s1a2b3c4d5e-type-res-region-us` |
| Password | sub-user password                     |

### ISP

| Field    | Value              |
| -------- | ------------------ |
| Type     | HTTP               |
| Host     | `198.51.100.42`    |
| Port     | `8000`             |
| User     | `helo_s1a2b3c4d5e` |
| Password | sub-user password  |

4. **Save** → tap the proxy entry to test (Shadowrocket reports latency and exit IP).
5. Toggle the main "Not Connected" switch to start routing.

\[screenshot: Shadowrocket proxy add form]

## SOCKS5

Type: **Socks5**. Same host/port/credentials as above. Shadowrocket handles the SOCKS5 auth handshake correctly and proxies DNS.

## Rules — proxy only specific apps or domains

Shadowrocket's **Rules** tab lets you direct only certain traffic through helodata, leaving the rest direct.

Example: route only `example.com` traffic via proxy, everything else direct:

```
DOMAIN-SUFFIX,example.com,helo-residential
FINAL,DIRECT
```

Where `helo-residential` is the name of the proxy you added.

## Verify

After connecting, open Safari → `https://ipv4.icanhazip.com`. Expect a helodata IP.

## Common pitfalls

* **VPN profile permission required** — first launch asks you to install a VPN profile. Approve it; Shadowrocket uses iOS Network Extension to capture traffic.
* **Battery drain** — system-level traffic routing is more battery-intensive than a Wi-Fi proxy. Disable when not in use.
* **Some apps detect VPN-style routing** — banking and some streaming apps refuse to run with any Network Extension active. Use the per-app routing rules to exempt them.


# AI & workflow


# Postman

Postman supports per-collection / per-request proxies for both gateway and ISP products. Recommended for testing username variants quickly.

## Global proxy

1. **Settings → Proxy → Use custom proxy configuration**.
2. Server: `gate.helodata.io`, Port: `7777` (or your ISP IP).
3. **Proxy Auth → on**. Username + password.
4. Toggle **Proxy** in the top-right action area.

\[screenshot: Postman Proxy settings tab]

This affects every request in the workspace.

## Per-environment

Create environments named `helo-us`, `helo-de`, etc. Each holds the proxy username variant:

| Variable     | Value                                 |
| ------------ | ------------------------------------- |
| `proxy_user` | `helo_s1a2b3c4d5e-type-res-region-us` |
| `proxy_pass` | sub-user password                     |

Then in **Settings → Proxy** reference them: `{{proxy_user}}`, `{{proxy_pass}}`. Switching the environment switches geo.

## Pre-request script (dynamic session)

Mint a fresh session ID per request:

```js
const sid = Math.random().toString(36).slice(2, 10);
pm.environment.set("proxy_user",
  `helo_s1a2b3c4d5e-type-res-region-us-session-${sid}-sesstime-10`
);
```

## Postman collection (provided)

helodata publishes an official collection covering:

* All REST API endpoints (sub-users, orders, traffic, whitelists, …)
* One sample request per product type with the right username structure

Import URL:

```
https://api.helodata.com/v1/postman/collection.json
```

See [Postman collection](/api-reference/postman) for details.

## Verify

Add a request to `https://ipv4.icanhazip.com`. Send — the body should show a helodata exit IP.

## Common pitfalls

* **Postman desktop vs web** — only the desktop app honors the proxy setting; the web client tunnels through Postman's own servers.
* **Newman (CLI runner)** uses the same settings file. For CI runs, pass `--global-proxy` or set `HTTPS_PROXY` envvar before `newman run`.
* **Self-signed certs at the target** — Postman validates upstream certs. helodata doesn't re-issue, so a target self-signed cert still fails; toggle **SSL certificate verification** off if needed.


# LangChain

LangChain pipelines that fetch web content (loaders, retrievers, web-search tools) can route through helodata to avoid IP-based blocks during LLM-driven scraping or RAG ingestion.

## Document loaders

`WebBaseLoader`, `PlaywrightURLLoader`, and friends use `requests` / `httpx` / Playwright under the hood. Set the proxy on the underlying client.

### `WebBaseLoader`

```python
from langchain_community.document_loaders import WebBaseLoader

USER = "helo_s1a2b3c4d5e-type-res-region-us"
PASS = "PASSWORD"
proxy = f"http://{USER}:{PASS}@gate.helodata.io:7777"

loader = WebBaseLoader("https://example.com")
loader.requests_kwargs = {
    "proxies": {"http": proxy, "https": proxy},
    "timeout": 30,
}
docs = loader.load()
```

### `PlaywrightURLLoader`

```python
from langchain_community.document_loaders import PlaywrightURLLoader

loader = PlaywrightURLLoader(
    urls=["https://example.com"],
    remove_selectors=["nav", "footer"],
    continue_on_failure=True,
    headless=True,
    launch_options={
        "proxy": {
            "server":   "http://gate.helodata.io:7777",
            "username": "helo_s1a2b3c4d5e-type-res-region-us",
            "password": "PASSWORD",
        }
    },
)
docs = loader.load()
```

## Web search tools

Tools like `TavilySearch` or `DuckDuckGoSearchRun` hit external APIs that may or may not need proxying. For tools backed by HTTP scraping (e.g. `RequestsGetTool`), inject the proxied client:

```python
from langchain.tools.requests.tool import RequestsGetTool
from langchain_community.utilities.requests import TextRequestsWrapper

wrapper = TextRequestsWrapper(
    headers={"User-Agent": "Mozilla/5.0"},
    proxies={"http": proxy, "https": proxy},
)
tool = RequestsGetTool(requests_wrapper=wrapper, allow_dangerous_requests=True)
```

## Sticky session per LLM call

LangChain agents often make multiple sub-requests during one chain. Use a sticky session so the target sees consistent IP behaviour:

```python
import random, string
sid = "".join(random.choices(string.ascii_lowercase + string.digits, k=8))
user = f"helo_s1a2b3c4d5e-type-res-region-us-session-{sid}-sesstime-30"
proxy = f"http://{user}:PASSWORD@gate.helodata.io:7777"
```

Re-use `proxy` across all sub-requests within the chain.

## Tagging for analytics

Pass `X-Helodata-Tag` in headers to separate LLM-driven traffic from your other scraping:

```python
wrapper = TextRequestsWrapper(
    headers={"X-Helodata-Tag": "llm-agent-prod"},
    proxies={"http": proxy, "https": proxy},
)
```

## Common pitfalls

* **`UnstructuredURLLoader` ignores proxies** in some versions — switch to `WebBaseLoader` or pre-download with `requests`.
* **Async loaders** — wrap the client in `httpx.AsyncClient(proxy=proxy)` for proxy support; `aiohttp` uses `proxy=` per request.


# n8n

n8n is a workflow automation tool whose HTTP Request node accepts a proxy per request. Useful for low-code pipelines that scrape, monitor, or post to web services through helodata.

## HTTP Request node

1. Drop an **HTTP Request** node.
2. **Settings → Proxy** (under the gear icon).
3. Fill in:

### Residential / Mobile (gateway)

| Field     | Value                                                                       |
| --------- | --------------------------------------------------------------------------- |
| Proxy URL | `http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777` |

### ISP

| Field     | Value                                                 |
| --------- | ----------------------------------------------------- |
| Proxy URL | `http://helo_s1a2b3c4d5e:PASSWORD@198.51.100.42:8000` |

4. **Save → Execute Node** to test.

\[screenshot: n8n HTTP Request node with Proxy URL field]

## Workflow-wide proxy via env vars

For the n8n process itself (so every HTTP node inherits), set environment variables before launch:

```bash
export HTTP_PROXY="http://helo_s1a2b3c4d5e-type-res-region-us:PASSWORD@gate.helodata.io:7777"
export HTTPS_PROXY="$HTTP_PROXY"
n8n start
```

n8n's underlying axios/HTTP layer honors these.

## Rotating IPs across runs

Use n8n's **Function** node to generate a fresh session per execution:

```js
const sid = Math.random().toString(36).slice(2, 10);
const user = `helo_s1a2b3c4d5e-type-res-region-us-session-${sid}-sesstime-10`;
return [{
  json: {
    proxy_url: `http://${user}:PASSWORD@gate.helodata.io:7777`,
  },
}];
```

Pass `{{ $json.proxy_url }}` into the HTTP Request node's Proxy field.

## ISP IP pool

Store your ISP list in a **Set** node (or fetch from a sheet/DB), pick a row via a **Function** node, and feed it into HTTP Request:

```js
const ips = $input.first().json.lines;       // array of "ip:port:user:pass"
const line = ips[Math.floor(Math.random() * ips.length)];
const [ip, port, user, pass] = line.split(":");
return [{ json: { proxy_url: `http://${user}:${pass}@${ip}:${port}` } }];
```

## Verify

Point the HTTP Request at `https://ipv4.icanhazip.com` and inspect the response.

## Common pitfalls

* **Self-hosted n8n behind a corporate proxy** — n8n's HTTP node doesn't chain proxies. Only the last-set proxy wins.
* **Special characters in password** — URL-encode (`@` → `%40`).
* **Webhook trigger nodes** don't go through proxies — they receive inbound traffic. Only outbound HTTP Request nodes do.


# MCP server

The Model Context Protocol (MCP) lets LLM-driven agents (Claude Desktop, Cursor, Cline, others) call external tools. helodata ships an MCP server that exposes proxied fetch + ISP/session management as tools an agent can use directly.

## What the server exposes

| Tool                  | Description                                                                              |
| --------------------- | ---------------------------------------------------------------------------------------- |
| `helo_fetch`          | Fetch a URL through helodata; choose product (`res`/`mob`/`isp`), region, sticky session |
| `helo_list_locations` | Return available regions/cities/ASNs for `res` and `mob`                                 |
| `helo_isp_list`       | Return your purchased ISP IPs                                                            |
| `helo_traffic_today`  | Today's bandwidth usage by product and sub-user                                          |

## Install

```bash
npm i -g @helodata/mcp-server
```

Or run directly via `npx`:

```bash
npx -y @helodata/mcp-server --api-key=YOUR_HELODATA_API_KEY
```

## Configure in Claude Desktop

Edit `claude_desktop_config.json` (Settings → Developer → Edit Config):

```json
{
  "mcpServers": {
    "helodata": {
      "command": "npx",
      "args": ["-y", "@helodata/mcp-server"],
      "env": {
        "HELODATA_API_KEY":      "YOUR_API_KEY",
        "HELODATA_SUBUSER":      "s1a2b3c4d5e",
        "HELODATA_SUBUSER_PASS": "PASSWORD"
      }
    }
  }
}
```

Restart Claude Desktop. The new tools appear in the tool list when prompted.

## Configure in Cursor / Cline

Same JSON shape under `mcp` (Cursor) or `mcp_servers` (Cline). See each tool's MCP docs for the exact field name.

## Example agent prompt

> "Use the `helo_fetch` tool to get the homepage of `https://example.de` from a German residential IP, and summarize the meta description."

The agent picks `helo_fetch` with `region=de`, `type=res`, gets the page, parses, summarizes.

## Sticky sessions in MCP

`helo_fetch` accepts a `session_id` parameter. Pass the same value across multiple tool calls in one conversation to reuse the same exit IP — useful for multi-step flows.

## Security note

The MCP server uses your API key to bill and authenticate. Treat it like a database password. Scope the API key to read-only operations if your agent doesn't need to manage orders / sub-users.

## Common pitfalls

* **MCP server not appearing** — check Claude Desktop logs at `~/Library/Logs/Claude/mcp.log` (macOS) or equivalent. Most often it's a Node version mismatch (require Node 18+).
* **`helo_fetch` returns 407** — the env var creds in the config are wrong; verify in the dashboard.
* **Large responses** — `helo_fetch` truncates above 1 MB by default; raise via the `max_bytes` parameter when the agent asks for full HTML.


# Overview

The helodata REST API lets you manage everything you'd otherwise click in the dashboard — sub-users, orders, whitelists, traffic reports — programmatically. Use it for provisioning, billing pipelines, and custom dashboards.

The API is **separate from the proxy gateway**: the gateway lives at `gate.helodata.io`, the API at `api.helodata.com`.

## Base URL

```
https://api.helodata.com/v1
```

All endpoints are versioned in the URL. Breaking changes ship as a new major version with a 6-month deprecation window — see [Changelog](/resources/changelog).

## Authentication

Every API call needs a Bearer token issued from **Dashboard → API**. See [Authentication](/api-reference/authentication) for details.

```bash
curl -H "Authorization: Bearer API_KEY" https://api.helodata.com/v1/users/me
```

## Resource overview

| Resource                                      | Endpoint                  | Purpose                                 |
| --------------------------------------------- | ------------------------- | --------------------------------------- |
| [Users](/api-reference/users)                 | `/users`                  | Account profile and security            |
| [Sub-users](/api-reference/sub-users)         | `/subusers`               | Per-product credentials and limits      |
| [Orders](/api-reference/orders)               | `/orders`, `/isp/batches` | Buying and managing IP batches          |
| [Subscriptions](/api-reference/subscriptions) | `/subscriptions`          | Recurring traffic and IP plans          |
| [Traffic & usage](/api-reference/traffic)     | `/{product}/traffic`      | Bandwidth and request analytics         |
| [Whitelists](/api-reference/whitelists)       | `/whitelists`             | IP allowlists for auth                  |
| [Sessions](/api-reference/sessions)           | `/{product}/sessions`     | Inspect active sticky sessions          |
| [Reports](/api-reference/reports)             | `/reports`                | Scheduled or ad-hoc exports             |
| [Rate limits](/api-reference/rate-limits)     | —                         | What we throttle and when               |
| [Postman collection](/api-reference/postman)  | —                         | Importable collection for the whole API |

## Conventions

* **Content type**: `application/json` on all writes
* **Dates**: RFC 3339 / ISO 8601 in UTC
* **IDs**: ULIDs prefixed by resource type (`sub_01HX…`, `bat_01HX…`, `ord_01HX…`)
* **Pagination**: cursor-based via `?cursor=…&limit=…`; `Link` header points to the next page
* **Errors**: structured JSON with `code`, `message`, `request_id`

Example error:

```json
{
  "code":       "invalid_argument",
  "message":    "region must be a 2-letter ISO 3166 code",
  "request_id": "req_01HX2K3PQ4M5",
  "field":      "region"
}
```

Include the `request_id` when contacting support so we can trace the call.

## Idempotency

Mutating endpoints accept an `Idempotency-Key` header — any string up to 64 chars. Retries that share the same key get the same response, so you can safely retry network failures.

```bash
curl -X POST \
     -H "Authorization: Bearer API_KEY" \
     -H "Idempotency-Key: order-2026-05-28-001" \
     -H "Content-Type: application/json" \
     -d '{"product":"residential","gb":50}' \
     https://api.helodata.com/v1/orders
```

## Webhooks

Subscribe to events (order completed, batch expiring, traffic threshold) via **Dashboard → API → Webhooks**. Payload format and signing key are documented in the Webhooks page (coming soon).


# Authentication

The helodata REST API authenticates with **Bearer tokens**. Each token belongs to your account and can be scoped to specific resources or actions.

## Get a token

1. **Dashboard → API → New token**.
2. Pick a name and scopes (see below).
3. Copy the token — shown once. We store only a hash; lost tokens cannot be recovered, only rotated.

## Use a token

Send it in the `Authorization` header as `Bearer <token>`:

```bash
curl -H "Authorization: Bearer hel_live_01HXabc..." \
     https://api.helodata.com/v1/users/me
```

## Scopes

| Scope              | Permits                                |
| ------------------ | -------------------------------------- |
| `read`             | Read-only on every resource            |
| `subusers:write`   | Create / update / delete sub-users     |
| `whitelists:write` | Manage IP whitelists                   |
| `orders:write`     | Place and modify orders                |
| `traffic:read`     | Read traffic and usage                 |
| `reports:write`    | Create scheduled reports               |
| `*`                | Full account access (use with caution) |

Scope at the **minimum** needed by each consumer. A common pattern: one read-only token for analytics dashboards, a separate `orders:write` token for the provisioning service.

## Token format

```
hel_live_01HXabc...                # production
hel_test_01HXabc...                # sandbox (coming soon)
```

64 chars after the prefix; cryptographically random.

## Rotation

To rotate a token:

1. **Dashboard → API → {token} → Rotate**.
2. A new token is generated. The old one keeps working for 24 hours, then begins returning `401`.
3. Roll the new token through your deployments before the 24-hour grace expires.

Programmatically:

```bash
curl -X POST -H "Authorization: Bearer EXISTING_KEY" \
     "https://api.helodata.com/v1/tokens/{token_id}/rotate"
```

Returns the new token in the response body.

## Revoke

```bash
curl -X DELETE -H "Authorization: Bearer EXISTING_KEY" \
     "https://api.helodata.com/v1/tokens/{token_id}"
```

Revoked tokens stop working immediately.

## IP allowlist on a token (optional)

Restrict a token to specific source IPs in **Dashboard → API → {token} → Source IPs**. Useful for tokens that only run from a known production environment.

## Error codes

| Status | Code                    | Meaning                                   |
| ------ | ----------------------- | ----------------------------------------- |
| `401`  | `missing_token`         | No `Authorization` header                 |
| `401`  | `invalid_token`         | Token doesn't exist or is malformed       |
| `401`  | `revoked_token`         | Token has been revoked                    |
| `401`  | `expired_token`         | Token's 24-hour rotation grace has ended  |
| `403`  | `insufficient_scope`    | Token lacks the scope this endpoint needs |
| `403`  | `source_ip_not_allowed` | Caller IP isn't on this token's allowlist |

## Security best practices

* **Never commit tokens** to git. Use a secret manager.
* **One token per consumer** — easier to scope, easier to revoke if leaked.
* **Rotate quarterly** at minimum.
* **Monitor** the token's last-used timestamp in **Dashboard → API**; unused tokens should be revoked.


# Users

Manage your own account profile and security settings. Most teams interact with these endpoints rarely — they're mostly read-only.

## Endpoints

| Method  | Path                     | Purpose                |
| ------- | ------------------------ | ---------------------- |
| `GET`   | `/users/me`              | The current account    |
| `PATCH` | `/users/me`              | Update profile fields  |
| `GET`   | `/users/me/security`     | MFA status, last login |
| `POST`  | `/users/me/security/mfa` | Enroll MFA             |

## `GET /users/me`

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/users/me
```

Response:

```json
{
  "id":          "usr_01HX2K3PQ4M5",
  "email":       "you@example.com",
  "company":     "Acme Inc.",
  "plan":        "pro",
  "kyc_status":  "approved",
  "created_at":  "2026-01-15T10:00:00Z",
  "balance": {
    "currency": "USD",
    "amount":   125.40
  }
}
```

### Fields

| Field            | Type           | Description                                                     |
| ---------------- | -------------- | --------------------------------------------------------------- |
| `id`             | string         | Account ULID                                                    |
| `email`          | string         | Login email                                                     |
| `company`        | string \| null | Optional company name                                           |
| `plan`           | string         | One of `trial`, `pay-as-you-go`, `starter`, `pro`, `enterprise` |
| `kyc_status`     | string         | `not-submitted`, `pending`, `approved`, `rejected`              |
| `balance.amount` | number         | Prepaid credit in `balance.currency`                            |

## `PATCH /users/me`

Update profile fields (email change requires re-verification, handled in the dashboard):

```bash
curl -X PATCH -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"company": "Acme Subsidiary"}' \
     https://api.helodata.com/v1/users/me
```

Editable fields: `company`, `billing_address`, `tax_id`, `default_currency`, `notification_email`.

## Security

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/users/me/security
```

Response:

```json
{
  "mfa_enabled":   true,
  "mfa_method":    "totp",
  "last_login_at": "2026-05-28T07:14:22Z",
  "last_login_ip": "203.0.113.5"
}
```

MFA enrollment via the API returns a TOTP secret + recovery codes:

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"method": "totp"}' \
     https://api.helodata.com/v1/users/me/security/mfa
```

Returned `secret` is what you put into your authenticator app. `recovery_codes` are one-shot fallbacks; store them offline.

## Errors

| Status | Code                      | Meaning                                         |
| ------ | ------------------------- | ----------------------------------------------- |
| `403`  | `mfa_required`            | The account requires MFA for this action        |
| `409`  | `email_in_use`            | Trying to change email to one already on file   |
| `422`  | `invalid_billing_address` | Address didn't pass tax-jurisdiction validation |


# Sub-users

Sub-users are the unit of access control on the proxy gateway. Every proxy request must reference one, and each sub-user has its own password, limits, and per-product scope.

## Endpoints

| Method   | Path                             | Purpose                        |
| -------- | -------------------------------- | ------------------------------ |
| `GET`    | `/subusers`                      | List all sub-users             |
| `POST`   | `/subusers`                      | Create a sub-user              |
| `GET`    | `/subusers/{id}`                 | Get one sub-user               |
| `PATCH`  | `/subusers/{id}`                 | Update name, limits, or status |
| `POST`   | `/subusers/{id}/rotate-password` | Rotate the password            |
| `DELETE` | `/subusers/{id}`                 | Delete (irreversible)          |

## List

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/subusers?product=residential
```

Response:

```json
{
  "data": [
    {
      "id":              "sub_01HX2K3PQ4M5",
      "name":            "s1a2b3c4d5e",
      "label":           "acme-prod",
      "products":        ["residential", "mobile"],
      "status":          "active",
      "concurrent_max":  600,
      "rps_max":         1000,
      "created_at":      "2026-05-01T00:00:00Z"
    }
  ],
  "next_cursor": null
}
```

Filter with `product=residential|mobile|isp`, `status=active|disabled`, `label_contains=...`.

## Create

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "label":    "acme-staging",
       "products": ["residential"],
       "concurrent_max": 200,
       "rps_max":        500
     }' \
     https://api.helodata.com/v1/subusers
```

Response:

```json
{
  "id":       "sub_01HX2K3PQ4M5",
  "name":     "s9z8y7x6w5v",
  "password": "Mz4yQv8R...",
  "label":    "acme-staging",
  "products": ["residential"]
}
```

> The `password` is shown **only** in this response. Store it immediately; we keep only a hash.

### Field constraints

| Field            | Constraint                                   |
| ---------------- | -------------------------------------------- |
| `label`          | 1–64 chars, `[a-z0-9-]`                      |
| `products`       | Subset of `["residential", "mobile", "isp"]` |
| `concurrent_max` | 1–10000, must not exceed plan limit          |
| `rps_max`        | 1–10000                                      |

## Get one

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/subusers/sub_01HX2K3PQ4M5
```

## Update

```bash
curl -X PATCH -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{"concurrent_max": 1000, "status": "active"}' \
     https://api.helodata.com/v1/subusers/sub_01HX2K3PQ4M5
```

Editable: `label`, `status` (`active` / `disabled`), `concurrent_max`, `rps_max`. To change `products`, delete and recreate.

Setting `status` to `disabled` makes the sub-user immediately return `403` on every proxy request — the credentials still exist, just deactivated.

## Rotate password

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/subusers/sub_01HX2K3PQ4M5/rotate-password
```

Response includes the new password (shown once). The old password works for 60 seconds, then `407`.

## Delete

```bash
curl -X DELETE -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/subusers/sub_01HX2K3PQ4M5
```

Deletes the sub-user. Existing proxy requests in flight may take up to 60 seconds to drain.

## Errors

| Status | Code                | Meaning                                      |
| ------ | ------------------- | -------------------------------------------- |
| `409`  | `label_taken`       | Another sub-user already has this label      |
| `422`  | `over_plan_limit`   | Requested `concurrent_max` exceeds your plan |
| `404`  | `subuser_not_found` | Wrong ID                                     |


# Orders

Orders are how you buy traffic (residential / mobile) or ISP batches. Different products use different sub-resources; both share the `/orders` envelope.

## Common shape

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -H "Idempotency-Key: order-2026-05-28-001" \
     -d '{ "product": "residential", "gb": 50 }' \
     https://api.helodata.com/v1/orders
```

Response:

```json
{
  "id":         "ord_01HX2K3PQ4M5",
  "product":    "residential",
  "status":     "completed",
  "total_usd":  225.00,
  "details":    { "gb": 50, "price_per_gb": 4.50 },
  "created_at": "2026-05-28T10:00:00Z"
}
```

## Residential / Mobile (bandwidth)

```bash
-d '{
  "product": "residential",        // or "mobile"
  "gb":      50
}'
```

Bandwidth is added to your sub-user pool immediately. Traffic is consumed across all sub-users of that product.

## ISP batches

ISP orders create a **batch** — a dedicated set of static IPs.

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "product":  "isp",
       "country":  "us",
       "city":     "newyork",
       "asn":      7922,
       "quantity": 20,
       "months":   1,
       "subuser":  "sub_01HX2K3PQ4M5"
     }' \
     https://api.helodata.com/v1/orders
```

Returns the order envelope plus a `batch_id` you'll use to manage the IPs:

```json
{
  "id":         "ord_01HXabc",
  "product":    "isp",
  "status":     "provisioning",
  "total_usd":  120.00,
  "details": {
    "batch_id": "bat_01HXabc",
    "quantity": 20,
    "country":  "us",
    "city":     "newyork",
    "asn":      7922,
    "expires_at": "2026-06-28T10:00:00Z"
  }
}
```

`status` transitions: `provisioning` → `completed` (typically 1–5 min, up to 24h for long-tail countries).

## ISP batch lifecycle

| Method   | Path                                            | Purpose                                    |
| -------- | ----------------------------------------------- | ------------------------------------------ |
| `GET`    | `/isp/batches`                                  | List batches                               |
| `GET`    | `/isp/batches/{id}`                             | Get one batch                              |
| `GET`    | `/isp/batches/{id}/ips?format=plain\|csv\|json` | Download IPs                               |
| `POST`   | `/isp/batches/{id}/refresh`                     | Refresh all IPs in batch (once per period) |
| `POST`   | `/isp/batches/{id}/renew`                       | Extend billing period                      |
| `POST`   | `/isp/batches/{id}/rotate-password`             | Change batch password                      |
| `DELETE` | `/isp/batches/{id}`                             | Release the entire batch                   |
| `POST`   | `/isp/ips/{ip_id}/replace`                      | Replace one bad IP                         |

### List

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/isp/batches
```

### Replace one IP

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"reason":"target-blocked"}' \
     "https://api.helodata.com/v1/isp/ips/{ip_id}/replace"
```

Free up to 14 days from purchase; afterwards 10% of batch per period is free. See [IP management](/products/overview-2/ip-management).

### Renew

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"months":1}' \
     "https://api.helodata.com/v1/isp/batches/{id}/renew"
```

## Inventory check (before ordering)

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/inventory?country=us&asn=7922"
```

Returns available quantity, cities, and pricing per ASN.

## Errors

| Status | Code                     | Meaning                                                                           |
| ------ | ------------------------ | --------------------------------------------------------------------------------- |
| `409`  | `insufficient_balance`   | Account balance can't cover the order                                             |
| `409`  | `kyc_required`           | Account is under a risk review; complete KYC to proceed (not required by default) |
| `422`  | `insufficient_inventory` | Not enough IPs in the requested geo/ASN                                           |
| `422`  | `quantity_out_of_range`  | Quantity below 5 or above 5000                                                    |


# Subscriptions

Subscriptions are recurring orders — typical for monthly traffic plans (residential / mobile) and monthly ISP renewals. The API mirrors what you'd configure in **Dashboard → Billing → Plans**.

## Endpoints

| Method  | Path                         | Purpose                                        |
| ------- | ---------------------------- | ---------------------------------------------- |
| `GET`   | `/subscriptions`             | List active subscriptions                      |
| `POST`  | `/subscriptions`             | Create a subscription                          |
| `GET`   | `/subscriptions/{id}`        | Get one                                        |
| `PATCH` | `/subscriptions/{id}`        | Modify quantity, plan tier, or cycle           |
| `POST`  | `/subscriptions/{id}/pause`  | Pause renewal (does not cancel current period) |
| `POST`  | `/subscriptions/{id}/cancel` | Cancel (effective end of period)               |

## List

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/subscriptions
```

Response:

```json
{
  "data": [
    {
      "id":           "sbn_01HX2K3PQ4M5",
      "product":      "residential",
      "plan":         "pro",
      "cycle":        "monthly",
      "gb_included":  500,
      "price_usd":    1500.00,
      "status":       "active",
      "renews_at":    "2026-06-01T00:00:00Z"
    },
    {
      "id":           "sbn_01HX2K4ABCD",
      "product":      "isp",
      "batch_id":     "bat_01HXabc",
      "cycle":        "annual",
      "quantity":     20,
      "price_usd":    1200.00,
      "status":       "active",
      "renews_at":    "2027-01-15T00:00:00Z"
    }
  ]
}
```

## Create — bandwidth subscription

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "product": "residential",
       "plan":    "pro",
       "cycle":   "monthly"
     }' \
     https://api.helodata.com/v1/subscriptions
```

Plan tiers (residential): `starter` (50 GB), `pro` (500 GB), `business` (2000 GB), `enterprise` (custom). Mobile uses the same tier names but lower GB caps.

## Create — ISP renewal subscription

ISP batches are usually auto-renewing by default after the first order. To turn auto-renew on for an existing batch:

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{
       "product":  "isp",
       "batch_id": "bat_01HXabc",
       "cycle":    "annual"           // or "monthly"
     }' \
     https://api.helodata.com/v1/subscriptions
```

Annual cycles get a 10% discount over equivalent monthly.

## Modify

Upgrade or downgrade:

```bash
curl -X PATCH -H "Authorization: Bearer API_KEY" \
     -d '{"plan": "business"}' \
     https://api.helodata.com/v1/subscriptions/sbn_01HX2K3PQ4M5
```

Upgrades are prorated for the current period. Downgrades take effect at the next renewal.

For ISP quantity changes:

```bash
curl -X PATCH -H "Authorization: Bearer API_KEY" \
     -d '{"quantity": 30}' \
     https://api.helodata.com/v1/subscriptions/sbn_01HX2K4ABCD
```

Quantity **increases** provision additional IPs immediately and are prorated. Decreases (releasing IPs) take effect at next renewal.

## Pause / cancel

```bash
# Pause — keep the current period's resources, don't renew
curl -X POST -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/subscriptions/sbn_01HX/pause

# Cancel — same effect, plus the subscription is archived
curl -X POST -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/subscriptions/sbn_01HX/cancel
```

After cancel, ISP batches enter the 7-day grace period documented in [IP management](/products/overview-2/ip-management#extend--renew).

## Errors

| Status | Code                    | Meaning                                                         |
| ------ | ----------------------- | --------------------------------------------------------------- |
| `409`  | `downgrade_below_usage` | Trying to downgrade below current period usage                  |
| `409`  | `already_subscribed`    | A subscription already exists for this product+plan combination |
| `422`  | `unknown_plan`          | Plan tier doesn't exist for this product                        |


# Traffic & usage

Read bandwidth and request analytics across products. Same shape per product — only the path prefix changes.

## Endpoints

| Path                   | Product     |
| ---------------------- | ----------- |
| `/residential/traffic` | Residential |
| `/mobile/traffic`      | Mobile      |
| `/isp/traffic`         | ISP         |

| Path                  | Purpose                                     |
| --------------------- | ------------------------------------------- |
| `/{product}/traffic`  | Aggregated bandwidth                        |
| `/{product}/requests` | Sampled request log                         |
| `/{product}/sessions` | Sticky-session inventory (gateway products) |

## Get aggregate traffic

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/traffic?from=2026-05-01&to=2026-05-28&group_by=subuser"
```

### Query parameters

| Param      | Required | Description                                      |
| ---------- | -------- | ------------------------------------------------ |
| `from`     | yes      | RFC 3339 start of range                          |
| `to`       | yes      | RFC 3339 end of range (exclusive)                |
| `group_by` | no       | `subuser`, `region`, `asn`, `tag`, `day`, `hour` |
| `subuser`  | no       | Filter to one sub-user                           |
| `region`   | no       | Filter to one ISO country code                   |
| `tag`      | no       | Filter to one `X-Helodata-Tag` value             |

### Response

```json
{
  "from": "2026-05-01T00:00:00Z",
  "to":   "2026-05-28T00:00:00Z",
  "rows": [
    {
      "subuser":   "sub_01HX2K3PQ4M5",
      "bytes_in":  12345678,
      "bytes_out": 9876543,
      "requests":  4210,
      "errors":    51,
      "error_rate": 0.012
    }
  ]
}
```

ISP adds per-IP rollups; mobile adds `asn` and `carrier_name`:

```json
{ "ip": "198.51.100.42", "bytes_in": 1234567, "bytes_out": 9876543, "requests": 4210 }
{ "asn": 6167, "carrier_name": "Verizon Wireless", "bytes_in": 222345 }
```

## Sampled request log

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/requests?subuser=sub_01HX2K3PQ4M5&limit=100"
```

Returns up to 1,000 sampled requests for the last 48 hours:

```json
{
  "data": [
    {
      "request_id":  "req_01HX2K3PQ4M5",
      "ts":          "2026-05-28T07:14:22Z",
      "subuser":     "sub_01HX2K3PQ4M5",
      "method":      "GET",
      "target_host": "example.com",
      "status":      200,
      "bytes":       38745,
      "exit_ip":     "198.51.100.42",
      "exit_geo":    "us/ca/la",
      "exit_asn":    7922,
      "tag":         "campaign-acme-q2"
    }
  ]
}
```

Useful for spotting hot-spots and debugging individual call patterns. Don't use it for billing — it's sampled, not exhaustive.

## Granularity

Data resolution downsamples over time:

| Age        | Resolution |
| ---------- | ---------- |
| < 24 h     | 1 minute   |
| 1–14 days  | 5 minutes  |
| 14–90 days | 1 hour     |
| > 90 days  | 1 day      |

Pull historical data sooner than your retention window if you need finer detail.

## Errors

| Status | Code               | Meaning                    |
| ------ | ------------------ | -------------------------- |
| `422`  | `range_too_wide`   | `from..to` exceeds 90 days |
| `422`  | `invalid_group_by` | Unknown `group_by` value   |


# Whitelists

Manage IP whitelists for sub-users and ISP batches. Whitelisting an IP lets requests from that source skip `Proxy-Authorization` — useful for keyless server-to-server setups.

## Scoping

Whitelists are **per resource**:

* **Sub-user whitelist** — gateway products (`residential`, `mobile`). Whitelisting an IP authorizes it for that sub-user, on that product.
* **ISP batch whitelist** — authorizes a source IP for every IP in that batch.

A single source IP can be whitelisted in many places independently.

## Endpoints

| Method   | Path                               | Purpose                         |
| -------- | ---------------------------------- | ------------------------------- |
| `GET`    | `/whitelists?subuser={id}`         | List sub-user's whitelisted IPs |
| `POST`   | `/whitelists`                      | Add an IP                       |
| `DELETE` | `/whitelists/{id}`                 | Remove an IP                    |
| `GET`    | `/isp/batches/{id}/whitelist`      | List batch's whitelist          |
| `POST`   | `/isp/batches/{id}/whitelist`      | Add to batch                    |
| `DELETE` | `/isp/batches/{id}/whitelist/{id}` | Remove from batch               |

## Add to a sub-user

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "subuser": "sub_01HX2K3PQ4M5",
       "product": "residential",
       "ip":      "203.0.113.5",
       "label":   "prod-eu-west-1"
     }' \
     https://api.helodata.com/v1/whitelists
```

Response:

```json
{
  "id":         "wl_01HX2K3PQ4M5",
  "subuser":    "sub_01HX2K3PQ4M5",
  "product":    "residential",
  "ip":         "203.0.113.5",
  "label":      "prod-eu-west-1",
  "active":     true,
  "created_at": "2026-05-28T10:00:00Z"
}
```

### IP / CIDR format

| Input                        | Accepted                          |
| ---------------------------- | --------------------------------- |
| Single IPv4 (`203.0.113.5`)  | yes                               |
| IPv4 CIDR (`203.0.113.0/24`) | yes — max `/24` per entry         |
| IPv6                         | yes (full address or up to `/64`) |
| Hostname                     | no                                |

## List

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/whitelists?subuser=sub_01HX2K3PQ4M5&product=residential"
```

## Remove

```bash
curl -X DELETE -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/whitelists/wl_01HX2K3PQ4M5"
```

Effective within 60 seconds.

## ISP batch whitelist

```bash
# List
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/batches/bat_01HXabc/whitelist"

# Add
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"ip":"203.0.113.5","label":"prod"}' \
     "https://api.helodata.com/v1/isp/batches/bat_01HXabc/whitelist"

# Remove
curl -X DELETE -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/isp/batches/bat_01HXabc/whitelist/wl_01HX..."
```

## Limits

| Scope                      | Max entries |
| -------------------------- | ----------- |
| Per sub-user (per product) | 50          |
| Per ISP batch              | 50          |

A `/24` CIDR counts as one entry — the gateway expands it server-side.

## Errors

| Status | Code                  | Meaning                                   |
| ------ | --------------------- | ----------------------------------------- |
| `409`  | `already_whitelisted` | The IP/CIDR is already on this whitelist  |
| `422`  | `invalid_cidr`        | CIDR larger than /24 (IPv4) or /64 (IPv6) |
| `422`  | `over_limit`          | More than 50 entries on this scope        |


# Sessions

Inspect active sticky sessions on gateway products. Useful for monitoring long-running scrapes and for cleaning up sessions that shouldn't be using more bandwidth.

> ISP doesn't have a sessions API — every ISP IP is itself permanent, so there's no transient session state.

## Endpoints

| Method   | Path                       | Purpose                                 |
| -------- | -------------------------- | --------------------------------------- |
| `GET`    | `/residential/sessions`    | List active residential sticky sessions |
| `GET`    | `/mobile/sessions`         | List active mobile sticky sessions      |
| `GET`    | `/{product}/sessions/{id}` | Details for one session                 |
| `DELETE` | `/{product}/sessions/{id}` | Terminate a session early               |

## List

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/sessions?subuser=sub_01HX2K3PQ4M5"
```

Response:

```json
{
  "data": [
    {
      "id":               "ses_01HX2K3PQ4M5",
      "subuser":          "sub_01HX2K3PQ4M5",
      "session_id":       "job1",
      "region":           "us",
      "state":            "ca",
      "city":             "la",
      "asn":              7922,
      "exit_ip":          "198.51.100.42",
      "started_at":       "2026-05-28T07:00:00Z",
      "expires_at":       "2026-05-28T07:30:00Z",
      "bytes_used":       3145728,
      "requests":         42,
      "repinned":         false
    }
  ],
  "next_cursor": null
}
```

Filter parameters: `subuser`, `session_id`, `region`, `asn`, `repinned=true|false`.

### Fields

* `session_id` is the user-chosen part of the username (e.g. `job1` from `-session-job1-sesstime-30`).
* `repinned` flips to `true` if the upstream IP was reassigned mid-session.
* `expires_at` = `started_at + sesstime`.

## Get one

```bash
curl -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/sessions/ses_01HX2K3PQ4M5"
```

Same fields as the list plus per-request samples (last 10 requests).

## Terminate

```bash
curl -X DELETE -H "Authorization: Bearer API_KEY" \
     "https://api.helodata.com/v1/residential/sessions/ses_01HX2K3PQ4M5"
```

Future requests with that exact username get a **new** exit IP, as if `sesstime` had elapsed. The session record is kept for 24h for audit; the entry's `status` becomes `terminated`.

Why terminate?

* A worker crashed and you want to ensure no stale state — minor effect since helodata will expire it anyway at `expires_at`
* You changed your mind about which geo to scrape — easier to drop than wait
* Cleanup as part of incident response

## Concurrency note

`session_id` reuse is allowed; the same `session_id` under different geo/sub-user counts as a **different** session. The full username is the session key.

## Errors

| Status | Code                     | Meaning                                           |
| ------ | ------------------------ | ------------------------------------------------- |
| `404`  | `session_not_found`      | Session expired or never existed                  |
| `403`  | `subuser_scope_mismatch` | Token scope doesn't cover this sub-user's product |


# Reports

Reports give you scheduled or on-demand exports of your account data — useful for invoicing pipelines, weekly summaries to stakeholders, or BI tools.

## Endpoints

| Method   | Path                 | Purpose                                 |
| -------- | -------------------- | --------------------------------------- |
| `GET`    | `/reports`           | List reports (scheduled + recent runs)  |
| `POST`   | `/reports`           | Schedule a new report                   |
| `GET`    | `/reports/{id}`      | Get a report definition                 |
| `PATCH`  | `/reports/{id}`      | Update schedule, fields, or destination |
| `DELETE` | `/reports/{id}`      | Delete a report                         |
| `POST`   | `/reports/{id}/run`  | Run on demand (returns a run ID)        |
| `GET`    | `/reports/runs/{id}` | Get run status and download URL         |

## Schedule

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "name":   "weekly-traffic",
       "type":   "traffic",
       "filters": {
         "products": ["residential", "mobile"]
       },
       "schedule": "0 9 * * MON",
       "format":   "csv",
       "destination": {
         "type":  "email",
         "email": "ops@acme.com"
       }
     }' \
     https://api.helodata.com/v1/reports
```

### Report types

| `type`        | Content                                           |
| ------------- | ------------------------------------------------- |
| `traffic`     | Aggregated bandwidth per sub-user / product / day |
| `requests`    | Sampled per-request log                           |
| `invoices`    | Invoice line items                                |
| `subusers`    | Sub-user inventory snapshot                       |
| `isp_batches` | ISP batch inventory with per-IP health            |

### Destinations

| `type`    | Fields                                                   |
| --------- | -------------------------------------------------------- |
| `email`   | `email`                                                  |
| `webhook` | `url`, `signing_secret`                                  |
| `s3`      | `bucket`, `prefix`, `role_arn` (for cross-account write) |
| `gcs`     | `bucket`, `prefix`, `service_account_email`              |

### Schedules

`schedule` accepts standard 5-field cron in UTC. Or omit `schedule` to keep it on-demand only.

## Run on demand

```bash
curl -X POST -H "Authorization: Bearer API_KEY" \
     -d '{"from":"2026-05-01","to":"2026-05-28"}' \
     "https://api.helodata.com/v1/reports/rep_01HX2K3PQ4M5/run"
```

Returns:

```json
{
  "run_id":  "run_01HX2K3PQ4M5",
  "status":  "queued"
}
```

Poll status:

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/reports/runs/run_01HX2K3PQ4M5
```

When `status` becomes `completed`, the response includes a `download_url` (signed, valid for 1 hour) — or the report was already pushed to your `destination`.

## Formats

| `format`  | Notes                              |
| --------- | ---------------------------------- |
| `csv`     | UTF-8, comma-separated, header row |
| `tsv`     | UTF-8, tab-separated               |
| `json`    | Newline-delimited JSON (NDJSON)    |
| `parquet` | Best for `s3` / `gcs` destinations |

## Retention

Report runs and their downloadable artifacts are retained for **90 days**. For longer retention, push to `s3` / `gcs`.

## Errors

| Status | Code                      | Meaning                                      |
| ------ | ------------------------- | -------------------------------------------- |
| `422`  | `invalid_cron`            | Bad cron expression                          |
| `422`  | `destination_unreachable` | Webhook URL failed pre-flight                |
| `403`  | `feature_not_in_plan`     | Reports are a Pro+ feature; upgrade required |


# Rate limits

helodata enforces three independent layers of rate limiting. Know which one you hit so you can react correctly.

## 1. API rate limits

The REST API (`api.helodata.com`) caps requests per API key:

| Plan       | Burst       | Sustained    |
| ---------- | ----------- | ------------ |
| Trial      | 30 / min    | 1,000 / day  |
| Starter    | 60 / min    | 10,000 / day |
| Pro        | 600 / min   | unlimited    |
| Business   | 1,200 / min | unlimited    |
| Enterprise | custom      | custom       |

Responses include the standard headers:

```
X-RateLimit-Limit:        600
X-RateLimit-Remaining:    582
X-RateLimit-Reset:        1717029600       # Unix epoch
```

Over the limit returns:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 8
{"code":"rate_limited","message":"API rate limit exceeded","request_id":"req_01HX..."}
```

## 2. Gateway concurrent limits

The proxy gateway (`gate.helodata.io`) caps concurrent TCP connections per sub-user:

| Plan       | Residential | Mobile | ISP         |
| ---------- | ----------- | ------ | ----------- |
| Trial      | 25          | 10     | n/a         |
| Starter    | 200         | 50     | per-IP only |
| Pro        | 600         | 200    | per-IP only |
| Business   | 2,000       | 500    | per-IP only |
| Enterprise | custom      | custom | per-IP only |

Over the limit, the gateway returns `429` (helodata code `30002`) with `X-Helodata-Error-Code: concurrent-limit`. Existing connections continue; new ones wait.

ISP IPs have no per-sub-user concurrency cap — each IP can handle hundreds of concurrent TCP connections. The bottleneck is the target site, not us.

## 3. Gateway requests-per-second limits

Distinct from concurrency: how many *new* requests you can fire each second.

| Plan       | Residential RPS | Mobile RPS |
| ---------- | --------------- | ---------- |
| Trial      | 50              | 25         |
| Starter    | 200             | 100        |
| Pro        | 1,000           | 500        |
| Business   | 3,000           | 1,500      |
| Enterprise | custom          | custom     |

Over the limit returns `429` (helodata code `30002`) with `X-Helodata-Error-Code: rps-limit`. Retry with jitter:

```python
import time, random
time.sleep((2 ** attempt) * 0.1 + random.random())
```

## Backoff strategy

Pseudo-code for any 429 from the gateway:

```python
RETRYABLE = {"rps-limit", "concurrent-limit"}

def on_429(resp, attempt):
    err = resp.headers.get("x-helodata-error-code")
    if err not in RETRYABLE:
        return False                      # don't retry config errors
    delay = int(resp.headers.get("retry-after", 0)) or (2 ** attempt + random.random())
    time.sleep(min(delay, 30))
    return True
```

## Raising limits

Per-sub-user limits can be raised within plan limits via `PATCH /subusers/{id}`. Plan-level limits require upgrading. Enterprise customers get custom limits — email <sales@helodata.com>.

## Counters reset

* API key: rolling 60-second window (burst), rolling 24-hour window (daily)
* Concurrent: instantaneous; recovers as soon as connections close
* RPS: rolling 1-second window


# Postman collection

We publish a maintained Postman collection covering every endpoint in this API reference, plus sample proxy requests for the three live products.

## Import

### From URL

In Postman: **Import → Link**, paste:

```
https://api.helodata.com/v1/postman/collection.json
```

The collection updates on every API release. Re-import to refresh.

### From file

Download once, then drag-drop into Postman:

```bash
curl -H "Authorization: Bearer API_KEY" \
     https://api.helodata.com/v1/postman/collection.json \
     -o helodata.postman_collection.json
```

The collection is also shipped with this docs repository at [`assets/helodata.postman_collection.json`](https://github.com/teokunsuper/helodata-docs/blob/main/assets/helodata.postman_collection.json) along with a starter environment [`assets/helodata-prod.postman_environment.json`](https://github.com/teokunsuper/helodata-docs/blob/main/assets/helodata-prod.postman_environment.json) — import both for an out-of-box setup.

## What's included

```
helodata/
├── 0 — Welcome
├── 1 — Auth check (GET /users/me)
├── 2 — Sub-users
│   ├── List
│   ├── Create
│   ├── Get
│   ├── Update
│   ├── Rotate password
│   └── Delete
├── 3 — Orders
│   ├── Create residential
│   ├── Create mobile
│   └── Create ISP batch
├── 4 — ISP batches
│   ├── List
│   ├── Download IPs (plain / csv / json)
│   ├── Replace IP
│   ├── Refresh batch
│   └── Renew
├── 5 — Subscriptions
├── 6 — Traffic & reports
├── 7 — Whitelists
├── 8 — Sessions
└── 9 — Proxy gateway samples
    ├── Residential — basic
    ├── Residential — city + session
    ├── Mobile — ASN-pinned
    └── ISP — single IP
```

## Environment

The collection ships with two environments to import:

* **helodata-prod** — production endpoints
* **helodata-test** — sandbox (coming soon)

Variables you fill in:

| Variable       | Value                                          |
| -------------- | ---------------------------------------------- |
| `api_key`      | Your API token                                 |
| `subuser_name` | e.g. `s1a2b3c4d5e`                             |
| `subuser_pass` | Sub-user password                              |
| `isp_ip`       | One IP from your ISP batch (for proxy samples) |

## Run via Newman (CI)

```bash
newman run helodata.postman_collection.json \
       --environment helodata-prod.postman_environment.json \
       --env-var "api_key=$HELODATA_API_KEY" \
       --folder "1 — Auth check"
```

Useful for smoke-testing your account from CI after credential rotation.

## OpenAPI

If your tools prefer OpenAPI, the same surface is available at:

```
https://api.helodata.com/v1/openapi.json
```

Compatible with Swagger UI, Redoc, Stoplight, and most code generators.

## Versioning

Both `collection.json` and `openapi.json` follow the same `/v1` versioning as the API itself. We publish a new spec **major version** when breaking changes ship; older versions remain importable at `/v0/...`, `/v1/...`, etc.


# Error codes hub

When something fails, three signals tell you what went wrong. Read them in this order:

1. **HTTP status** — the gross category (auth, rate-limit, upstream, target)
2. **`X-Helodata-Error-Code`** response header — the precise reason (only on gateway-originated errors)
3. **`X-Helodata-Request-Id`** — give this to support when you need help

A pasted log line is worth a thousand guesses.

## Quick triage

| Status                 | helodata code | Most likely cause                | First check                                                                                          |
| ---------------------- | ------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `400`                  | `20002`       | Username syntax                  | [Username format](/getting-started/username-format)                                                  |
| `401` / `407`          | `20003`       | Wrong creds / IP not whitelisted | [407 Proxy Authentication Required](/troubleshooting/407)                                            |
| `403`                  | —             | Restricted target or KYC missing | `X-Helodata-Error-Code` and [Restricted targets](/products/overview/restricted-targets)              |
| `429`                  | `30002`       | Rate limit                       | [Rate limits](/api-reference/rate-limits)                                                            |
| `502`                  | `40001`       | Pool exhausted, TLS issue        | [SSL / TLS errors](/troubleshooting/ssl-errors), loosen username filter                              |
| `522`                  | —             | Upstream timeout                 | [522 Connection timed out](/troubleshooting/522)                                                     |
| `524`                  | —             | Target was slow                  | Slow target — retry, raise client timeout                                                            |
| Cloudflare `1020` page | —             | Geo or fingerprint detected      | [CAPTCHAs](/troubleshooting/captchas), [IP location mismatch](/troubleshooting/ip-location-mismatch) |

## How to read a failing request

A real example response:

```
HTTP/1.1 407 Proxy Authentication Required
X-Helodata-Error-Code: not-whitelisted
X-Helodata-Request-Id: req_01HX2K3PQ4M5
Content-Type: application/json

{"code":"not-whitelisted","message":"Source IP 203.0.113.5 isn't on the whitelist for sub-user sub_01HX..."}
```

The three things to extract:

* **Status 407** → auth class
* **`not-whitelisted`** → your source IP isn't on the sub-user's whitelist
* **`req_01HX2K3PQ4M5`** → save it; if you need to ask us, we'll find this specific call

## Common scenarios

| What you observe                        | Likely cause                             | Page                                                          |
| --------------------------------------- | ---------------------------------------- | ------------------------------------------------------------- |
| First request always returns 407        | Wrong username syntax or password        | [407](/troubleshooting/407)                                   |
| Random `522` on a tiny city filter      | Pool too small                           | [522](/troubleshooting/522)                                   |
| Some sites work, others give SSL errors | Target's TLS misconfig or your CA bundle | [SSL/TLS errors](/troubleshooting/ssl-errors)                 |
| Lots of CAPTCHAs from one country       | Fingerprint mismatch or pool burnt       | [CAPTCHAs](/troubleshooting/captchas)                         |
| `Exit-IP` geolocates wrong              | MaxMind lag                              | [IP location mismatch](/troubleshooting/ip-location-mismatch) |
| 5–10 s response times                   | Mobile, or anti-bot delay                | [Slow speeds](/troubleshooting/slow-speeds)                   |
| Page just doesn't render                | JS-heavy site or block                   | [Website won't load](/troubleshooting/website-wont-load)      |

## When you can't tell from the error

Log the trio `status + X-Helodata-Error-Code + X-Helodata-Request-Id` and email <support@helodata.com>. We can pull the full request record from the `Request-Id` for up to 7 days after the call.

## Per-product error tables

The exhaustive code tables live in each product's reference:

* [Residential error definitions](/products/overview/error-definitions)
* [Mobile error definitions](/products/overview-1/error-definitions)
* [ISP error definitions](/products/overview-2/error-definitions)


# 407 Proxy Authentication Required

The most common gateway error — and almost always a config issue, not a helodata fault. The `X-Helodata-Error-Code` header tells you which kind:

## `auth-failed` — wrong password

```
HTTP/1.1 407 Proxy Authentication Required
X-Helodata-Error-Code: auth-failed
```

**Causes**

* Sub-user password is wrong
* Sub-user was deleted
* Password was rotated and your deployment still has the old one

**Fix**

1. Verify the password in **Dashboard → {Product} → Sub-users → {name}**.
2. If wrong, rotate the password and update your deployment within the 60-second grace window.
3. If the sub-user is gone, recreate it.

## `not-whitelisted` — source IP not authorized

```
HTTP/1.1 407 Proxy Authentication Required
X-Helodata-Error-Code: not-whitelisted
```

**Causes**

* You configured the sub-user to require IP whitelisting and your source IP isn't on the list.
* Your egress IP changed (cloud autoscale, new ISP, NAT switch).

**Fix**

1. Find your current egress IP: `curl https://ipv4.icanhazip.com` (without the proxy).
2. Add it to the whitelist via **Dashboard → {Product} → Sub-users → {name} → Whitelisted IPs**, or:

   ```bash
   curl -X POST -H "Authorization: Bearer API_KEY" \
        -d '{"subuser":"sub_01HX...","product":"residential","ip":"203.0.113.5"}' \
        https://api.helodata.com/v1/whitelists
   ```
3. Wait up to 60 seconds for propagation, then retry.

## `bad-username` — username doesn't parse

```
HTTP/1.1 400 Bad Request
X-Helodata-Error-Code: bad-username
```

(Strictly 400, not 407, but conceptually the same family.)

**Causes**

* Missing `helo_` prefix
* Misordered segments (e.g. `city` before `state`)
* Unknown segment names (`-country-` instead of `-region-`)
* Bad characters in `session_id`

**Fix** — re-read [Username format](/getting-started/username-format). A few common mistakes:

| Wrong                                                  | Right                                                         |
| ------------------------------------------------------ | ------------------------------------------------------------- |
| `helo_s1a2b3c4d5e-region-us-type-res`                  | `helo_s1a2b3c4d5e-type-res-region-us`                         |
| `helo_s1a2b3c4d5e-type-res-country-us`                 | `helo_s1a2b3c4d5e-type-res-region-us`                         |
| `helo_s1a2b3c4d5e-type-res-region-US`                  | `helo_s1a2b3c4d5e-type-res-region-us`                         |
| `helo_s1a2b3c4d5e-type-res-region-us-city-la-state-ca` | `helo_s1a2b3c4d5e-type-res-region-us-state-ca-city-la`        |
| `helo_s1a2b3c4d5e-type-res-region-us-session-abc`      | `helo_s1a2b3c4d5e-type-res-region-us-session-abc-sesstime-30` |

## `unknown-subuser` — sub-user ID doesn't exist

```
HTTP/1.1 401 Unauthorized
X-Helodata-Error-Code: unknown-subuser
```

**Cause** — typo in the sub-user name, or you're using a sub-user from a different account.

**Fix** — `curl /v1/subusers` to list the right ones for your API key.

## How to debug 407 once

```bash
curl -v -x "http://USERNAME:PASSWORD@gate.helodata.io:7777" \
     https://ipv4.icanhazip.com
```

`-v` shows the `Proxy-Authorization` header that's being sent. Common things to spot:

* Special characters in password that weren't URL-encoded
* Wrong port (must be `7777` — works for plain HTTP, SOCKS5, and TLS-wrapped)
* Extra whitespace in username from a copy-paste

## Still stuck

Email <support@helodata.com> with the `X-Helodata-Request-Id` from a failed call. We can see exactly which check failed.


# 522 Connection timed out

A `522` from the helodata gateway means the proxy couldn't reach (or finish a handshake with) the upstream IP. It's usually transient — but the `X-Helodata-Error-Code` distinguishes a few flavors.

## `upstream-timeout` — backend IP didn't respond

```
HTTP/1.1 522
X-Helodata-Error-Code: upstream-timeout
```

**Causes**

* The selected residential/mobile/ISP IP went offline between IP selection and connection
* Target site is slow and the request exceeded the gateway's upstream timeout
* ISP IP whose carrier dropped the connection

**Fix**

* Just retry. Pick a new exit:
  * Gateway products: drop the `session` segment to get a different IP
  * ISP: switch to another IP from your batch

If you see `upstream-timeout` on **more than 10%** of requests over 5+ minutes, that's not normal — it's worth opening a ticket with a few `X-Helodata-Request-Id`s.

## `insufficient-pool` — your filter is too narrow

```
HTTP/1.1 522
X-Helodata-Error-Code: insufficient-pool
X-Helodata-Insufficient-Pool: true
```

**Cause** — the combined geo + ASN + session filter matches fewer than 10 live IPs.

**Fix** — broaden the filter, in this order:

1. Drop `city`
2. Drop `state`
3. Drop `asn`
4. Switch country

If you genuinely need that narrow filter, contact support — we can sometimes pre-stage a larger pool for paid plans.

## Target's TLS handshake timed out

```
HTTP/1.1 502
X-Helodata-Error-Code: upstream-tls-error
```

(Strictly 502, but related.)

**Cause** — TLS to the target site failed mid-handshake. Likely target-side: misconfigured cert, OCSP timeout, slow server.

**Fix** — see [SSL / TLS errors](/troubleshooting/ssl-errors).

## What to do programmatically

```python
RETRYABLE = {"upstream-timeout"}

def on_522(resp, attempt):
    code = resp.headers.get("x-helodata-error-code")
    if code == "insufficient-pool":
        # Broaden filter — don't blindly retry
        raise ConfigError("Loosen geo/ASN filter")
    if code in RETRYABLE and attempt < 5:
        time.sleep(2 ** attempt + random.random())
        return True
    return False
```

## What `522` is **not**

* It's not a target `522`. Cloudflare also returns 522 — that's the target site telling the proxy *its* origin timed out. Distinguish by the presence of `X-Helodata-Error-Code`:
  * **Has `X-Helodata-Error-Code`** → helodata gateway error
  * **No such header, but has `Server: cloudflare` and a cf-ray** → target's Cloudflare timing out its own backend; retry won't help much

## Still seeing it

Email <support@helodata.com> with at least one `X-Helodata-Request-Id`. We log the upstream IP, the target, and the timeout breakdown, and can tell you whether the issue is on our side or the target's.


# SSL / TLS errors

helodata is a **forwarding** proxy — for HTTPS targets, the gateway tunnels a TCP connection via `CONNECT`, and your client negotiates TLS end-to-end with the target. So SSL errors are almost always one of:

1. Your client's CA bundle is stale
2. The target site has a broken TLS config
3. You're trying to intercept TLS yourself with another MITM tool in the chain

## Symptom: `SSL: CERTIFICATE_VERIFY_FAILED` (Python)

```
requests.exceptions.SSLError: HTTPSConnectionPool(host='example.com', port=443):
  Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(...)))
```

**Diagnose**

```bash
# Bypass helodata to isolate the target
openssl s_client -connect example.com:443 -servername example.com < /dev/null

# Then with helodata
curl -v --proxy http://USER:PASS@gate.helodata.io:7777 https://example.com
```

If both fail with the same cert error, **the target itself is misconfigured**.

**Fix options**

* **Update certifi** (Python): `pip install -U certifi`
* **Update CA bundle** (system): `apt update && apt install ca-certificates -y && update-ca-certificates`
* **Disable verification** as last resort — only on a test target:

  ```python
  requests.get(url, verify=False)
  ```

## Symptom: gateway returns `502` + `upstream-tls-error`

```
HTTP/1.1 502
X-Helodata-Error-Code: upstream-tls-error
```

The target's TLS handshake failed against the helodata exit IP. Most often the target:

* Has an expired certificate
* Doesn't include the intermediate certificate in its chain
* Requires a specific TLS version or cipher your exit IP's TLS stack negotiates differently from your local one

The exit IP doesn't actually matter for TLS terms — TLS is end-to-end. The gateway just observed that the handshake didn't complete in the configured time and reported it back.

**Fix**

* Retry with a fresh exit IP (rotate the session)
* If consistent across IPs, the target is broken — confirm with `openssl s_client`

## Symptom: HTTPS works at home, fails via proxy

Most common cause: **DNS resolution**. The local resolver returned an IP for a CDN closest to your home, but the proxy's resolution returned a different (sometimes misconfigured) edge.

**Fix**

* Switch to SOCKS5 with `socks5h://` so the proxy resolves DNS uniformly across your fleet.
* For very few targets, hard-code a known-good DNS over HTTPS (DoH) — but this defeats geo-targeting.

## Symptom: header `X-Helodata-Bypass-TLS-Validation: skipped`

helodata never modifies upstream TLS. If you see hints of certificate mangling in your client's logs, look for **another** MITM proxy in the chain — your corporate firewall, a debug tool like Charles or mitmproxy you forgot to disable, or a VPN client that re-signs certs.

## Self-signed targets

helodata cannot help — it doesn't re-issue certs. Your client must trust the self-signed root explicitly (`requests.get(..., verify="my-ca.pem")`).

## Quick checklist

* [ ] Did this work directly (no proxy) just now?
* [ ] Is `certifi` (Python) or your OS CA bundle up to date?
* [ ] Does `openssl s_client` succeed?
* [ ] Is there another MITM proxy (Charles, mitmproxy, corp firewall) in the chain?
* [ ] Does retrying with a different exit IP help?

If you've answered yes to "still failing" after all of these, open a ticket with `X-Helodata-Request-Id` and the `openssl s_client` output.


# CAPTCHAs

CAPTCHA pages mean the target site detected something off about your request — not necessarily a "bot," just that something doesn't add up. helodata supplies the IP; the rest is your client's job.

## Common detection signals (in order of importance)

1. **IP reputation** — has this IP been hammering the target recently?
2. **Header consistency** — UA, Accept-Language, Sec-CH-UA-\* must agree with each other and with the IP's country
3. **TLS fingerprint** — JA3 / JA4 hash matching a known bot library
4. **Behavior** — request pattern, timing, mouse / scroll on JS pages

## Quick wins

In rough order of effort vs payoff:

### 1. Use a session, not rotation, for multi-step flows

Bot detection counts how many requests came from each IP. A sticky session looks like one user across the flow; pure rotation looks like a million one-page users — that's a classic bot signal.

```
helo_s1a2b3c4d5e-type-res-region-us-session-job1-sesstime-30
```

### 2. Align headers with the IP geo

If your exit IP is in Germany, your headers should look German:

```
Accept-Language: de-DE,de;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (... a modern browser UA from a major OS ...)
```

Mismatched `Accept-Language` (e.g. `zh-CN` from a US IP) is a strong signal.

### 3. Send a complete browser header set

A bare `requests.get()` sends fewer headers than any real browser. Pad it:

```python
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "DNT": "1",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
}
```

### 4. Use a JS engine for JS-heavy sites

Targets that ship behavioral fingerprinting (`navigator.webdriver`, mouse movements, canvas) need a real browser — use [Playwright](/integrations/scraping-tools/playwright) or [Puppeteer](/integrations/scraping-tools/puppeteer) with stealth plugins.

### 5. Switch to mobile

If the target trusts mobile devices more (a common pattern), switch from `type-res` to `type-mob`. Pair with a carrier ASN:

```
helo_s1a2b3c4d5e-type-mob-region-us-asn-6167-session-x-sesstime-60
```

## What about CAPTCHA solvers?

helodata doesn't solve CAPTCHAs. Common patterns to combine:

* **2Captcha / Anti-Captcha / CapSolver** — pay-per-solve services that return the token
* **`puppeteer-extra-plugin-recaptcha`** — Puppeteer plugin that wires the token in
* **Browser-fingerprint-aware antidetect browsers** — [AdsPower](/integrations/antidetect-browsers/adspower), [Multilogin](/integrations/antidetect-browsers/multilogin) — these handle the fingerprint side, leaving you to fetch the token

For Cloudflare's invisible challenges (`1020`, JS challenge), a real Chromium with stealth almost always beats a CAPTCHA solver.

## When the target *just* hates the IP

If you've matched headers, fingerprint, and behavior and still see a CAPTCHA — that exit IP is burnt for this target. **Rotate**:

* Gateway: drop the session, get a fresh IP
* ISP: switch to another IP from your batch; consider requesting a replacement if it persists across multiple IPs from the same batch ([replace IP](/products/overview-2/ip-management#replace-a-dead-ip))

## Coming soon

helodata's [Web Unblocker](/products/overview-4) handles all of the above transparently — JS rendering, header rotation, CAPTCHA, retries. Join the waitlist if this is the pattern you keep solving.


# IP location mismatch

You asked for `region-de`, the gateway gave you a German IP, but the target says "we don't serve your country." Three things can cause this — work through them in order.

## 1. Geo database lag

Target sites use third-party geo databases (MaxMind, IPinfo, internal). These update on different schedules from helodata's. An IP that we know is residential in Frankfurt today might still be listed as "datacenter in Amsterdam" by an old MaxMind copy the target queries.

**Diagnose** — confirm the IP we routed you to:

```
X-Helodata-Exit-IP: 198.51.100.42
X-Helodata-Exit-Geo: de/he/frankfurt
```

Then look it up in the target's likely geo source:

* MaxMind demo: <https://www.maxmind.com/en/geoip2-precision-demo>
* IPinfo: <https://ipinfo.io/{ip}>
* Whoer: <https://whoer.net/>

If those agree with `X-Helodata-Exit-Geo`, the target has older data — wait or try a new IP. If they **disagree** with us, that IP's geo recently changed and our database isn't refreshed yet.

## 2. Behavioral geo (not IP geo)

Some targets check more than IP. Signals that override IP:

* `Accept-Language` header
* `Time-Zone` from JavaScript (`Intl.DateTimeFormat().resolvedOptions().timeZone`)
* Currency or locale cookies from a previous visit
* Login state if the user has an account region

**Fix** — clear cookies, set headers to match the IP country, use an antidetect browser that aligns timezone with proxy geo (most do — see [Multilogin](/integrations/antidetect-browsers/multilogin), [AdsPower](/integrations/antidetect-browsers/adspower)).

## 3. Genuine routing error (rare)

The gateway picks an IP whose **live** geo doesn't match the requested filter. helodata's geo database is refreshed daily, but device-level mobility (a residential subscriber traveled internationally, a mobile SIM crossed a border) can produce a mismatch in the gap.

When that happens we tag the response:

```
X-Helodata-Exit-Geo-Confidence: low
```

**Fix** — request a new IP (drop session) and we'll route past the questionable one. If you see `Exit-Geo-Confidence: low` on > 5% of your requests, raise a ticket.

## Verifying programmatically

```python
import requests

USER = "helo_s1a2b3c4d5e-type-res-region-de"
proxy = f"http://{USER}:PASSWORD@gate.helodata.io:7777"

r = requests.get("https://ipinfo.io/json",
                 proxies={"http": proxy, "https": proxy}, timeout=30)
exit_info = r.json()
exit_geo = r.headers.get("x-helodata-exit-geo")

# Compare what we say vs what a third-party geo provider says
print("helodata:", exit_geo)              # e.g. de/he/frankfurt
print("ipinfo:",   exit_info["country"])  # e.g. DE
```

If those disagree, that's the mismatch — and the target was probably using something like the second one.

## What about VPN / hosting flags?

Some targets reject IPs flagged as "VPN" or "hosting" regardless of country. helodata residential IPs aren't flagged as hosting (they're real consumer ISPs). If a target's blocking based on this flag:

* Try ISP — these are flagged as residential by major databases
* Try mobile — usually flagged "mobile carrier" which most targets allow
* If you must use residential and the target keeps flagging, the IP was likely re-classified after we issued it; rotate

## Still mismatching

Email <support@helodata.com> with one `X-Helodata-Request-Id`, the target URL, and what geo it reported back. We'll trace whether the issue is in our pool, in the third-party geo database, or on the target.


# Slow speeds

"Slow" is rarely the gateway itself — it's almost always one of: the residential / mobile network, your client config, or the target site. Walk through the diagnosis below.

## Baseline expectations

| Product     | First byte | Throughput per connection |
| ----------- | ---------- | ------------------------- |
| Residential | 300–800 ms | 10–50 Mbps                |
| Mobile      | 500 ms–3 s | 5–20 Mbps                 |
| ISP         | 50–150 ms  | 100 Mbps – 1 Gbps         |

If you're seeing this and assuming it should be faster — re-set expectations. Residential is *real* residential bandwidth.

## Step 1 — measure where time goes

```bash
curl -w "\nDNS:%{time_namelookup}s Connect:%{time_connect}s TLS:%{time_appconnect}s TTFB:%{time_starttransfer}s Total:%{time_total}s\n" \
     -o /dev/null -s \
     --proxy http://USER:PASS@gate.helodata.io:7777 \
     https://example.com
```

Output decoded:

| Field                | Meaning                                      |
| -------------------- | -------------------------------------------- |
| `time_connect`       | TCP to **gateway**                           |
| `time_appconnect`    | TLS handshake to **target** (through tunnel) |
| `time_starttransfer` | First byte from target                       |
| `time_total`         | End to end                                   |

Where is the time going?

* High `time_connect` (>500 ms) → network from you to `gate.helodata.io`. Switch to a regional gateway (`na.gate.helodata.io`, `eu.…`, `ap.…`).
* High `time_appconnect` → target is slow or far from the exit IP.
* High `time_starttransfer` after handshake → target server processing, not network.

## Step 2 — eliminate connection re-establishment

Re-using TCP / TLS connections (HTTP keep-alive, HTTP/2) saves the handshake cost. In `requests`:

```python
s = requests.Session()                   # persistent connection
```

In Node with undici, the default dispatcher pools connections automatically — just don't construct a new `ProxyAgent` per request.

## Step 3 — check your concurrency

You might have hit a per-sub-user concurrent limit:

```
HTTP/1.1 429
X-Helodata-Error-Code: concurrent-limit
```

See [Rate limits](/api-reference/rate-limits). Either lower your concurrency or upgrade your plan.

## Step 4 — try a different exit IP

The selected residential IP might just be slow today (it's someone's home internet). Drop your `session` to get a different IP.

## Step 5 — try a different product

If residential is too slow for your throughput needs, ISP gives you 10–50× the per-IP throughput at a different cost model.

## When the *gateway* is slow

Rare but real. Check the status page:

* <https://status.helodata.com>

If status is green but you're seeing high `time_connect` consistently, open a ticket with `traceroute gate.helodata.io` output and `X-Helodata-Request-Id` from a slow request.

## Common pitfalls

* **SOCKS5 with local DNS** — local resolver adds latency. Use `socks5h://`.
* **TLS-wrapped connection to gateway** — adds an extra handshake on top of the target's own TLS; only use when your network demands TLS-only egress.
* **DNS cache cold** — `time_namelookup` >100 ms means your DNS resolver isn't caching. Configure a local resolver.
* **Logging slows your client** — verbose logs can dominate the wall-clock time on small requests.

## Benchmarking properly

To compare proxy speed, measure many requests, not one:

```bash
for i in $(seq 100); do
  curl -w "%{time_total}\n" -o /dev/null -s \
       --proxy http://USER:PASS@gate.helodata.io:7777 \
       https://example.com/sitemap.xml
done | sort -n | awk 'NR==50{print "median:",$1} END{print "max:",$1}'
```

Median > 95th percentile — the latter is where bot detection lives.


# Website won't load

The page is blank, half-loaded, or just spinning. Walk through this from top to bottom.

## 1. Is helodata returning anything at all?

Run a known-good request through the same credentials:

```bash
curl -v --proxy http://USER:PASS@gate.helodata.io:7777 https://ipv4.icanhazip.com
```

If that returns an IP, the gateway path is fine — move on. If not, see [407](/troubleshooting/407) or [522](/troubleshooting/522).

## 2. Is the target reachable from this exit IP?

Use the request\_id from the failed request, or test directly:

```bash
curl -v --proxy http://USER:PASS@gate.helodata.io:7777 https://example.com
```

Look for the response status and headers. Possibilities:

* **`200` but empty body** — site requires JS rendering, see step 4
* **`403` from helodata** (`X-Helodata-Error-Code: restricted-category-*`) — target is on the [restricted list](/products/overview/restricted-targets)
* **`403` from target** (no `X-Helodata-Error-Code`, `Server: cloudflare`) — IP reputation, see [CAPTCHAs](/troubleshooting/captchas)
* **`5xx`** — see [522](/troubleshooting/522) or [SSL errors](/troubleshooting/ssl-errors)

## 3. Is the target a JS app (SPA)?

Open the target in a non-proxied browser. If it loads via DevTools showing a flurry of XHR / fetch calls after the initial HTML, it's a SPA. Plain `requests` won't render the page — it only gets the empty shell.

**Fix** — switch to a real browser:

* [Playwright](/integrations/scraping-tools/playwright) or [Puppeteer](/integrations/scraping-tools/puppeteer)
* An antidetect browser ([AdsPower](/integrations/antidetect-browsers/adspower), [Multilogin](/integrations/antidetect-browsers/multilogin))

When [Web Unblocker](/products/overview-4) ships, it does this transparently.

## 4. Does the site return different markup to your IP's country?

Compare what you get through helodata vs what you see in a regular browser:

```bash
curl --proxy http://USER:PASS@gate.helodata.io:7777 https://example.com -o through-proxy.html
curl https://example.com -o direct.html
diff <(head -200 through-proxy.html) <(head -200 direct.html)
```

Differences mean the site geo-personalizes content. That's expected for `region-de` requests — make sure that's what you wanted.

## 5. Is the page being served but resources are blocked?

Inspect with curl `--include` and look at `Content-Security-Policy`, `X-Frame-Options`, or redirects to a captcha challenge. Browsers will display a blank page if a script-src is blocked.

## 6. Are you hitting a Cloudflare challenge?

A response with HTML content like `cf_chl_opt` or a `cf-mitigated: challenge` header means Cloudflare is challenging you. Handling:

* Try sticky session + browser-aligned headers — see [CAPTCHAs](/troubleshooting/captchas)
* Use a real browser (Playwright/Puppeteer with stealth)
* Try a different exit IP — reputation may differ

## 7. Is there a redirect loop?

```bash
curl -L --max-redirs 10 --proxy http://USER:PASS@gate.helodata.io:7777 https://example.com
```

`-L` follows redirects. If `--max-redirs` triggers, the target is bouncing you between two URLs based on a cookie / header it expects.

**Fix** — capture and replay the cookie expected at each hop, or use a real browser.

## 8. Last resort — open a ticket

```
- Target URL:
- Sub-user:
- Two X-Helodata-Request-Ids from failing requests:
- What you tried already:
```

Email <support@helodata.com>. We can replay the request from our side and tell you whether the issue is on us, the target, or your client.




---

[Next Page](https://docs.helodata.com/llms-full.txt/1)

