Documentation
AI Proxies is a single HTTP/SOCKS5 gateway in front of four egress products. There is no SDK to install: you authenticate with a proxy username and password, and every request can carry its own targeting flags.
402 payment_required on every provisioning endpoint.
Quickstart
Buy a plan from the dashboard. Once payment clears you get an API key, a proxy username and a proxy password.
# 1. export the proxy URL
export PROXY_URL="http://USERNAME:PASSWORD@gate.aiproxies.dev:8000"
# 2. make a request
curl -x "$PROXY_URL" https://httpbin.org/ip
# 3. confirm the exit IP
curl -s -x "$PROXY_URL" https://api.ipify.org
Every response through the gateway carries the exit IP in X-AIP-IP, the resolved country in X-AIP-Country, and the session id (when pinned) in X-AIP-Session.
Billing & provisioning
Signing up creates an account and returns an API key, but no proxy credentials. Credentials are generated the moment a payment is confirmed, by one of:
- the Stripe
checkout.session.completedwebhook, POST /api/checkout/confirmwhen the browser returns from Stripe, or- the reconciliation inside
GET /api/me, so a closed tab does not strand a paid account.
| Status | Meaning | Proxy access |
|---|---|---|
pending_payment | Account exists, payment not confirmed | Blocked — 402 |
active | Paid, credentials provisioned | Allowed |
suspended | Canceled subscription or AUP action | Blocked — 401 |
Provisioning is idempotent: a webhook retry, a refresh and a confirm call all converge on the same credentials and never rotate them.
POST /api/admin with { "action": "activate", "accountId": "acc_…" }.
Authentication
Signing in
The dashboard accepts either your API key or a single-use sign-in link emailed to the address on the account. Request one with POST /api/auth/magic-link; the response is always 202 so the endpoint cannot be used to probe which emails have accounts. Links expire after 15 minutes, work once, and requests are throttled to one per address per minute.
Exchange the token from the link with POST /api/auth/exchange, which returns your existing API key (sealed at rest with AES-256-GCM, so it can be handed back without storing it in plaintext). A sign-in alert email is sent every time this succeeds.
Proxy credentials
The proxy username encodes targeting. The password is a per-account secret you can rotate from the dashboard at any time — rotating invalidates the old password immediately and does not affect your API key.
http://user-country-us-session-ab12-product-res:PASSWORD@gate.aiproxies.dev:8000
socks5://user-country-de:PASSWORD@gate.aiproxies.dev:1080
Control API
The control API is used by the dashboard and by your own tooling. Authenticate with the API key as a bearer token. Keys are shown once at creation and stored only as a SHA-256 hash.
curl https://aiproxies.dev/api/me \
-H "Authorization: Bearer sk_live_..."
Gateway endpoint
| Product | Protocol | Host | Port |
|---|---|---|---|
| All (HTTP/HTTPS) | HTTP CONNECT | gate.aiproxies.dev | 8000 |
| All (SOCKS5) | SOCKS5 | gate.aiproxies.dev | 1080 |
| Dedicated ISP | HTTP / SOCKS5 | isp.aiproxies.dev | 8000 / 1080 |
Region-specific entry points (us-gate, eu-gate, ap-gate) exist for latency-sensitive workloads and accept the same credentials.
Username flags
Flags are dash-separated, lowercase, and order-independent. Unknown flags are rejected with auth_failed rather than ignored, so a typo cannot silently change your egress.
| Flag | Values | Example | Notes |
|---|---|---|---|
country- | ISO 3166-1 alpha-2 | country-de | Defaults to random if omitted |
region- | Provider region code | region-bavaria | Residential only |
city- | Lowercase city slug | city-berlin | Availability varies |
asn- | AS number | asn-3320 | Residential only |
session- | [a-z0-9]{1,32} | session-ab12 | Pins one IP for 30 min |
product- | res · isp · mob · dc | product-isp | Defaults to res |
sticky- | minutes, 1–30 | sticky-10 | Overrides session TTL |
Flags are additive. A request with country-us-session-ab12-product-isp exits from a US ISP address and keeps that address across requests until the session expires.
Header flags
When the client cannot express a username (some frameworks mangle special characters), send the same values as request headers. Headers win over username flags.
X-AIP-Country: jp
X-AIP-City: tokyo
X-AIP-Session: ab12
X-AIP-Product: res
Sticky sessions
Sessions are named, not allocated: the first request for a session id claims an exit IP, and subsequent requests reuse it until the TTL expires or POST /api/sessions/{id}/release is called. There is no hard cap on how many sessions a key can create, but concurrent in-flight connections are capped by plan.
- Default TTL: 30 minutes, sliding while traffic continues.
- Sessions are per key. Two keys using
ab12get different IPs. - If the assigned peer goes unhealthy, the session rotates and returns
X-AIP-Rotated: 1.
Control API
| Method | Path | Purpose |
|---|---|---|
POST | /api/auth/magic-link | Email a single-use sign-in link (always 202) |
POST | /api/auth/exchange | Trade a sign-in token for the account's API key |
POST | /api/signup | Create an account in pending_payment; returns the API key once |
POST | /api/checkout | Start or restart a Stripe Checkout session for the account's plan |
POST | /api/checkout/confirm | Verify a Stripe session id and provision on success |
GET | /api/me | Account, plan, status, usage and (once paid) proxy credentials |
POST | /api/rotate | Rotate the proxy password (active accounts only) |
GET | /api/proxies | Gateway list for the account's plan (active accounts only) |
POST | /api/usage | Report metered traffic (gateway → control plane) |
POST | /api/webhooks/stripe | Signed Stripe events; activates on payment, suspends on cancellation |
POST | /api/lead | Contact sales / enterprise request |
All responses are JSON. Errors use { "error": "code", "message": "human readable" } with a matching HTTP status.
Webhook
POST /api/webhooks/stripe verifies the Stripe-Signature header against the raw request body with a 5-minute tolerance. It handles checkout.session.completed, invoice.paid (renewals), invoice.payment_failed (dunning email) and customer.subscription.deleted (suspends the account). Valid signatures always receive 200, including for event types the service ignores, so Stripe stops retrying.
Every state change sends a transactional email: account created with the checkout link, payment confirmed with credentials and a first-request snippet, payment failed, plan canceled, account suspended, password rotated, sign-in link, sign-in alert, and lead notifications. Delivery goes through Resend when RESEND_API_KEY is set; otherwise emails are written to an outbox and logged, which is how local development and the test suite read sign-in links.
Error codes
| Code | Status | Meaning | Retry? |
|---|---|---|---|
auth_failed | 407 | Bad username flags or rotated password | No |
payment_required | 402 | Account has no active plan; credentials not provisioned | No — complete payment |
quota_exceeded | 402 | Request or bandwidth cap reached | No — raise the cap |
geo_unavailable | 422 | No peer matches the requested targeting | No — widen targeting |
blocked | 403 | Target rejected the exit IP; gateway already retried once | Yes, with a new session |
target_timeout | 504 | Target did not respond within 30 s | Yes, once |
concurrency_limited | 429 | In-flight connections exceed plan | Yes, with backoff |
upstream_error | 502 | Peer failed mid-request | Yes |
Through the proxy, the code is duplicated in the X-AIP-Error response header and in the JSON body when the target is HTTPS. Branch on the code, never on the HTML.
Usage metering
curl https://aiproxies.dev/api/me \
-H "Authorization: Bearer sk_live_..."
{
"accountId": "acc_9f2c",
"status": "active",
"plan": { "name": "Starter", "price": 49, "quota": 200000 },
"usage": { "requests": 148, "gb": 0.31 },
"limits": { "requestsPerMonth": 200000, "gbPerMonth": 10, "concurrency": 20 },
"proxy": { "username": "user-product-res", "host": "gate.aiproxies.dev" }
}
While an account is in pending_payment, proxy is null and paymentRequired is true. Counters are near real-time (under 60 s lag).
Tool schema
Give the model one tool instead of a proxy string. This schema works with the OpenAI, Anthropic and Gemini function-calling formats with no changes.
{
"name": "egress_fetch",
"description": "Fetch a URL through a chosen egress region and return the status, final URL, body and exit IP.",
"input_schema": {
"type": "object",
"properties": {
"url": { "type": "string", "description": "Absolute https URL" },
"country": { "type": "string", "description": "ISO 3166-1 alpha-2, e.g. de" },
"product": { "type": "string", "enum": ["res", "isp", "mob", "dc"] },
"session": { "type": "string", "description": "Reuse an id to keep the same exit IP" }
},
"required": ["url"]
}
}
Return a compact object — {status, finalUrl, exitIp, error, body} — so the model reasons over results, not connection details. Keep the body truncated (for example 8 KB) to protect the context window.
Agent patterns
Rotate per request, pin per task
Use no session flag for broad discovery calls and a deterministic session id (task id, or hash of the seed URL) for multi-step flows such as login → cart → checkout. A pinned session that succeeds is worth keeping for the whole task.
Retry with intent
blocked→ new session, same country, once.geo_unavailable→ widen from city to country, once.target_timeout→ same session, one retry with backoff.quota_exceeded→ stop the task and surface it; do not loop.
Cap before you ship
Set the per-key request ceiling just above your expected task cost. The gateway returns 402 instead of billing, which turns a runaway loop into a handled failure.
Rate limits
| Plan | Concurrent connections | Requests / min | Control API |
|---|---|---|---|
| Starter | 20 | 1,200 | 120 / min |
| Scale | 200 | 12,000 | 600 / min |
| Enterprise | Custom | Custom | Custom |
Rate-limit responses include Retry-After. The control API is rate-limited per key, not per IP.
Logging & privacy
Request bodies, response bodies, headers and target content are never stored. We retain connection metadata — key, timestamp, exit IP, bytes, hostname, status — for 24 hours for abuse handling and billing reconciliation, then delete it. Privacy policy · Acceptable use
Questions about a specific integration? support@aiproxies.dev.