Hypercall
A residential proxy network. You connect with a standard HTTP or SOCKS5 proxy client, and your traffic exits through a real residential IP in the location you choose.
Everything is controlled through the proxy username: geo targeting, sticky sessions, TTL, and speed are all option tokens appended to it, so any client that supports username/password proxy auth works with no SDK and no special integration.
curl -x 'http://USER-country-us:PASS@proxy.hypercall.dev:4444' https://api.ipify.org
How it works
- One endpoint.
proxy.hypercall.devautomatically routes you to the nearest entry point. You never pick a region; the exit location you want goes in the username. - Targeting is measured, not claimed. Exits are indexed by their real,
verified location and network, so
country,state,city, andasnreflect where the IP actually is. - Pay for what you transfer. Your balance is metered in bytes
(1 GB = 1,000,000,000 bytes). When it runs out, requests stop with
402.
Where to start
| Quickstart | Your first request, in one command |
| Connecting | Ports, protocols, proxy-string formats |
| Targeting options | The full username token grammar |
| Error codes | What each failure means and how to fix it |
| API reference | Manage accounts and sub-users over REST |
Quickstart
You need the username and password issued when your account was created.
1. Make a request
curl -x 'http://USER:PASS@proxy.hypercall.dev:4444' https://api.ipify.org
The response is the residential exit IP your request came from. Run it again and you will get a different one; by default, every request rotates to a fresh exit.
Quote the URL. Passwords can contain characters (
@ : / # %) that a shell or a URL parser will mangle, which shows up as a confusing407. Single-quote the whole-xvalue, or pass credentials with--proxy-userinstead:curl -x http://proxy.hypercall.dev:4444 --proxy-user 'USER:PASS' https://api.ipify.org
2. Pick a country
Append option tokens to the username, not the host:
curl -x 'http://USER-country-de:PASS@proxy.hypercall.dev:4444' https://api.ipify.org
Now the exit is in Germany. See Targeting options for state, city, ASN, and more.
3. Keep the same IP across requests
Add a session id (sid), which can be any string you choose. Requests with
the same sid reuse the same exit:
curl -x 'http://USER-country-us-sid-abc123:PASS@proxy.hypercall.dev:4444' https://api.ipify.org
curl -x 'http://USER-country-us-sid-abc123:PASS@proxy.hypercall.dev:4444' https://api.ipify.org
Both return the same IP. See Sticky sessions for how long that lasts.
4. Use it from code
Any HTTP client with proxy support works. Nothing Hypercall-specific:
import requests
proxy = "http://USER-country-us:PASS@proxy.hypercall.dev:4444"
r = requests.get("https://api.ipify.org", proxies={"http": proxy, "https": proxy})
print(r.text)
import { HttpsProxyAgent } from 'https-proxy-agent';
const agent = new HttpsProxyAgent('http://USER-country-us:PASS@proxy.hypercall.dev:4444');
const res = await fetch('https://api.ipify.org', { agent });
console.log(await res.text());
Next
- Connecting: SOCKS5, ports, and IP-based auth
- Targeting options: the full grammar
- Usage & limits: check your remaining balance
Connecting
Endpoint
| Host | proxy.hypercall.dev (routes to the nearest entry point) |
| HTTP | 4444, 8888 |
| SOCKS5 | 1080 |
Those are the conventional assignments, but every port accepts both
protocols; the protocol is detected per connection, so a SOCKS5 client pointed
at 4444 works too. 8888 exists because some clients default to it.
Regional hostnames (us.proxy.hypercall.dev, eu.proxy.hypercall.dev) let
you connect directly to a region you already know you want. This does not
change the exit location, only where you enter the network.
Protocols
HTTP: both plain forward proxying and CONNECT for HTTPS.
curl -x 'http://USER-country-us:PASS@proxy.hypercall.dev:4444' https://example.com
SOCKS5: CONNECT only. Hostnames are resolved at the exit
(socks5h-style), so DNS never leaks from your machine.
curl -x 'socks5h://USER-country-us:PASS@proxy.hypercall.dev:1080' https://example.com
SOCKS5 UDP ASSOCIATE is not supported. UDP requests are rejected cleanly with SOCKS reply
0x07.
Proxy-string formats
Most tools accept one of these. host:port:user:pass is a common format for
automation tools:
proxy.hypercall.dev:4444:USER-country-us:PASS
http://USER-country-us:PASS@proxy.hypercall.dev:4444
socks5h://USER-country-us:PASS@proxy.hypercall.dev:1080
You can generate ready-made strings, including sticky ids spread across sessions, from the API:
curl -H "Authorization: Bearer $HYPERCALL_KEY" \
'https://api.hypercall.dev/api/me/proxies?country=us&count=5&format=txt'
See the API reference for the parameters.
IP authentication
Instead of username/password, an account can authorise by source IP: you register your IPs, connect with no credentials, and the account is identified by where you’re connecting from. IP auth runs on a dedicated port that is enabled on request: contact support to have it set up for your account before relying on it.
Register the IPs with:
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X PUT \
-H 'Content-Type: application/json' \
-d '{"ips": ["203.0.113.7"]}' \
https://api.hypercall.dev/api/users/<id>/ip-auths
With IP auth you cannot pass option tokens (there’s no username to put them in), so requests use the account’s defaults.
Restricting where credentials can be used
Separately, you can restrict a username/password account to a set of source networks. Requests from anywhere else are refused even with valid credentials:
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X PUT \
-H 'Content-Type: application/json' \
-d '{"cidrs": ["203.0.113.0/24"]}' \
https://api.hypercall.dev/api/users/<id>/allowed-ips
An empty list means unrestricted. The check always uses the real connecting address, never a forwarded header.
Targeting options
Options are -key-value pairs appended to your username, freely combinable
and order-independent:
USER-country-us-state-texas-city-houston-sid-s42-ttl-30
Everything before the first known option key is your account username, so a username containing hyphens still works.
Geo
| Option | Values | Notes |
|---|---|---|
country | ISO-2 code, or any | country-us. any = no country constraint |
state / region | name or code | Aliases for the same thing: state-texas |
city | name | city-houston. Approximate; see the caveat below |
asn | number, AS prefix optional | asn-7922 and asn-AS7922 are equivalent |
zip | postal code | zip-77002 |
continent | europe, asia, africa, northamerica, southamerica, oceania, or the short codes eu, as, af, na, sa, oc | Expands to one country in that continent; an explicit country wins |
Multi-word values use no spaces: state-newyork, city-sanfrancisco.
City accuracy. Country, state, and ASN come from a verified measurement of each exit and are reliable. City is approximate: city-level location data is inherently less precise for residential connections. If exact city matters, verify the exit yourself and treat the label as a hint.
Session
| Option | Values | Default | Notes |
|---|---|---|---|
sid / session | any string, ≤60 chars | none | Same sid → same exit. See Sticky sessions |
ttl | minutes, 10-1440 | 10 min | How long a sid stays pinned. Clamped into range |
strict | false/0/off/no to disable | on | See below |
speed | 1-10 | none | Prefer faster exits within the matched set |
quality | low, medium (or med), high | none | Filters on measured network class; high restricts to residential-grade lines |
mobile | flag (no value) | off | Mobile-carrier exits only |
ttl is minutes in the username. ttl-30 is thirty minutes.
Strict mode
Strict is on by default: if nothing matches your exact target, you get
417 rather than a silently broader exit. This matters: a
“US, Texas, Comcast” request should never quietly return a random US IP.
Turn it off to allow progressive widening (asn → city → zip → state;
country and session are never dropped):
USER-country-us-state-texas-city-houston-strict-false
Only false, 0, off, and no disable it. Any other value leaves it on.
Examples
# Rotating US residential
USER-country-us
# A specific carrier
USER-country-us-asn-7922
# Mobile only, high quality
USER-country-gb-mobile-quality-high
# Sticky for 30 minutes
USER-country-de-sid-checkout-42-ttl-30
# City, but accept broader if unavailable
USER-country-us-city-houston-strict-false
# Fastest available exit in Japan
USER-country-jp-speed-10
Sticky sessions
By default every request rotates to a fresh exit. To hold one IP across
requests, pick any sid:
USER-country-us-sid-mysession-ttl-30
Every request carrying that sid routes to the same exit, for up to ttl
minutes. Different sid values are independent, so you can run many sticky
identities in parallel under one account.
# Two independent identities, each with its own stable IP
curl -x 'http://USER-country-us-sid-cart-a:PASS@proxy.hypercall.dev:4444' https://api.ipify.org
curl -x 'http://USER-country-us-sid-cart-b:PASS@proxy.hypercall.dev:4444' https://api.ipify.org
How long a session really lasts
Stickiness is best-effort, and this is the single most important thing to
design around. ttl is an upper bound you request, not a guarantee.
The exit is a real residential device, and it can go offline at any time. When
that happens, your sid moves to another exit that matches the same targeting
options, and the IP changes mid-session.
Practical expectations:
- Requesting a long
ttldoes not make a device stay online. - Holds beyond ~30 minutes are unreliable regardless of
ttl. - Short sessions (a checkout flow, a login sequence) are very reliable.
Write your client to tolerate a mid-session IP change. If your workflow breaks when the IP moves, re-verify the exit at the start of each critical step rather than assuming continuity:
def exit_ip(session_id):
p = f"http://USER-country-us-sid-{session_id}-ttl-30:PASS@proxy.hypercall.dev:4444"
return requests.get("https://api.ipify.org",
proxies={"http": p, "https": p}, timeout=30).text
ip = exit_ip("checkout-1")
# ... later, before a step that must run from the same IP:
if exit_ip("checkout-1") != ip:
restart_flow() # the device rotated; don't continue on a new identity
Choosing a sid
Any string up to 60 characters. Longer values are truncated. Use something meaningful to your workflow, such as an order id, a worker number, or a customer id, so concurrent jobs don’t collide on the same exit unintentionally.
Error codes
Errors come back as HTTP status codes on the proxy connection (or the equivalent SOCKS5 reply byte).
| Code | Meaning | What to do |
|---|---|---|
402 | Data limit reached. Your byte balance is exhausted | Top up. Check with /stats |
407 | Proxy authentication required. Bad or missing credentials | See below; this is the most commonly misdiagnosed one |
417 | No exit for that location. Nothing matched your target | Loosen the target, or add strict-false |
429 | Too many connections. Account thread cap reached | Lower concurrency, or ask for a higher cap |
502 | Upstream failed. A temporary failure on the exit connection | Retry; a new exit is chosen |
SOCKS5 equivalents: 0x02 when the data limit or connection cap is reached,
0x04 when no exit matches your target, 0x07 for an unsupported command
(e.g. UDP ASSOCIATE), 0x01 for a general or upstream failure.
Debugging 407
A 407 means the credentials we received were not valid. The most common
cause is not a wrong password: it is the password being mangled before it
ever leaves your machine.
Passwords can contain @, :, /, #, %. Inside a proxy URL those
are structural characters, so a parser splits the string in the wrong place
and sends the wrong credentials:
# WRONG: the password is parsed as part of the URL
curl -x http://USER:pa@ss/word@proxy.hypercall.dev:4444 https://api.ipify.org
# RIGHT: credentials passed separately, never parsed as a URL
curl -x http://proxy.hypercall.dev:4444 --proxy-user 'USER:pa@ss/word' https://api.ipify.org
# ALSO FINE: quoted, with the password percent-encoded
curl -x 'http://USER:pa%40ss%2Fword@proxy.hypercall.dev:4444' https://api.ipify.org
Work through this order:
- Re-test with
--proxy-user(or your client’s separate credential fields). If it works, it was a URL-parsing problem, not the password. - Check the account is active. A suspended account returns
407.GET /api/meshowsstatus. - Check source-IP restrictions. If the account has an
allowed-ipslist, requests from other networks are refused. - Rotate the password if you believe it leaked; see Credentials.
Debugging 417
417 means strict mode found no exit matching your exact target. Either the
combination is too narrow (a specific ASN in a specific small city), or
coverage for it is momentarily thin.
# Narrow: may return 417
USER-country-us-state-wyoming-city-cody-asn-7922
# Same intent, tolerant of thinner coverage
USER-country-us-state-wyoming-city-cody-asn-7922-strict-false
With strict-false the target widens step by step (asn → city → zip →
state) until something matches. Country and session are never dropped.
Usage & limits
Byte convention
Everything is metered in bytes, where 1 GB = 1,000,000,000 bytes (decimal,
not 2³⁰). A “10 GB” balance is exactly 10000000000 bytes.
Usage counts both directions: bytes sent upstream plus bytes received.
Checking your balance
With your API key:
curl -H "Authorization: Bearer $HYPERCALL_KEY" https://api.hypercall.dev/api/me
{
"id": "6f1c1f8e-6f0a-4b8f-9a5e-2f9d1c3b7a10",
"parent_id": null,
"can_resell": true,
"name": "acme",
"status": "active",
"proxy_username": "acme",
"proxy_password": "<your proxy password>",
"stats_token": "<opaque token>",
"max_bytes": 500000000000,
"allocated_bytes": 0,
"bytes_used": 2885007,
"remaining_bytes": 499997114993,
"thread_cap": null,
"speed_limit_mbps": null,
"expires_at": null,
"ip_auths": [],
"ip_restrictions": [],
"created_at": "2026-01-12T09:30:00Z"
}
| Field | Meaning |
|---|---|
max_bytes | Your total budget. null = unlimited |
bytes_used | Consumed by your own proxy traffic |
allocated_bytes | Handed down to sub-users (see below) |
remaining_bytes | max_bytes − allocated_bytes − bytes_used |
status | active, suspended, depleted, or expired |
The public stats endpoint
Each account has an opaque stats_token for unauthenticated usage checks.
This is useful for dashboards, or for handing a customer a read-only link
without giving them an API key.
curl https://api.hypercall.dev/stats/<stats_token>
curl 'https://api.hypercall.dev/stats/<stats_token>?format=txt'
used=2885007 total=500000000000 remaining=499997114993
On unlimited accounts, total and remaining read unlimited.
The token exposes usage only: no credentials, no ability to change anything.
Limits
| Limit | Effect when hit |
|---|---|
| Byte budget | Requests stop with 402 |
thread_cap | Concurrent connections over the cap get 429 |
speed_limit_mbps | Throughput shaped to the cap; requests still succeed |
expires_at | After this time the account stops serving |
Limits apply the moment they’re hit. In-flight connections are cut when a budget runs out, not just new ones.
Sub-users
If your account can resell, you can create sub-users and carve your budget between them:
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X POST \
-H 'Content-Type: application/json' \
-d '{"name": "customer-a", "gb": 50}' \
https://api.hypercall.dev/api/users
That moves 50 GB out of your balance into theirs: your allocated_bytes
rises, your remaining_bytes falls. Sub-users get their own credentials,
their own stats token, and their own limits.
Allocated data belongs to the sub-user: their consumption does not draw on
your bytes_used again, and you can only reclaim what they haven’t spent:
# Take back everything unused
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X POST \
https://api.hypercall.dev/api/users/<id>/refund
If you allocate your entire budget out, you stop serving (your remaining is 0) while your sub-users keep running, because they already own those bytes.
Credentials
Each account has two independent secrets:
| Used for | Looks like | |
|---|---|---|
| Proxy password | Authenticating proxy connections | short random string |
| API key | Authenticating REST calls | uk_ + hex |
Rotating one does not affect the other.
Rotating the proxy password
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X POST \
https://api.hypercall.dev/api/me/rotate-password
{ "proxy_password": "<new password>" }
The new password is shown exactly once. It cannot be retrieved later, so store it before you close the terminal.
Rotation takes effect across the network within seconds. Connections using the old password stop working, so roll it when you can update your clients.
Rotating the API key
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X POST \
https://api.hypercall.dev/api/me/rotate-key
{ "api_key": "uk_..." }
This mints a fresh key and revokes every other key on the account, including the one you just authenticated with. This is deliberate: it is designed for the case where a key may have leaked, and it guarantees no forgotten key survives. Switch to the returned key immediately.
Rotating a sub-user’s password
Parents can roll a sub-user’s proxy password without knowing the old one:
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X POST \
https://api.hypercall.dev/api/users/<id>/rotate-password
If something leaks
-
Rotate immediately: password, key, or both.
-
Check usage on
/api/me; unexpectedbytes_usedis the clearest sign a credential was used by someone else. -
Restrict by source IP so credentials alone aren’t enough:
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X PUT \ -H 'Content-Type: application/json' \ -d '{"cidrs": ["203.0.113.0/24"]}' \ https://api.hypercall.dev/api/users/<id>/allowed-ips
Handling passwords safely
Proxy passwords can contain @ : / # %. Never paste one raw into a proxy URL:
it gets misparsed, and you’ll chase a 407 that has nothing to
do with the password being wrong. Use your client’s separate credential
fields, or percent-encode.
API overview
Base URL: https://api.hypercall.dev
| Surface | Auth | What |
|---|---|---|
/api/* | Authorization: Bearer uk_… | Your account and your sub-users |
/stats/{token} | none | Usage by opaque token |
/locations/* | none | Available countries, regions, cities, ASNs |
The full machine-readable spec is openapi.yaml. Import it
into Postman or Insomnia, or generate a client from it.
Authentication
curl -H "Authorization: Bearer $HYPERCALL_KEY" https://api.hypercall.dev/api/me
A missing or invalid key returns:
{ "message": "Invalid API key." }
Errors
| Status | Body | Meaning |
|---|---|---|
401 | {"message": "Invalid API key."} | Bad or missing credentials |
403 | {"message": "..."} | Account inactive, or lacks the capability |
404 | {"message": "Not found."} | Not found, or not yours (see below) |
422 | {"message": "..."} | Validation error |
422 | {"error": "..."} | Billing error, e.g. insufficient balance |
Note the two distinct 422 shapes: message means the request was malformed;
error means the request was valid but there is a billing problem, such as
insufficient balance.
404 is deliberately ambiguous. Asking about a sub-user that isn’t yours
returns 404, not 403; the API never confirms that an account you don’t own
exists.
Idempotency
Creating a sub-user (POST /api/users) and allocating data
(POST /api/users/{id}/allocate) accept an Idempotency-Key header. Retrying
with the same key replays the original response instead of doing the work
twice:
curl -H "Authorization: Bearer $HYPERCALL_KEY" \
-H 'Idempotency-Key: order-4417' \
-X POST -H 'Content-Type: application/json' \
-d '{"name": "customer-a", "gb": 50}' \
https://api.hypercall.dev/api/users
Use it for anything triggered by a queue, a webhook, or a user clicking twice.
The account model
Every account is the same kind of object. parent_id describes the whole
hierarchy:
parent_id: null: a top-level account.parent_idset: a sub-user, created by (and drawing budget from) its parent.
can_resell gates whether an account may create sub-users. Nesting can go as
deep as you need: a sub-user with can_resell can carve its own budget
further down, using the identical endpoints.
Bytes follow one rule everywhere:
remaining = max_bytes − allocated_bytes − bytes_used
allocated_bytes is what you’ve handed to sub-users; bytes_used is your own
proxy traffic. A parent pays once, at allocation. A sub-user’s consumption
never draws on the parent again.
Reference
Base URL: https://api.hypercall.dev
Authenticate /api/* calls with your API key:
curl -H "Authorization: Bearer $HYPERCALL_KEY" https://api.hypercall.dev/api/me
/stats/{token} and /locations/* need no authentication.
Prefer a machine-readable spec? Download openapi.yaml and
import it into Postman, Insomnia, or your client generator.
Your account
GET /api/me
Your account summary. Returns every account field:
{
"id": "6f1c1f8e-6f0a-4b8f-9a5e-2f9d1c3b7a10",
"parent_id": null,
"can_resell": true,
"name": "acme",
"status": "active",
"proxy_username": "acme",
"proxy_password": "<your proxy password>",
"stats_token": "<opaque token>",
"max_bytes": 500000000000,
"allocated_bytes": 0,
"bytes_used": 2885007,
"remaining_bytes": 499997114993,
"thread_cap": null,
"speed_limit_mbps": null,
"expires_at": null,
"ip_auths": [],
"ip_restrictions": [],
"created_at": "2026-01-12T09:30:00Z"
}
status is one of active, suspended, depleted, expired.
max_bytes: null means unlimited. See Usage & limits for what
the byte fields mean.
GET /api/me/proxies
Generate ready-to-use proxy strings.
| Query param | Meaning |
|---|---|
country, state, city, asn, zip, continent | Targeting, same values as the username options |
sid | Explicit sticky session id, up to 60 chars. Not combinable with count > 1 |
count | 1 to 100, default 1. Each line gets its own random session id |
ttl | Sticky TTL in minutes, 10 to 1440 |
strict | false/0/off/no to allow widening |
format | json (default) or txt for host:port:user:pass lines |
curl -H "Authorization: Bearer $HYPERCALL_KEY" \
'https://api.hypercall.dev/api/me/proxies?country=us&count=5&format=txt'
JSON responses are an array of:
{
"server": "us-east",
"host": "us.proxy.hypercall.dev",
"username": "acme-country-us-sid-48291057",
"password": "<proxy password>",
"http_port": 4444,
"socks5_port": 1080,
"http_url": "http://acme-country-us-sid-48291057:<password>@us.proxy.hypercall.dev:4444",
"socks5_url": "socks5://acme-country-us-sid-48291057:<password>@us.proxy.hypercall.dev:1080"
}
POST /api/me/rotate-password
Returns {"proxy_password": "..."} exactly once. See
Credentials.
POST /api/me/rotate-key
Returns {"api_key": "uk_..."} and revokes every other key on the account,
including the one used for this request.
GET /api/nodes
Entry nodes you can connect to:
[{ "id": "...", "name": "us-east", "host": "us.proxy.hypercall.dev", "region": "us", "http_port": 4444, "socks5_port": 1080 }]
Sub-users
All sub-user routes require can_resell on your account and only ever see
your own sub-users; anyone else’s return 404.
POST /api/users
Create a sub-user and fund it from your balance. Accepts an
Idempotency-Key header.
| Body field | Meaning |
|---|---|
name | Required |
gb | Budget in GB. Omit or null for unlimited (only from an unlimited parent) |
can_resell | Default false |
proxy_username / proxy_password | Generated when omitted |
thread_cap, speed_limit_mbps | Optional limits |
expires_days | Days until the account stops serving |
curl -H "Authorization: Bearer $HYPERCALL_KEY" -X POST \
-H 'Content-Type: application/json' \
-d '{"name": "customer-a", "gb": 50}' \
https://api.hypercall.dev/api/users
Returns {"user": {...}, "api_key": "uk_..."}. The key is shown once.
GET /api/users and GET /api/users/{id}
List your sub-users, or fetch one. Same shape as GET /api/me.
PATCH /api/users/{id}
Update name, status (active or suspended only), can_resell,
thread_cap, or speed_limit_mbps. depleted and expired are computed
states and cannot be set.
DELETE /api/users/{id}
Returns {"deleted": true}. The sub-user’s unused balance returns to you.
POST /api/users/{id}/allocate
Body {"gb": 25}. Moves GB from your balance into theirs. Accepts an
Idempotency-Key header. Insufficient balance returns a billing 422.
POST /api/users/{id}/refund
Body {"gb": 10}, or an empty body to reclaim everything they haven’t spent.
POST /api/users/{id}/rotate-password
Returns {"proxy_password": "..."} once. You never need the old one.
PUT /api/users/{id}/allowed-ips
Body {"cidrs": ["203.0.113.0/24"]}. Restricts where the sub-user’s
credentials work. Empty list = unrestricted. Returns {"allowed_ips": [...]}.
PUT /api/users/{id}/ip-auths
Body {"ips": ["203.0.113.7"]}. Source IPs that authenticate as this account
with no password (see IP authentication). Returns
{"ip_auths": [...]}.
GET /api/users/{id}/proxies
Same parameters and response as /api/me/proxies, for a sub-user.
Public (no auth)
GET /stats/{token}
Usage by the account’s opaque stats_token. Add ?format=txt for
used=... total=... remaining=... (with unlimited on unlimited accounts).
See Usage & limits.
GET /locations/...
What the network can currently serve, as flat JSON arrays of strings:
| Endpoint | Filters |
|---|---|
/locations/countries | none |
/locations/regions | ?country=us |
/locations/cities | ?country=us®ion=texas |
/locations/asns | ?country=us |
curl 'https://api.hypercall.dev/locations/cities?country=us®ion=texas'
Errors
See API overview for status codes and the two 422 shapes.