Skip to content

Hybrid and headless integration

Keep your identity, checkout, subscriptions and UI. Use ProxyRequest to provision proxy access and account for traffic. This quickstart creates independent purchased data for one customer; it does not allocate from a reseller’s pool. For that model, use the reseller quickstart.

  • Use a test deployment with the packages you want to sell and a working gateway.
  • Prepare a finite residential package whose pricing accepts 10 GiB. Store its returned package_id; a product name is not an API identifier.
  • Use a backend-only service account with is_superuser=true, is_reseller=true and no parent. The superuser flag permits status=paid; the reseller flag is also required when sending invoice user_id.
  • Install cURL or a compatible official SDK for the API steps; keep cURL for the proxy smoke test. Use a generated customer password of 8–128 characters. Never expose the service key in your frontend.
RequestGET /profile
curl 'https://{api_host}/api/v1/profile' \
--header 'Authorization: Static {api_key}'
Response200 OK

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440010",
"username": "customer_demo_42",
"is_reseller": true,
"is_superuser": true,
"orders": []
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440010",
"username": "customer_demo_42",
"email": "",
"is_reseller": true,
"is_marketer": false,
"is_superuser": true,
"date_joined": "2026-09-16T12:00:00Z",
"date_joined_ts": 1789560000,
"first_name": "",
"last_name": "",
"balance": 0,
"language": "en",
"country": "",
"state": "",
"city": "",
"address": "",
"zip": "",
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_country": "",
"company_vat_number": "",
"allowed_ips": [],
"blocked_domains": [],
"connection_limit": 100,
"parent_id": "",
"sub_users": 0,
"referrals": 0,
"referral_id": "",
"referral_code": "example42",
"referral_data_earned": 0,
"referral_data_pending": 0,
"referral_balance_pending": 0,
"referral_balance_earned": 0,
"currency": {
"code": "USD",
"symbol": "$"
},
"coupons": [],
"orders": []
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

Replace {api_host} with your API hostname (without https://), and {api_key} with the test service key. Use your configured {package_id} and a unique {username} with a generated {account_password}. Check the profile before continuing.

Persist each write body and the current workflow step before sending it. Store every returned resource ID before moving to the next step.

1. Create identity without allocating data

Section titled “1. Create identity without allocating data”

POST /users returns 201. Leave is_top_level at its default false to create a managed identity under the service account. Omit package_id and data so identity creation does not allocate a shared-pool quota. The separate invoice will buy this customer’s own root order: managed identity does not mean shared traffic.

RequestPOST /users
curl --request POST 'https://{api_host}/api/v1/users' \
--header 'Authorization: Static {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"username": "{username}",
"password": "{account_password}"
}'
Response201 Created

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440001",
"username": "customer_demo_42",
"is_reseller": false,
"is_superuser": false,
"orders": []
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"username": "customer_demo_42",
"email": "",
"is_reseller": false,
"is_marketer": false,
"is_superuser": false,
"date_joined": "2026-09-16T12:00:00Z",
"date_joined_ts": 1789560000,
"first_name": "",
"last_name": "",
"balance": 0,
"language": "en",
"country": "",
"state": "",
"city": "",
"address": "",
"zip": "",
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_country": "",
"company_vat_number": "",
"allowed_ips": [],
"blocked_domains": [],
"connection_limit": 100,
"parent_id": "550e8400-e29b-41d4-a716-446655440010",
"sub_users": 0,
"referrals": 0,
"referral_id": "",
"referral_code": "example42",
"referral_data_earned": 0,
"referral_data_pending": 0,
"referral_balance_pending": 0,
"referral_balance_earned": 0,
"currency": {
"code": "USD",
"symbol": "$"
},
"coupons": [],
"orders": []
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

Save the returned id as {user_id} in your customer mapping immediately. The response contains the new account, not proof of a funded order.

Only do this after your backend has verified the external payment or approved a deliberate test grant. POST /invoices with gateway: manual does not charge your external provider. It records a purchase and provisions the corresponding entitlement. The package still determines the platform price; this request is not an arbitrary price override.

RequestPOST /invoices
curl --request POST 'https://{api_host}/api/v1/invoices' \
--header 'Authorization: Static {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"user_id": "{user_id}",
"package_id": "{package_id}",
"data": 10737418240,
"gateway": "manual",
"status": "paid"
}'
Response201 Created

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440004",
"user_id": "550e8400-e29b-41d4-a716-446655440001",
"status": "paid",
"data": 10737418240,
"price_total": 2500,
"payment_url": "",
"currency": "USD"
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"country": null,
"user_id": "550e8400-e29b-41d4-a716-446655440001",
"coupon": null,
"payment_amount": 2500,
"payment_currency": "USD",
"fx_market_rate": "1",
"fx_effective_rate": "1",
"fx_markup_percent": "0",
"fx_quoted_at": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z",
"type": "residential",
"is_one_time": false,
"is_payout": false,
"internal_id": "EXAMPLE-00042",
"status": "paid",
"description": "",
"connection_limit": 100,
"quantity": 0,
"data": 10737418240,
"balance": 0,
"price_total": 2500,
"gateway": "manual",
"payment_url": "",
"currency": "USD",
"provider_checkout_id": "",
"provider_payment_id": "",
"checkout_status": "not_required",
"vat": 0,
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_registration_number": "",
"company_vat_number": "",
"paid": "2026-09-16T12:00:00Z"
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

Expect 201 with status: "paid" and data: 10737418240. Save the returned id as {invoice_id} against your purchase. A non-superuser sending paid receives 403; staff access or reseller status alone is insufficient. Other callers can use the default pending purchase flow.

To buy for the service account itself, omit user_id rather than sending its own ID. To give an existing managed sub-user independent data, the target must not already have a virtual order for this package. Read the two accounting models before mixing workflows.

3. Verify the paid invoice and resulting order

Section titled “3. Verify the paid invoice and resulting order”
RequestGET /invoices/{invoice_id}
curl 'https://{api_host}/api/v1/invoices/{invoice_id}' \
--header 'Authorization: Static {api_key}'
Response200 OK

Selected fields

{
"id": "550e8400-e29b-41d4-a716-446655440004",
"user_id": "550e8400-e29b-41d4-a716-446655440001",
"status": "paid",
"data": 10737418240,
"price_total": 2500,
"payment_url": "",
"currency": "USD"
}
Show full responseShow selected fields
{
"id": "550e8400-e29b-41d4-a716-446655440004",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"country": null,
"user_id": "550e8400-e29b-41d4-a716-446655440001",
"coupon": null,
"payment_amount": 2500,
"payment_currency": "USD",
"fx_market_rate": "1",
"fx_effective_rate": "1",
"fx_markup_percent": "0",
"fx_quoted_at": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z",
"type": "residential",
"is_one_time": false,
"is_payout": false,
"internal_id": "EXAMPLE-00042",
"status": "paid",
"description": "",
"connection_limit": 100,
"quantity": 0,
"data": 10737418240,
"balance": 0,
"price_total": 2500,
"gateway": "manual",
"payment_url": "",
"currency": "USD",
"provider_checkout_id": "",
"provider_payment_id": "",
"checkout_status": "not_required",
"vat": 0,
"company_name": "",
"company_address": "",
"company_city": "",
"company_postal_code": "",
"company_registration_number": "",
"company_vat_number": "",
"paid": "2026-09-16T12:00:00Z"
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

RequestGET /users/{user_id}/orders
curl 'https://{api_host}/api/v1/users/{user_id}/orders' \
--header 'Authorization: Static {api_key}'
Response200 OK

Selected fields

{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002"
},
"data": 10737418240,
"data_remaining": 10737418240,
"data_spent": 0,
"ledgers": [
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"data": 10737418240,
"data_remaining": 10737418240,
"expires": "2026-10-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
]
}
]
}
Show full responseShow selected fields
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"is_auto_renewal": false,
"auto_renewal_percentage": 0,
"auto_renewal_data": 0,
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"proxy_password": "example-proxy-password",
"proxy_password_reset": null,
"pools": [],
"data": 10737418240,
"data_remaining": 10737418240,
"data_spent": 0,
"ledgers": [
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"data": 10737418240,
"data_remaining": 10737418240,
"expires": "2026-10-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
],
"latest_data_top_up": 10737418240,
"latest_data_top_up_date": "2026-09-16T12:00:00Z",
"data_updated": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
]
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

For a fresh finite 10 GiB purchase, expect a matching order, 10,737,418,240 remaining bytes and a purchased ledger. Follow next if the order is not on the first page. Persist the matching order_id.

Fulfillment normally happens during payment processing, but recovery can be asynchronous. If the invoice is paid and the order is not ready, poll these reads with bounded backoff; after your application deadline, mark the purchase reconcile_required and investigate. Do not create another paid invoice to repair an uncertain result. There is no invoice.paid accounting webhook to wait for.

4. Generate credentials and make a request

Section titled “4. Generate credentials and make a request”
RequestPOST /proxies/generate
curl --request POST 'https://{api_host}/api/v1/proxies/generate' \
--header 'Authorization: Static {api_key}' \
--header 'Content-Type: application/json' \
--data '{
"user_id": "{user_id}",
"package_id": "{package_id}",
"quantity": 1,
"connection": {
"protocol": "http"
}
}'
Response201 Created
{
"count": 1,
"proxies": [
{
"host": "proxy.example.test",
"port": 8000,
"protocol": "http",
"username": "package-elite",
"password": "example-proxy-password",
"connection_string": "http://package-elite:[email protected]:8000"
}
]
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

RequestGET https://api.ipify.org?format=json
curl 'https://api.ipify.org?format=json' \
--proxy '{proxy_url}'
Response200 OK
{
"ip": "203.0.113.42"
}

Replace placeholders in braces with your values. Responses use synthetic example data.

Generation returns 201 and a proxies array containing host, port, username, password and connection string. The final request should return an exit IP. Use the returned connection_string as {proxy_url}. Keep it and the generated credentials out of logs: they contain a reusable secret. Generation alone is not a connectivity test.

The generator accepts user_id only for the caller’s own sub-user, including when the caller is a superuser. That is why this quickstart uses managed identity with independently purchased data. If you instead create a top-level customer with is_top_level: true, generate using that customer’s own authenticated context and omit user_id; the global service key cannot bypass this generator ownership check.

Start without optional targeting, then use catalog and proxy generation to add country or continent selection. A session TTL is in seconds; omit session for rotating eligibility.

RequestGET /users/{user_id}/orders
curl 'https://{api_host}/api/v1/users/{user_id}/orders' \
--header 'Authorization: Static {api_key}'
Response200 OK

Selected fields

{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002"
},
"data": 10737418240,
"data_remaining": 10737417216,
"data_spent": 1024,
"ledgers": [
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"data": 10737418240,
"data_remaining": 10737417216,
"expires": "2026-10-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
]
}
]
}
Show full responseShow selected fields
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "550e8400-e29b-41d4-a716-446655440003",
"is_auto_renewal": false,
"auto_renewal_percentage": 0,
"auto_renewal_data": 0,
"package": {
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "Residential example",
"alias": "elite",
"is_unlimited_data": false,
"targeting_options": {
"package": "package",
"split_char": "-",
"value_char": "-",
"continent": "continent",
"country": "country",
"region": "region",
"city": "city",
"asn": "asn",
"isp": "isp",
"username": "username",
"pool": "pool",
"location": "location",
"location_format": "",
"session": "sid",
"session_mode_tag": "",
"session_ttl": "ttl",
"session_ttl_format": 1,
"os": "os",
"os_combined": false,
"os_split_char": "-",
"os_linux": "linux",
"os_windows": "windows",
"os_ios": "ios",
"os_macos": "macos",
"os_android": "android"
}
},
"proxy_password": "example-proxy-password",
"proxy_password_reset": null,
"pools": [],
"data": 10737418240,
"data_remaining": 10737417216,
"data_spent": 1024,
"ledgers": [
{
"id": "550e8400-e29b-41d4-a716-446655440005",
"data": 10737418240,
"data_remaining": 10737417216,
"expires": "2026-10-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
],
"latest_data_top_up": 10737418240,
"latest_data_top_up_date": "2026-09-16T12:00:00Z",
"data_updated": "2026-09-16T12:00:00Z",
"updated": "2026-09-16T12:00:00Z",
"created": "2026-09-16T12:00:00Z"
}
]
}

Replace placeholders in braces with your values. Responses use synthetic example data.Install SDKsAPI reference →

This example assumes 1,024 bytes were counted after the test. Your actual byte count will differ.

Read the customer’s order again after accounting batches arrive. A controlled request should increase its usage and reduce usable data. For exact reporting windows and customer filters, use analytics. Signed webhooks keep your UI current but are not your payment journal.

Local recordPersisted ProxyRequest mapping
Customeruser_id, local customer reference and synchronization state
Productpackage_id, configured pricing and byte-unit convention
Customer/product accessorder_id; reused by later purchases of the same package
Each purchaseinvoice_id, exact request and verified outcome

Several local purchases or subscriptions may point to one user/package order. Keep their commercial histories separately. Optional user meta can hold non-sensitive correlation values; it does not replace this mapping or API IDs.

The official SDK handles short transient failures during a running call. If the call still fails or your process restarts, do not blindly repeat a paid invoice: inspect the visible invoice and order first. See retries and recovery and keep local pending, active, reconcile_required and deprovisioning states.

Subscription renewal and cancellation remain your business logic. Another paid purchase tops up data but does not reset all usage counters. A refund in your payment provider does not automatically reverse a ProxyRequest grant; invoice deletion is not a refund API. Revoke appropriate access under your policy and retain mappings for reconciliation. Never apply shared-quota subtraction to an independently purchased order.