406 lines
8.7 KiB
Markdown
406 lines
8.7 KiB
Markdown
# 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:
|
||
|
||
```json
|
||
{
|
||
"items": [...],
|
||
"next_cursor": "eyJpZCI6Ii4uLiJ9",
|
||
"total_count": 1234
|
||
}
|
||
```
|
||
|
||
### Error responses
|
||
|
||
All errors follow a consistent shape:
|
||
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"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**:
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"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.0–1.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`:
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"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**:
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"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**:
|
||
```json
|
||
{
|
||
"url": "https://example.com",
|
||
"count": 5,
|
||
"timeout_seconds": 10,
|
||
"protocol": "http",
|
||
"country": "US"
|
||
}
|
||
```
|
||
|
||
**Response** `200`:
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"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**:
|
||
```json
|
||
{
|
||
"email": "user@example.com",
|
||
"display_name": "Alice"
|
||
}
|
||
```
|
||
|
||
**Response** `201`:
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"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`:
|
||
```json
|
||
{
|
||
"status": "healthy",
|
||
"postgres": "connected",
|
||
"redis": "connected",
|
||
"version": "0.1.0"
|
||
}
|
||
```
|