Point-in-time metrics tell you what's happening now. Time-series data tells you what changed, when it changed, and whether something is trending toward a problem. Track CAPTCHA solve rates, latency, and cost over time to catch degradation before it impacts your pipeline.
What to Track
| Metric | Type | Why |
|---|---|---|
| Solve rate (%) | Gauge | Detect provider quality changes |
| Solve latency (ms) | Histogram | Spot slowdowns, plan timeouts |
| Error rate by code | Counter | Identify emerging error patterns |
| Cost per solve ($) | Gauge | Budget tracking, anomaly detection |
| Queue depth | Gauge | Capacity planning |
| Tokens expired before use | Counter | TTL tuning signal |
| API balance | Gauge | Refill trigger |
Prometheus + Python (Push Gateway)
Instrument Your Solver
import os
import time
import requests
from prometheus_client import CollectorRegistry, Counter, Histogram, Gauge, push_to_gateway
registry = CollectorRegistry()
SOLVE_TOTAL = Counter(
"captcha_solve_total", "Total CAPTCHA solve attempts",
["type", "status"], registry=registry
)
SOLVE_LATENCY = Histogram(
"captcha_solve_latency_seconds", "CAPTCHA solve latency",
["type"], buckets=[5, 10, 15, 20, 30, 45, 60, 90, 120],
registry=registry
)
SOLVE_COST = Counter(
"captcha_solve_cost_dollars", "Total cost of CAPTCHA solves",
["type"], registry=registry
)
API_BALANCE = Gauge(
"captcha_api_balance_dollars", "CaptchaAI account balance",
registry=registry
)
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
PUSHGATEWAY = os.environ.get("PUSHGATEWAY_URL", "localhost:9091")
def solve_with_metrics(sitekey, pageurl, captcha_type="recaptcha_v2"):
start = time.time()
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
})
data = resp.json()
if data.get("status") != 1:
SOLVE_TOTAL.labels(type=captcha_type, status="submit_error").inc()
push_metrics()
return {"error": data.get("request")}
captcha_id = data["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get",
"id": captcha_id, "json": 1
}).json()
if result.get("status") == 1:
elapsed = time.time() - start
SOLVE_TOTAL.labels(type=captcha_type, status="solved").inc()
SOLVE_LATENCY.labels(type=captcha_type).observe(elapsed)
SOLVE_COST.labels(type=captcha_type).inc(0.00299)
push_metrics()
return {"solution": result["request"]}
if result.get("request") != "CAPCHA_NOT_READY":
SOLVE_TOTAL.labels(type=captcha_type, status="error").inc()
push_metrics()
return {"error": result.get("request")}
SOLVE_TOTAL.labels(type=captcha_type, status="timeout").inc()
push_metrics()
return {"error": "TIMEOUT"}
def push_metrics():
try:
push_to_gateway(PUSHGATEWAY, job="captcha_solver", registry=registry)
except Exception:
pass # Don't fail solving because metrics push failed
def update_balance():
resp = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "getbalance"
})
try:
balance = float(resp.text)
API_BALANCE.set(balance)
push_metrics()
except ValueError:
pass
Prometheus Queries
# Success rate over last hour
rate(captcha_solve_total{status="solved"}[1h])
/ rate(captcha_solve_total[1h]) * 100
# P95 solve latency
histogram_quantile(0.95, rate(captcha_solve_latency_seconds_bucket[1h]))
# Error rate by type
rate(captcha_solve_total{status="error"}[1h])
# Hourly cost
increase(captcha_solve_cost_dollars_total[1h])
InfluxDB + Python
Write Solve Metrics
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
INFLUX_URL = os.environ.get("INFLUX_URL", "http://localhost:8086")
INFLUX_TOKEN = os.environ.get("INFLUX_TOKEN", "")
INFLUX_ORG = os.environ.get("INFLUX_ORG", "captcha")
INFLUX_BUCKET = os.environ.get("INFLUX_BUCKET", "captcha_metrics")
influx_client = InfluxDBClient(url=INFLUX_URL, token=INFLUX_TOKEN, org=INFLUX_ORG)
write_api = influx_client.write_api(write_options=SYNCHRONOUS)
def record_solve_metric(captcha_type, status, elapsed_ms, cost=0.0, error=None):
point = (
Point("captcha_solve")
.tag("type", captcha_type)
.tag("status", status)
.field("elapsed_ms", elapsed_ms)
.field("cost", cost)
.field("success", 1 if status == "solved" else 0)
)
if error:
point = point.tag("error_code", error)
write_api.write(bucket=INFLUX_BUCKET, record=point)
def record_balance(balance):
point = Point("captcha_balance").field("balance", balance)
write_api.write(bucket=INFLUX_BUCKET, record=point)
InfluxDB Queries (Flux)
// Success rate over last 24 hours (1-hour windows)
from(bucket: "captcha_metrics")
|> range(start: -24h)
|> filter(fn: (r) => r._measurement == "captcha_solve" and r._field == "success")
|> aggregateWindow(every: 1h, fn: mean)
|> map(fn: (r) => ({r with _value: r._value * 100.0}))
|> yield(name: "success_rate")
// Average solve time by type
from(bucket: "captcha_metrics")
|> range(start: -24h)
|> filter(fn: (r) => r._measurement == "captcha_solve" and r._field == "elapsed_ms" and r.status == "solved")
|> group(columns: ["type"])
|> aggregateWindow(every: 1h, fn: mean)
|> yield(name: "avg_latency")
// Cumulative cost
from(bucket: "captcha_metrics")
|> range(start: -24h)
|> filter(fn: (r) => r._measurement == "captcha_solve" and r._field == "cost")
|> cumulativeSum()
|> yield(name: "cumulative_cost")
JavaScript Implementation (Prometheus)
const client = require("prom-client");
const axios = require("axios");
const register = new client.Registry();
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const solveTotal = new client.Counter({
name: "captcha_solve_total",
help: "Total CAPTCHA solve attempts",
labelNames: ["type", "status"],
registers: [register],
});
const solveLatency = new client.Histogram({
name: "captcha_solve_latency_seconds",
help: "CAPTCHA solve latency",
labelNames: ["type"],
buckets: [5, 10, 15, 20, 30, 45, 60, 90, 120],
registers: [register],
});
async function solveWithMetrics(sitekey, pageurl, type = "recaptcha_v2") {
const start = Date.now();
const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: { key: API_KEY, method: "userrecaptcha", googlekey: sitekey, pageurl, json: 1 },
});
if (submit.data.status !== 1) {
solveTotal.inc({ type, status: "submit_error" });
return { error: submit.data.request };
}
const captchaId = submit.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const poll = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
});
if (poll.data.status === 1) {
const elapsed = (Date.now() - start) / 1000;
solveTotal.inc({ type, status: "solved" });
solveLatency.observe({ type }, elapsed);
return { solution: poll.data.request };
}
if (poll.data.request !== "CAPCHA_NOT_READY") {
solveTotal.inc({ type, status: "error" });
return { error: poll.data.request };
}
}
solveTotal.inc({ type, status: "timeout" });
return { error: "TIMEOUT" };
}
// Expose metrics endpoint
const express = require("express");
const app = express();
app.get("/metrics", async (req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});
app.listen(9090);
Database Comparison
| Feature | Prometheus | InfluxDB | TimescaleDB |
|---|---|---|---|
| Best for | Operational monitoring | IoT / high-cardinality metrics | SQL-based analytics |
| Query language | PromQL | Flux | SQL |
| Retention | Config-based | Policy-based | PostgreSQL retention |
| Grafana integration | Native | Native | Native |
| Learning curve | Low | Medium | Low (if you know SQL) |
| Self-hosted | Yes | Yes | Yes (PostgreSQL extension) |
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Metrics gaps in dashboard | Push gateway not receiving data | Check network between solver and push gateway |
| Latency histogram shows wrong percentiles | Bucket boundaries don't match workload | Adjust buckets: [5, 10, 15, 20, 30, 45, 60, 90, 120] for CAPTCHA solving |
| Cost metrics don't match actual spend | Different prices per CAPTCHA type | Tag cost by type; use actual per-type pricing |
| Too much cardinality | Too many label values | Limit labels to type, status, error_code |
FAQ
Which time-series database should I use?
Prometheus if you already have it for infrastructure monitoring — just add CAPTCHA metrics. InfluxDB if you want a standalone metrics store. TimescaleDB if you want SQL queries on time-series data.
How long should I retain CAPTCHA metrics?
Keep high-resolution data (per-second) for 7 days, aggregated (per-hour) for 90 days, and daily summaries indefinitely. This balances storage cost with trend visibility.
Can I detect solve rate degradation automatically?
Yes. Set alerts on rolling averages — e.g., alert when the 1-hour success rate drops below 90% or when P95 latency exceeds 45 seconds. Both Prometheus Alertmanager and InfluxDB alerts support this.
Next Steps
Track your CAPTCHA solving performance over time — get your CaptchaAI API key and start collecting metrics.
Related guides:
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.