If proxies are used in production — for scraping, automating SMM accounts, or large-scale multi-accounting — sooner or later, the moment comes when "it seems to work" is no longer a sufficient answer. You need numbers: how many requests are failing with errors, how much has latency increased after changing the IP pool, whether the traffic limit from the provider has been exhausted. We will analyze 7 metrics that should be collected in Prometheus and visualized in Grafana to see problems before users or blocked accounts do.
Why monitor proxies in production
Proxies are not a static infrastructure like a server with a fixed IP. They are a constantly changing pool of addresses, the behavior of which depends on the provider, load, region, and even time of day. Without monitoring, you will only learn about a problem when the Wildberries or Ozon parser stops returning data, the Facebook Ads account gets blocked, or the Instagram automation script starts throwing CAPTCHAs.
The goal of monitoring is to turn the subjective "something is wrong" into concrete numbers: the error rate has increased from 2% to 15% in the last hour, the average latency has tripled, traffic through one proxy has exceeded the limit. This allows you to respond before the problem becomes critical — automatically switch pools, change providers, or reduce request frequency.
To collect metrics, we use the combination of Prometheus (time series storage) + Grafana (visualization and alerts). This is a standard stack in DevOps that easily integrates with any language — Python, Node.js, Go. Below are examples in Python using the prometheus_client library, but the principle is universal.
Metric 1: Success Rate — share of successful requests
This is a basic metric that signals problems with the proxy pool first. It is calculated as the ratio of requests with response codes 200-299 to the total number of requests over a period of time. A drop in the success rate below 90% usually indicates that some IPs in the pool have been banned by the target site or the provider is delivering unstable addresses.
Example instrumentation in Python:
from prometheus_client import Counter, start_http_server
import requests
REQUESTS_TOTAL = Counter(
'proxy_requests_total', 'Total requests through the proxy',
['proxy_pool', 'status']
)
def fetch_with_proxy(url, proxy, pool_name="residential"):
try:
resp = requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=10)
status = "success" if resp.status_code < 300 else "failure"
except requests.exceptions.RequestException:
status = "failure"
REQUESTS_TOTAL.labels(proxy_pool=pool_name, status=status).inc()
start_http_server(9100) # endpoint for Prometheus scrape
PromQL query to calculate the success rate over the last 5 minutes:
sum(rate(proxy_requests_total{status="success"}[5m]))
/
sum(rate(proxy_requests_total[5m])) * 100
If you are working with multiple types of proxies simultaneously — for example, residential proxies for social media and datacenter proxies for scraping — it makes sense to break down the success rate by the proxy_pool label to see which specific pool is degrading.
Metric 2: Latency — response delay
Latency shows how quickly the proxy returns a response. A sharp increase in latency often precedes a drop in the success rate — the provider is overloaded, or the IP is already close to being blocked, and the target server begins to intentionally slow down responses. For latency, it is important to collect not only the average value but also percentiles — p50, p95, p99, as the average hides tail latencies that actually hinder performance.
from prometheus_client import Histogram
import time
REQUEST_DURATION = Histogram(
'proxy_request_duration_seconds',
'Duration of requests through the proxy',
['proxy_pool'],
buckets=[0.1, 0.25, 0.5, 1, 2, 5, 10]
)
def fetch_timed(url, proxy, pool_name="mobile"):
start = time.time()
try:
requests.get(url, proxies={"http": proxy, "https": proxy}, timeout=10)
finally:
REQUEST_DURATION.labels(proxy_pool=pool_name).observe(time.time() - start)
PromQL for p95 latency:
histogram_quantile(0.95,
sum(rate(proxy_request_duration_seconds_bucket[5m])) by (le, proxy_pool)
)
For tasks where speed is critical — for example, when automating TikTok Ads, where every second of page load increases the risk of a timeout in an anti-detect browser — it is advisable to set an alert for p95 above 3-4 seconds.
Metric 3: Error Rate by error types
A simple success/failure metric is not enough for diagnostics. It is necessary to categorize errors by type: 407 (Proxy Authentication Required) indicates an authorization problem, 429 (Too Many Requests) indicates exceeding the request limit, timeouts indicate a network issue, and 403 indicates a block due to anti-bot protection of the target site. Each type of error requires a different response.
ERROR_BY_TYPE = Counter(
'proxy_errors_total', 'Proxy errors by type',
['proxy_pool', 'error_type']
)
def classify_error(resp=None, exc=None):
if exc:
if isinstance(exc, requests.exceptions.Timeout):
return "timeout"
return "connection_error"
if resp.status_code == 407:
return "auth_error"
if resp.status_code == 429:
return "rate_limited"
if resp.status_code == 403:
return "blocked_by_target"
return "other"
A table of typical errors and their causes:
| Code/type | Probable cause | What to check |
|---|---|---|
| 407 | Invalid credentials or expired session | Proxy login/password, connection string format |
| 429 | Request limit exceeded for IP | Request frequency, pool size |
| 403 | Anti-bot protection of the target site | User-Agent, fingerprint, proxy type |
| timeout | Provider or network overload | Load on the pool, client timeout settings |
Metric 4: Rotation speed and IP longevity
If you are using rotating proxies, it is important to know how often the IP changes and how many requests can go through before the address "burns out" (gets banned by the target site). This metric is especially critical for multi-accounting — for example, when farming Facebook Ads accounts through Dolphin Anty or AdsPower, where premature IP changes can interrupt a session and trigger a security check.
from prometheus_client import Gauge
IP_LIFETIME = Gauge(
'proxy_ip_lifetime_seconds', 'IP lifetime before rotation',
['proxy_pool']
)
IP_REQUESTS_BEFORE_BAN = Histogram(
'proxy_requests_before_ban',
'How many requests went through before IP ban',
['proxy_pool'],
buckets=[10, 50, 100, 500, 1000, 5000]
)
For mobile proxies, this metric behaves differently than for residential ones — rotation through changing the operator's base station provides "cleaner" IPs with a lower likelihood of bans. If your task is specifically SMM automation with high sensitivity to bans, it is worth comparing IP longevity metrics between mobile proxies and residential ones using real data from your dashboard, rather than relying on general recommendations.
Metric 5: Bandwidth — traffic consumption
Most proxy plans are billed based on traffic volume rather than the number of requests. Without monitoring, it is easy to miss the moment when the limit is nearly exhausted — especially if the task involves loading images or videos, such as scraping product cards from Wildberries with a large number of photographs.
BANDWIDTH_USED = Counter(
'proxy_bandwidth_bytes_total', 'Traffic consumed through the proxy',
['proxy_pool', 'direction'] # direction: upload/download
)
def track_bandwidth(resp, pool_name):
size = len(resp.content)
BANDWIDTH_USED.labels(proxy_pool=pool_name, direction="download").inc(size)
On the Grafana dashboard, it is useful to build not the absolute traffic value but the rate of its consumption — this helps to predict when the limit will run out and plan for additional purchases or switching to another pool in advance.
Metric 6: Geo-distribution and regional compliance
For marketers testing ads from different countries and for arbitrageurs working with geo-targeting in Google Ads or TikTok Ads, it is critical that the IP actually corresponds to the declared region. A geolocation error can lead to showing ads to the wrong audience or blocking the account for non-compliance with geo-settings.
GEO_MISMATCH = Counter(
'proxy_geo_mismatch_total',
'Cases of mismatch between declared and actual geo',
['expected_country', 'actual_country']
)
def verify_geo(proxy, expected_country):
resp = requests.get("https://ipapi.co/json/", proxies={"https": proxy}, timeout=5)
actual = resp.json().get("country_code")
if actual != expected_country:
GEO_MISMATCH.labels(expected_country=expected_country, actual_country=actual).inc()
It is advisable to check geo not on every request (this is costly in terms of traffic and time) but periodically — once an hour on a sample of IPs from the pool that is sufficient for statistical confidence.
Metric 7: Concurrent Connections — parallel sessions
The number of simultaneous connections through the proxy shows the actual load on the pool. If the number of parallel sessions exceeds what the IPs in the pool can handle without degradation, it directly leads to an increase in latency and error rate from the previous points. This metric is especially important during scaling — when an SMM agency increases the number of concurrently operating client accounts from 10 to 50.
from prometheus_client import Gauge
ACTIVE_CONNECTIONS = Gauge(
'proxy_active_connections', 'Current active connections through the proxy',
['proxy_pool']
)
def on_request_start(pool_name):
ACTIVE_CONNECTIONS.labels(proxy_pool=pool_name).inc()
def on_request_end(pool_name):
ACTIVE_CONNECTIONS.labels(proxy_pool=pool_name).dec()
Correlating this metric with the error rate on the same graph in Grafana will quickly show the correlation: as soon as the number of parallel sessions exceeds a certain threshold, errors begin to rise non-linearly.
Setting up a dashboard in Grafana: step by step
After the metrics are instrumented and Prometheus is collecting them, you need to visualize the data in Grafana. Here are the steps:
- Add Prometheus as a data source: Configuration → Data Sources → Add data source → Prometheus, specify the URL of your Prometheus server (usually
http://localhost:9090). - Create a new dashboard: Create → Dashboard → Add new panel.
- For the Success Rate panel, use the PromQL query from the section on Metric 1, visualization type — Gauge or Time series.
- For Latency, create a panel with the Heatmap type to see the distribution of p50/p95/p99 over time.
- For Error Rate, build a Stacked Bar Chart broken down by the
error_typelabel — this will clearly show which type of errors is dominant. - Add a dashboard variable (Dashboard Settings → Variables) for
proxy_poolto switch between pools without creating separate dashboards. - Save the dashboard and set it to auto-refresh every 30 seconds for near-real-time monitoring.
The finished dashboard JSON can be exported via Share → Export → Save to file and reused for different proxy pools — residential, mobile, and datacenter, changing only the variable value.
Alerts: when to take action
A dashboard is useless if no one is watching it during an incident. Therefore, it is critical to set up alerts in Grafana (Alerting → Alert rules) with the following threshold values tailored for most production scenarios:
| Metric | Alert threshold | Action |
|---|---|---|
| Success Rate | below 85% for 5 minutes | Check the pool, switch to backup |
| p95 Latency | above 5 seconds | Reduce parallelism, check the provider |
| Rate 429 | above 10% of requests | Reduce request frequency, increase IP pool |
| Bandwidth rate | forecast of limit exhaustion in 24 hours | Purchase more traffic or optimize requests |
It is better to direct alerts to Telegram or Slack via webhook — this ensures an immediate response rather than checking the dashboard once a day.
Conclusion
Seven metrics — success rate, latency, error rate by types, IP longevity, traffic, geo-compliance, and connection concurrency — provide a complete picture of the health of the proxy infrastructure in production. The combination of Prometheus + Grafana allows not just to record problems post-factum but to see trends and react before pool degradation leads to account bans or scraping stoppages.
The choice of proxy type directly affects which metrics will be prioritized: for tasks with high sensitivity to bans, such as multi-accounting on Instagram or TikTok, it is advisable to monitor IP longevity and success rate first, using residential proxies for maximum similarity with real user traffic. For tasks where speed and volume are important — mass scraping of Ozon, Avito, or Yandex.Market — latency and bandwidth come to the forefront, and here datacenter proxies often work more effectively due to higher throughput.