proxy-pool/docs/04-api-reference.md

8.7 KiB
Raw Permalink Blame History

API reference

Authentication

All endpoints except POST /auth/register and GET /health require an API key in the Authorization header:

Authorization: Bearer pp_a1b2c3d4e5f6g7h8i9j0...

API keys use a pp_ prefix (proxy pool) followed by 48 random characters. The prefix aids visual identification and log filtering.

Unauthenticated or invalid requests return 401 Unauthorized. Requests with valid keys but insufficient credits return 402 Payment Required.

Common response patterns

Pagination

List endpoints support cursor-based pagination:

GET /proxies?limit=50&cursor=eyJpZCI6Ii4uLiJ9

Response includes a next_cursor field when more results exist:

{
  "items": [...],
  "next_cursor": "eyJpZCI6Ii4uLiJ9",
  "total_count": 1234
}

Error responses

All errors follow a consistent shape:

{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "You need at least 1 credit to acquire a proxy. Current balance: 0.",
    "details": {}
  }
}

Proxy domain endpoints

Sources

GET /sources

List all configured proxy sources.

Query parameters: is_active (bool, optional), limit (int, default 50), cursor (string, optional).

Response 200:

{
  "items": [
    {
      "id": "uuid",
      "url": "https://example.com/proxies.txt",
      "parser_name": "plaintext",
      "cron_schedule": "*/30 * * * *",
      "default_protocol": "http",
      "is_active": true,
      "last_scraped_at": "2025-01-15T10:30:00Z",
      "created_at": "2025-01-01T00:00:00Z"
    }
  ],
  "next_cursor": null,
  "total_count": 5
}

POST /sources

Add a new proxy source.

Request body:

{
  "url": "https://example.com/proxies.txt",
  "parser_name": "plaintext",
  "cron_schedule": "*/30 * * * *",
  "default_protocol": "http"
}

Validation: The parser_name must match a registered plugin. If omitted, the registry attempts auto-detection via supports(). Returns 422 if no matching parser is found.

Response 201: The created source object.

PATCH /sources/{source_id}

Update a source. All fields are optional — only provided fields are changed.

DELETE /sources/{source_id}

Delete a source. Associated proxies are NOT deleted (they may have been discovered by multiple sources).

POST /sources/{source_id}/scrape

Trigger an immediate scrape of the source, bypassing the cron schedule. Returns the scrape result.

Response 200:

{
  "source_id": "uuid",
  "proxies_discovered": 142,
  "proxies_new": 23,
  "proxies_updated": 119,
  "duration_ms": 1540
}

Proxies

GET /proxies

Query the proxy pool with filtering and sorting.

Query parameters:

Parameter Type Description
status string Filter by status: active, dead, unchecked
protocol string Filter by protocol: http, https, socks4, socks5
anonymity string Filter by anonymity: transparent, anonymous, elite
country string ISO 3166-1 alpha-2 country code
min_score float Minimum composite score (0.01.0)
max_latency_ms float Maximum average latency
min_uptime_pct float Minimum uptime percentage
verified_within_minutes int Only proxies checked within the last N minutes
sort string Sort field: score, latency, uptime, last_checked
order string Sort order: asc, desc (default: desc for score)
limit int Results per page (default: 50, max: 200)
cursor string Pagination cursor

Response 200:

{
  "items": [
    {
      "id": "uuid",
      "ip": "203.0.113.42",
      "port": 8080,
      "protocol": "http",
      "status": "active",
      "anonymity": "elite",
      "exit_ip": "203.0.113.42",
      "country": "US",
      "score": 0.87,
      "avg_latency_ms": 245.3,
      "uptime_pct": 94.2,
      "last_checked_at": "2025-01-15T10:25:00Z",
      "first_seen_at": "2025-01-10T08:00:00Z",
      "tags": {"provider": "datacenter"}
    }
  ],
  "next_cursor": "...",
  "total_count": 892
}

GET /proxies/{proxy_id}

Get detailed info for a single proxy, including recent check history.

Response 200:

{
  "proxy": { ... },
  "recent_checks": [
    {
      "checker_name": "tcp_connect",
      "stage": 1,
      "passed": true,
      "latency_ms": 120.5,
      "detail": "TCP connect OK",
      "created_at": "2025-01-15T10:25:00Z"
    }
  ]
}

POST /proxies/acquire

Acquire a proxy with an exclusive lease. Costs 1 credit.

Request body:

{
  "protocol": "http",
  "country": "US",
  "anonymity": "elite",
  "min_score": 0.7,
  "lease_duration_seconds": 300
}

All filter fields are optional. lease_duration_seconds defaults to 300 (5 minutes), max 3600 (1 hour).

Response 200:

{
  "lease_id": "uuid",
  "proxy": {
    "ip": "203.0.113.42",
    "port": 8080,
    "protocol": "http",
    "country": "US",
    "anonymity": "elite"
  },
  "expires_at": "2025-01-15T10:30:00Z",
  "credits_remaining": 42
}

Error responses: 402 if insufficient credits, 404 if no proxy matches the filters, 409 if all matching proxies are currently leased.

POST /proxies/acquire/{lease_id}/release

Release a lease early. The proxy becomes available immediately. The credit is NOT refunded (credits are consumed on acquisition).

POST /proxies/test

Test whether good proxies can reach a specific URL.

Request body:

{
  "url": "https://example.com",
  "count": 5,
  "timeout_seconds": 10,
  "protocol": "http",
  "country": "US"
}

Response 200:

{
  "url": "https://example.com",
  "results": [
    {
      "proxy_id": "uuid",
      "ip": "203.0.113.42",
      "port": 8080,
      "reachable": true,
      "status_code": 200,
      "latency_ms": 340,
      "error": null
    },
    {
      "proxy_id": "uuid",
      "ip": "198.51.100.10",
      "port": 3128,
      "reachable": false,
      "status_code": null,
      "latency_ms": null,
      "error": "Connection refused by target"
    }
  ],
  "success_rate": 0.8
}

Stats

GET /stats/pool

Pool health overview.

Response 200:

{
  "total_proxies": 15420,
  "by_status": {"active": 3200, "dead": 11800, "unchecked": 420},
  "by_protocol": {"http": 8000, "https": 4000, "socks4": 1200, "socks5": 2220},
  "by_anonymity": {"transparent": 1500, "anonymous": 1000, "elite": 700},
  "avg_score": 0.62,
  "avg_latency_ms": 380.5,
  "sources_active": 12,
  "sources_total": 15,
  "last_scrape_at": "2025-01-15T10:30:00Z",
  "last_validation_at": "2025-01-15T10:25:00Z"
}

GET /stats/plugins

Plugin registry status.

Response 200:

{
  "parsers": [{"name": "plaintext", "type": "SourceParser"}],
  "checkers": [
    {"name": "tcp_connect", "stage": 1, "priority": 0},
    {"name": "http_anonymity", "stage": 2, "priority": 0}
  ],
  "notifiers": [
    {"name": "smtp", "healthy": true, "subscribes_to": ["proxy.pool_low", "credits.*"]},
    {"name": "webhook", "healthy": false, "subscribes_to": ["*"]}
  ]
}

Accounts domain endpoints

Auth

POST /auth/register

Create a new user account and initial API key. No authentication required.

Request body:

{
  "email": "user@example.com",
  "display_name": "Alice"
}

Response 201:

{
  "user": {"id": "uuid", "email": "user@example.com", "display_name": "Alice"},
  "api_key": {
    "id": "uuid",
    "key": "pp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4",
    "prefix": "pp_a1b2c",
    "label": "default"
  }
}

Important: The key field is the raw API key. It is returned ONLY in this response. Store it securely — it cannot be retrieved again.

POST /auth/keys

Create an additional API key for the authenticated user.

GET /auth/keys

List all API keys for the authenticated user (returns prefix and metadata, never the full key).

DELETE /auth/keys/{key_id}

Revoke an API key.

Account

GET /account

Get the authenticated user's account info.

GET /account/credits

Get current credit balance and recent transaction history.

Response 200:

{
  "balance": 42,
  "recent_transactions": [
    {
      "amount": -1,
      "tx_type": "acquire",
      "description": "Proxy acquired: 203.0.113.42:8080",
      "created_at": "2025-01-15T10:25:00Z"
    },
    {
      "amount": 100,
      "tx_type": "purchase",
      "description": "Credit purchase",
      "created_at": "2025-01-14T00:00:00Z"
    }
  ]
}

GET /account/leases

List the authenticated user's active proxy leases.

System endpoints

GET /health

Basic health check. No authentication required.

Response 200:

{
  "status": "healthy",
  "postgres": "connected",
  "redis": "connected",
  "version": "0.1.0"
}