Proxy pool#
A proxy pool is a store of proxies that 9Hits keeps for you at https://dash.9hits.com/pool. You import your proxy list once, then just give the pool key to Viewer: each session picks up a proxy from it by itself.
Create a pool and import proxies#
Choose a pool in the Select a pool box; the three buttons next to it are Create pool,
Rename pool and Delete pool. Each account can have up to 100 pools.
Click Import, choose a Default type for lines that do not state a type, add a Note for the
whole batch if you like (for example the provider's name), then paste the Proxy list. Put one proxy
per line in the order type, address:port, user, password, separated by ;, | or spaces.
Only the address is required:
When the import finishes, the dashboard reports the number of Added, Updated, Duplicated and
Error lines. Importing a proxy that is already there does not create a copy.
Reading the proxy table#
The Quality column holds three icons, Is proxy, Speed and Performance (hover to see the
text), and is filled in only after the proxy has been used. The Status column is Online,
Waiting, Offline or Error. The filter strip filters by Type, Is proxy, Speed,
Performance, Status (plus Duplicated and Unused) and Note. The Delete button offers
Delete selected, Delete offline and Delete matched filter.
There is no trash
Delete pool deletes the pool and every proxy in it. Delete matched filter with no filter set
also wipes the whole pool. Check the number of rows in the table before you click.
Pool key and URL#
Each pool has a 32-character hex key and a URL of the form http://proxy.9hits.com/pool/<key>,
shown in the Pool URL box with a Copy URL button. In Viewer, change the session's connection type
to ExProxy, then paste the key or the URL (see Assign a proxy to a session).
Sessions that share a pool split its proxies among themselves, one proxy per session, so you should run fewer sessions than there are proxies in the pool; otherwise many sessions will have to wait.
Keep your pool key private
Anyone with the key can get your proxies without signing in. If the key leaks, create a new pool, import the proxies again and delete the old pool.
Share a pool#
To share a pool, give the key to someone else to paste into their ExProxy session; no permissions
are needed. Proxies they are using show another user in the Used by column. You are still the one
paying, and the pool runs out faster.
Build your own proxy source#
ExProxy also accepts URLs outside 9Hits, usually an endpoint you write yourself. You do not get
the tracking columns and filters of this page, but Viewer tells your endpoint what each proxy is
doing, so you can keep your own table.
The two calls#
Getting a proxy. Viewer calls your URL with GET and the query parameters token (the access
key of that Viewer), user (the 9Hits username, once Viewer knows it), ver, platform and
rand. Reply with one proxy as JSON:
type is http, socks4, socks5 or ssh; user, password and id are optional, and id
comes back in the status reports below. Reply {"error": "No proxy available"} to make the session
wait 30 seconds and ask again.
Status reports. Viewer calls the same URL with the same parameters plus status when the
proxy's state changes, and repeats the current state about every 5 minutes while the session runs,
so a proxy with no report for 15 minutes can be treated as free. That is a few requests per session
per hour, plus one for each change. The reply is ignored, so answer with any 2xx. Each report
carries proxy (the server you returned), id, session (the Viewer session) and, once the
session is online, ip and country as 9Hits sees them.
status |
Extra parameters | When |
|---|---|---|
idle |
reason |
The session is online and waiting for a campaign, or got none |
working |
camp, duration (seconds) |
A campaign was taken; the 5-minute repeats carry the current one |
error |
error, camp while viewing |
Signing in or loading a page failed through this proxy |
released |
reason |
The session let the proxy go: stopped, repeated errors, no campaign, or 9Hits asked for a new one |
For example, a session that just took campaign 48213 for 60 seconds through 1.2.3.4:1080 sends:
GET /pool.php?token=...&user=alice&ver=6.0.2&platform=win-x64&rand=1760770000000
&status=working&proxy=1.2.3.4%3A1080&id=42&session=7f3a9c2e1b4d&ip=203.0.113.9&country=DE
&camp=48213&duration=60
Example: a pool in one PHP file#
pool.php hands out the proxy that has been free the longest and keeps the last report of each
proxy in pool.json. Set the session's ExProxy field to https://example.com/pool.php.
<?php
$proxies = [
['type' => 'http', 'server' => '1.2.3.4:8080', 'user' => 'u1', 'password' => 'p1'],
['type' => 'socks5', 'server' => '5.6.7.8:1080'],
];
$state = json_decode(@file_get_contents('pool.json'), true) ?: [];
if (isset($_GET['status'])) { // a status report: remember it, nothing to answer
$state[$_GET['proxy']] = [
'status' => $_GET['status'],
'session' => $_GET['session'] ?? '',
'camp' => $_GET['camp'] ?? '',
'detail' => $_GET['error'] ?? $_GET['reason'] ?? '',
'at' => time(),
];
file_put_contents('pool.json', json_encode($state, JSON_PRETTY_PRINT));
exit;
}
// a proxy request: free proxies first, then the one reported on least recently.
// A proxy is busy while its last report is not "released" and is under 15 minutes old,
// so a Viewer that died without releasing frees its proxy by itself.
$rank = function ($p) use ($state) {
$s = $state[$p['server']] ?? null;
$busy = $s && $s['status'] !== 'released' && $s['at'] > time() - 900;
return [$busy ? 1 : 0, $s['at'] ?? 0];
};
usort($proxies, function ($a, $b) use ($rank) { return $rank($a) <=> $rank($b); });
$proxy = $proxies[0];
$proxy['id'] = $proxy['server'];
$state[$proxy['server']] = ['status' => 'assigned', 'session' => '', 'camp' => '', 'detail' => '', 'at' => time()];
file_put_contents('pool.json', json_encode($state, JSON_PRETTY_PRINT));
header('Content-Type: application/json');
echo json_encode($proxy);
Encrypt the reply before sharing the URL#
A plain JSON reply shows your proxy credentials to anyone who opens the URL. If you give the URL
to other people (see Share a pool), encrypt the reply through 9Hits first: send the JSON
to http://proxy.9hits.com/we/encrypt-proxy and return its output instead. Viewer decrypts it; a
browser or curl only sees ciphertext. Replace the last two lines of the example with:
$enc = json_decode(file_get_contents('http://proxy.9hits.com/we/encrypt-proxy?input='
. urlencode(base64_encode(json_encode($proxy)))));
echo $enc->status === 'ok' ? $enc->output : json_encode(['error' => $enc->message]);
The reference sample is at https://github.com/9hitste/ExProxy-9Hits-Viewer/blob/master/exproxy.php. Whoever has the URL can still use your proxies, so treat it like a pool key.