Order API
FonProxy Order API reference documentation.
FonProxy API β Orders
All
/orders/*endpoints requireAuthorization: Bearer <token>unless noted.
Orders
GET /orders/config
Get the full proxy catalog config. Public β no auth required.
Response (200):
{
"proxyTypes": [
{
"id": "residential-giga",
"name": "Residential Giga",
"slug": "residential-giga",
"shortDescription": "Real residential IPs, pay per Giga β maximum trust.",
"paymentModel": "prepaid",
"unit": "giga",
"pricePerUnit": 1.5,
"hasTraffic": true,
"trafficDiscounts": [
{ "min": 10, "discount": 0.05, "label": "5% off" }
],
"countDiscounts": [],
"tags": ["Rotating IP", "Real ISP"],
"countryMode": "anytime",
"requiresCountry": false,
"supportsCountry": true,
"supportsCity": true,
"supportsState": true,
"supportsChangeCountry": true,
"billingPeriods": [],
"presets": [
{ "label": "Trial", "trafficGb": 1 }
]
}
],
"billingPeriods": [
{ "id": "week", "label": "Weekly", "multiplier": 0.4167 },
{ "id": "month", "label": "Monthly", "multiplier": 1 },
{ "id": "year", "label": "Yearly", "multiplier": 10.5 }
],
"minOrderAmount": 0.5,
"trafficPricePerGb": 0.10,
"trafficVolumeDiscounts": [
{ "min": 50, "discount": 0.05, "label": "5% off traffic" },
{ "min": 100, "discount": 0.10, "label": "10% off traffic" },
{ "min": 500, "discount": 0.15, "label": "15% off traffic" }
]
}
POST /orders/preview
Preview order pricing before buying. Requires JWT.
Request body (prepaid giga):
{
"proxyTypeId": "residential-giga",
"trafficGb": 50
}
Request body (postpaid IP):
{
"proxyTypeId": "private-proxy",
"count": 25,
"trafficGb": 50,
"billingPeriod": "month",
"countries": { "US": 10, "DE": 10, "GB": 5 }
}
| Field | Type | Required | Description |
|---|---|---|---|
proxyTypeId | string | yes | Proxy type ID from config |
trafficGb | number | giga types: yes | GB of traffic |
count | number | ip types: yes | Number of IPs |
billingPeriod | string | no | week/month/year |
countries | object | no | Country β count map |
Response (200):
{
"proxyType": {
"id": "residential-giga",
"name": "Residential Giga",
"paymentModel": "prepaid",
"unit": "giga"
},
"breakdown": {
"proxyTypeId": "residential-giga",
"count": 0,
"trafficGb": 50,
"subtotal": 63.75,
"discountAmount": 11.25,
"total": 63.75
},
"display": {
"totalDisplay": 2637.19,
"currency": "UAH",
"exchangeRate": 41.37
}
}
POST /orders/create
Create a real order. Both prepaid and postpaid orders deduct balance immediately. Requires JWT.
Request body (postpaid with selected countries):
{
"proxyTypeId": "private-proxy",
"count": 25,
"trafficGb": 50,
"billingPeriod": "month",
"countries": { "US": 10, "DE": 10, "GB": 5 }
}
Request body (postpaid with random countries):
{
"proxyTypeId": "private-proxy",
"count": 25,
"trafficGb": 50,
"billingPeriod": "month"
}
When
countriesis omitted ornull, IPs are assigned from random available countries. Country selection does not affect price.
Response (200): Full order object (same as GET /orders/:id).
Errors:
{ "message": "order.invalid_proxy_type" }
{ "message": "order.invalid_billing_period" }
{ "message": "order.invalid_count" }
{ "message": "order.invalid_traffic_gb" }
{ "message": "order.countries_required" }
{ "message": "order.insufficient_balance" }
GET /orders
List user's orders (paginated). Requires JWT.
Query params: ?page=1&limit=20&type=residential-giga
Response (200):
{
"orders": [ ],
"total": 42,
"page": 1,
"pages": 3
}
GET /orders/:id
Get a single order by hashid. Requires JWT.
Response (200):
{
"id": "k5Xz9qR2Wp",
"name": "My US Proxies",
"status": "paid",
"proxyType": {
"id": "residential-giga",
"name": "Residential Giga",
"paymentModel": "prepaid",
"unit": "giga"
},
"count": 0,
"trafficGb": 50,
"billingPeriod": null,
"proxyUsername": "user_a1b2c3d4e5f6",
"proxyPassword": "xYz9q2Wp4rT8mN1k",
"peer": {
"host": "us1.fonproxy.com",
"portMin": 10000,
"portMax": 19999
},
"countries": null,
"usage": {
"uploadBytes": 0,
"downloadBytes": 0,
"totalBytes": 0,
"requestCount": 0,
"uploadGb": 0,
"downloadGb": 0,
"totalGb": 0,
"maxBytes": 53687091200,
"maxGb": 50,
"remainingBytes": 53687091200,
"remainingGb": 50,
"usedPercent": 0,
"lastReportedAt": null
},
"pricing": {
"pricePerUnit": 1.5,
"subtotal": 63.75,
"discountAmount": 11.25,
"totalUsd": 63.75,
"totalDisplay": 2637.19,
"currency": "UAH",
"exchangeRate": 41.37
},
"dates": {
"createdAt": "2026-03-16T10:00:00.000Z",
"paidAt": "2026-03-16T10:00:01.000Z"
},
"meta": null
}
Order statuses: pending, paid, active, expired, cancelled, refunded
Usage fields
| Field | Type | Description |
|---|---|---|
uploadBytes | number | Total bytes uploaded |
downloadBytes | number | Total bytes downloaded |
totalBytes | number | Upload + download |
requestCount | number | Total proxy requests processed |
uploadGb | number | Upload in GB (3 decimal places) |
downloadGb | number | Download in GB (3 decimal places) |
totalGb | number | Total traffic in GB |
maxBytes | number|null | Max allowed bytes (giga orders only, null for IP orders) |
maxGb | number|null | Max allowed GB (= order.trafficGb for giga, null for IP) |
remainingBytes | number|null | Remaining bytes before quota is exhausted |
remainingGb | number|null | Remaining GB |
usedPercent | number|null | Percentage of quota used (0β100, 2 decimal places) |
lastReportedAt | string|null | ISO timestamp of last peer usage report |
For giga orders (
unit: 'giga'):maxGbequals the purchasedtrafficGb. Frontend can show a progress bar usingusedPercent. For IP orders (unit: 'ip'):maxBytes,maxGb,remainingBytes,remainingGb,usedPercentare allnullβ traffic is informational only.
Automatic IP provisioning
After an order is created and paid, IP assignment happens automatically via an event-driven background process:
| Payment model | Behaviour |
|---|---|
| prepaid | Balance is deducted immediately. IPs are auto-assigned by country. Order is always activated β even if some countries had insufficient IPs (partial). Missing IPs stored in meta.failedCountries. Notification sent on activation. |
| postpaid | Order is created as pending. IPs are auto-assigned by country. If all countries are fully filled β order activates immediately + notification sent. If any country is short β order stays pending (meta.provisioningPending = true, meta.failedCountries lists what's missing). Admin must manually add the remaining IPs to trigger activation. |
meta provisioning fields:
{
"provisioningPartial": true,
"assignedCountries": { "US": 3, "CA": 2 },
"failedCountries": { "DE": 1 }
}
Admins can use
POST /admin/orders/:id/ips/assign-by-countryorPOST /admin/orders/:id/ips/add-by-countryto fill missing slots.
GET /orders/:id/ips
Get the IPs assigned to an order. Requires JWT or API key.
- Only active (assigned) and visible IPs are returned β IPs hidden by admin (
visible=false) are excluded. - The
indexfield is the absolute position (0-based). There may be gaps where hidden IPs sit β this is intentional. - Connection info is computed from the active peer:
host : portMin + index. - Credentials are the order's own
proxyUsername/proxyPasswordβ NOT the original proxy creds.
Response (200):
{
"orderId": "K4Xb9r",
"credentials": {
"username": "user_a1b2c3d4e5f6",
"password": "xYz9q2Wp4rT8mN1k"
},
"ips": [
{
"index": 0,
"host": "us1.fonproxy.com",
"port": 10000,
"originalIp": "185.123.45.67",
"countryCode": { "geoip": "US" },
"city": { "geoip": "New York" },
"state": { "geoip": "NY" },
"status": "active",
"lastVerifiedAt": "2026-03-23T08:14:00.000Z",
"assignedAt": "2026-03-20T10:01:00.000Z"
},
{
"index": 2,
"host": "us1.fonproxy.com",
"port": 10002,
"originalIp": "91.45.12.33",
"countryCode": { "geoip": "DE" },
"city": { "geoip": "Berlin" },
"state": {},
"status": "active",
"lastVerifiedAt": "2026-03-23T08:10:00.000Z",
"assignedAt": "2026-03-20T10:01:00.000Z"
}
],
"total": 2
}
Index
1is missing β that IP was hidden by admin. The user sees 0, 2 with ports 10000, 10002.Use
credentials.username/credentials.passwordto authenticate withhost:port.If no peer is active,
hostandportwill benull.
POST /orders/:id/reset-hash
Reset the public download hash for an order. Requires JWT.
Response (200):
{ "publicHash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" }
PATCH /orders/:id/settings
Update user-editable order settings. Requires JWT. All fields optional.
| Field | Type | Description |
|---|---|---|
name | string | null | Custom label. Pass null to clear. Falls back to order hashid in responses. |
proxyUsername | string | New proxy username. Only letters and digits. Always stored as u_<value>. Cannot be empty after stripping. |
proxyPassword | string | New proxy password. Cannot be empty. |
Body:
{
"name": "My US Proxies",
"proxyUsername": "myuser",
"proxyPassword": "mySecurePass123"
}
Response (200): Full order object (same as GET /orders/:id).
Errors:
{ "message": "order.username_empty" }
{ "message": "order.password_empty" }
Proxy Download (Public β No Auth)
GET /proxy/formats
List available export formats. Public.
{
"formats": [
{ "id": "txt", "name": "Plain text β host:port:user:pass", "extension": "txt" },
{ "id": "user-pass-at", "name": "user:pass@host:port", "extension": "txt" },
{ "id": "proto-url", "name": "proto://user:pass@host:port", "extension": "txt" },
{ "id": "curl", "name": "cURL commands", "extension": "sh" },
{ "id": "json", "name": "JSON array", "extension": "json" },
{ "id": "csv", "name": "CSV β host,port,user,pass,proto", "extension": "csv" }
]
}
GET /proxy/dl/:hash?format=txt&proto=http
Download proxy list by public hash. No authentication required.
| Param | Type | Default | Description |
|---|---|---|---|
hash | string | β | The order's publicHash |
format | string | txt | Export format id (see /proxy/formats) |
proto | string | http | Protocol: http or socks5 |
Returns a file download with the appropriate content type.
Example txt response:
us1.fonproxy.com:10000:u_a1b2c3:xYz9q2Wp4rT8mN1k
us1.fonproxy.com:10002:u_a1b2c3:xYz9q2Wp4rT8mN1k
Example curl response:
curl --proxy u_a1b2c3:xYz9q2Wp4rT8mN1k@us1.fonproxy.com:10000 https://httpbin.org/ip
curl --proxy u_a1b2c3:xYz9q2Wp4rT8mN1k@us1.fonproxy.com:10002 https://httpbin.org/ip
Example curl with socks5 (?proto=socks5):
curl --socks5 u_a1b2c3:xYz9q2Wp4rT8mN1k@us1.fonproxy.com:10000 https://httpbin.org/ip
The hash is visible in the order response as
publicHash. Reset it withPOST /orders/:id/reset-hashif compromised.