Tutorial · 6 min read

Count your own requests: ProxyDesk’s activity log and its CSV export

ProxyDesk counts every connection it carries, on your machine. What the log and its CSV hold, what they leave out, and how to hold them against our meter.

We keep telling you not to trust a proxy meter, ours included, until you have counted the same traffic yourself. Counting it yourself usually means wrapping every request in your scraper with a byte counter. If your tools go through ProxyDesk, that work is already done: it counts every connection it carries, on your machine, and exports the lot as CSV.

This post covers what that count holds, what it leaves out on purpose, how to read the export with a short Python script, and how to hold it up against our meter without fooling yourself. One thing first, said plainly: ProxyDesk’s log is its own count. It is not your bill, and it never sees ours. That is exactly why it is worth having.

What ProxyDesk counts

Open the Activity page. The top-left panel is Your traffic, this session: connections, bytes up, bytes down and how many failed. Next to it, Health-check traffic counts the checks ProxyDesk runs on your proxies, kept apart so they never pad your figures. Below, Recent connections lists each connection your apps made, with its destination, the proxy that carried it, the result and the bytes. Pool events, the other tab, is the history of proxies being benched and coming back.

ProxyDesk activity page: connection and byte totals for the session, health-check traffic counted separately, recent connections with destination, proxy, result and bytes, and an Export CSV button

A row is written when a connection ends: bytes up is what your app sent through the tunnel, bytes down what came back. A connection ProxyDesk could not place is written too, with zero bytes and the reason.

What it leaves out, on purpose

  • Health checks. Counted in their own panel and never in the log or your session totals.
  • The proxy’s own handshake. The count starts once the tunnel to the target is open. The login ProxyDesk sends to the proxy, and the request that opens the tunnel, are not in it.
  • Old rows. The log keeps the latest 500 connections, in memory. Pressing Start begins a fresh log, so export before you start again.
  • Requests. A row is a connection, not a request. One connection kept alive by your HTTP client can carry many requests, so the row count will not match a per-request log. Compare totals, not rows.

Export it

Press Export CSV on the Activity page and pick a folder. The file name defaults to proxydesk-activity- plus the date and time. The columns come straight from ProxyDesk’s exporter:

Columns in ProxyDesk's activity CSV export
ColumnWhat it holds
timeWhen the connection ended or failed, in UTC, like 2026-11-06T09:14:03Z
destinationThe host and port your app asked for, like shop.example:443
upstreamThe proxy that carried it: its label, or its address when it has none. Empty when no proxy was found
resultok or failed
causeEmpty for ok. Otherwise one of login_refused, proxy_auth_required, unreachable, timeout, dns_failure, protocol, plan_expired, no_upstream or unknown
errorThe raw error text behind the cause
bytes_upBytes your app sent through the tunnel
bytes_downBytes that came back

There is no login column. One small courtesy: a cell that starts with =, +, - or @ gets an apostrophe in front, so a spreadsheet shows it as text instead of running it as a formula. A sample file looks like this:

time,destination,upstream,result,cause,error,bytes_up,bytes_down
2026-11-06T09:14:03Z,shop.example:443,Germany ISP,ok,,,1834,48211
2026-11-06T09:14:12Z,api.example:443,,failed,no_upstream,no healthy upstream available,0,0

Read it with a short Python script

Standard library only. It adds up successful connections per host, heaviest first, and counts failures by cause:

import csv
import sys
from collections import Counter, defaultdict

per_host = defaultdict(lambda: [0, 0, 0])
failures = Counter()

with open(sys.argv[1], newline="") as f:
    for row in csv.DictReader(f):
        if row["result"] != "ok":
            failures[row["cause"]] += 1
            continue
        host = row["destination"].rsplit(":", 1)[0]
        stats = per_host[host]
        stats[0] += 1
        stats[1] += int(row["bytes_up"])
        stats[2] += int(row["bytes_down"])

for host, (conns, up, down) in sorted(per_host.items(), key=lambda item: -sum(item[1][1:])):
    print(f"{host:30} {conns:5} conns  up {up:>12,}  down {down:>12,}")
for cause, count in failures.most_common():
    print(f"failed: {cause:20} {count}")

Save it as summarise.py and run python3 summarise.py proxydesk-activity-….csv. On a short sample file it prints:

shop.example                       2 conns  up        3,624  down       99,213
api.example                        1 conns  up          912  down        3,307
failed: no_upstream          1
failed: login_refused        1

A pile of login_refused means a password changed under you. plan_expired means a proxy’s plan has ended. no_upstream means every proxy was benched at that moment, which is ProxyDesk refusing to send your request out on your own IP, exactly as designed. The ProxyDesk quick start explains the benching.

What the log tells you besides the total

  • Lots of small connections to one host. Your client is opening a fresh connection per request, and every fresh HTTPS connection pays for a new TLS handshake, which is real bytes on any meter. A session that keeps connections alive cuts that. The catch: with ProxyDesk’s default of a new proxy for each connection, a kept-alive connection also stays on one proxy for longer. Rotating vs sticky helps you decide which you want.
  • Hosts you never meant to visit. Something else on the machine is pointed at ProxyDesk. The usual suspect is a shell where you exported ALL_PROXY and forgot: every tool started from it goes through your proxies, and through your balance.
  • Failures that all name one proxy. Its login changed or its plan ended. Pool events shows when ProxyDesk benched it.

Hold it up against our meter

  1. Note when you pressed Start and when you exported. That is your window.
  2. Export the same window from the usage log in your dashboard.
  3. Sum bytes per host on both sides. Add ProxyDesk’s health-check figure to its side first: those checks went through your proxies too, so our meter saw them even though your log does not list them.

Expect our number to come out a little higher than yours. We meter request and response bytes including headers and protocol overhead, and ProxyDesk’s count starts after the proxy handshake, so a small gap in our direction is normal. Rows with zero bytes should cost nothing, because connections that fail before moving data are not billed. Ours coming out lower than yours is unusual; check the window first.

Ours coming out a lot higher is what the whole meter promise is for. Bring both files to Discord. For the request-by-request version of this check, with a script on each side, read auditing your proxy bill; for the ways cheap providers pad a meter, and how to spot each one, catching usage inflation.

Try it while you read

Top-ups start at $5.

One shared datacenter IP for 30 days is $1.25. A single gigabyte of residential is $2.40. The balance never expires.

Published

Filed under

Found a mistake? Tell us in Discord and we will fix the post.

The community layer

Stuck halfway through?

Paste the error in Discord. Someone has hit it before and the answer is usually one message long.

Join the Discord

4,200+monkeys in the Discord

  • Help from humans

    Post your error, get an answer. Usually in minutes, usually from someone who has hit the same wall.

  • A status bot that tells on us

    Pool health, incidents and maintenance posted automatically. Including the bad days.

  • Deals and free traffic

    Bonus GB drops, early access to new pools, and the occasional giveaway for a good bug report.

Join the Discord4,200+ monkeys, free to lurk