Spyderproxy
https://spyderproxy.com/services/v1/reseller Reseller Reference

Reseller API Reference

The Spyderproxy Reseller API lets approved partners resell our full proxy catalog under their own accounts — manage sub-users, buy and provision residential, mobile, and datacenter proxies, allocate bandwidth, and generate ready-to-use connection strings. Every call is authenticated with your reseller key and billed to your reseller wallet.

Contents — jump to an endpoint

The Spyderproxy Reseller API lets approved partners resell the full Spyderproxy proxy catalog under their own accounts. With it you can inspect your reseller wallet and product catalog at bulk pricing, create and manage sub-users (your own end-customers), purchase and provision proxies across every network we operate — residential, budget residential, mobile/LTE, rotating mobile, datacenter, and rotating datacenter — allocate and reclaim residential bandwidth, generate ready-to-use proxy connection strings, and extend or renew time-based orders. Every purchase draws from your reseller wallet balance.


Getting started

Base URL

https://spyderproxy.com/services/v1/reseller

Authentication

Every request must include your reseller API key in the Authorization header:

Authorization: Token <YOUR_RESELLER_API_KEY>

The key identifies an active reseller account. Requests with a missing, invalid, or inactive token are rejected with 401 before the handler runs. Keep this key secret — treat it like a password. Never embed it in client-side code, browser apps, mobile apps, or anything a customer could inspect; all calls should be made from your own backend.

Your first request

Fetch your reseller profile — wallet balance, account id, and residential bandwidth still available in your own pool:

curl -H "Authorization: Token <RESELLER_API_KEY>" \
  https://spyderproxy.com/services/v1/reseller/account/
{
  "balance": 152.75,
  "id": 4021,
  "available_traffic": 87.5
}
  • balance — your reseller wallet balance in account currency (USD). All purchases are debited from this.
  • available_traffic — residential bandwidth (in GB) still available in your own residential sub-user pool. Degrades gracefully to 0.0 if you have no linked residential sub-user or the provider is unreachable.

Conventions

  • Authentication — All endpoints require the Authorization: Token <RESELLER_API_KEY> header. There are no unauthenticated endpoints.
  • Content type — Send Content-Type: application/json and a JSON body on all POST requests.
  • Units — All bandwidth quantities at the API surface are in GB. Internal conversions differ per provider (our residential network uses bytes = GB × 1024³; the budget residential network uses MB = GB × 1024), but you always send and receive GB.
  • Money — Reseller purchases draw from your wallet balance. Pricing uses your reseller-specific ResellerPlan price when one is configured, otherwise the plan's bulk_price (the reseller/bulk unit price, not the retail price).
  • Errors — Errors return an HTTP status with a JSON body carrying either a detail or a message field, e.g. 400 {"detail": "..."} or 402 {"message": "Insufficient Balance."}. A bad/missing/inactive token returns 401. See the Errors section for the full list.
  • Pagination — List endpoints that are paginated use django-ninja's default LimitOffset scheme: query params limit (int, default 100) and offset (int, default 0), and a response envelope of the form {"items": [...], "count": <int>}.
  • Async provisioning — Some orders return status: "In Progress" (or "Pending") while the upstream provider provisions them. Poll GET /reseller/orders/{id}/ until the status settles to "Completed" and the proxy list is populated.

Account & Products

Mounted at /reseller/.

GET/reseller/account/

Get the authenticated reseller's profile: wallet balance, account id, and residential bandwidth still available in the reseller's own pool.

Request — No path params, query params, or body. Identity comes entirely from the Authorization token.

Response — JSON object:

Field Type Description
balance number The reseller user's wallet balance (USD).
id int The authenticated reseller account id.
available_traffic float Residential traffic still available (GB) in the reseller's own sub-user pool.

available_traffic is resolved live from the residential provider (our residential network). If you have no linked residential sub-user, the sub-user no longer exists at the provider, or the provider call fails, it degrades to 0.0 rather than erroring.

curl -H "Authorization: Token <RESELLER_API_KEY>" \
  https://spyderproxy.com/services/v1/reseller/account/
{
  "balance": 152.75,
  "id": 4021,
  "available_traffic": 87.5
}

GET/reseller/products/

List all sellable products with their reseller-priced plans (the top-up product is excluded).

Request — No path params, query params, or body.

Response — Array of products:

[{ id, name, active, plans: [ { id, name, active, price } ] }]

Each plan's price is the reseller/bulk unit price (Plan.bulk_price, or your ResellerPlan override), not the retail price. For bandwidth products the plan represents GB; for IP-based products it represents IP count (read the plan name). The returned product id is the DB Product.id — that is the id you pass to /products/countries/{id}/ and use as product in order-create bodies.

curl -H "Authorization: Token <RESELLER_API_KEY>" \
  https://spyderproxy.com/services/v1/reseller/products/
[
  {
    "id": 12,
    "name": "Residential Proxies",
    "active": true,
    "plans": [
      { "id": 44, "name": "1 GB", "active": true, "price": 1.20 },
      { "id": 45, "name": "5 GB", "active": true, "price": 5.50 }
    ]
  },
  {
    "id": 18,
    "name": "Rotating Datacenter",
    "active": true,
    "plans": [
      { "id": 61, "name": "100 IPs", "active": true, "price": 25.00 }
    ]
  }
]

GET/reseller/products/countries/{id}/

List the available locations/countries for a product, as reported by the upstream proxy provider.

Request

Param In Type Description
id path int The DB Product.id (from /products/), not the provider ref_id.

Response — Array of provider-defined location objects (the locations array from the matching provider product entry). Returns [] if the product exists but no provider product matches its ref_id. If the Product.id does not exist, the API returns 404.

The exact location object shape is provider-defined (residential is our residential network); the example below is representative, not guaranteed field-for-field.

curl -H "Authorization: Token <RESELLER_API_KEY>" \
  https://spyderproxy.com/services/v1/reseller/products/countries/12/
[
  { "id": "US", "name": "United States" },
  { "id": "GB", "name": "United Kingdom" },
  { "id": "DE", "name": "Germany" }
]

GET/reseller/products/lte/available/

List currently available LTE (mobile 1-by-1) proxies for a given country.

Request

Param In Type Required Description
country query str Yes ISO country code, e.g. US.

Omitting country causes a 422 validation error.

Response — On provider status ok, the provider proxies array (provider-defined objects; each includes at least country_code). On any non-ok status, an empty object {} (not an array).

LTE/mobile stock comes from a separate provider (not our residential network); the upstream key is injected server-side and never exposed. The proxy object shape is provider-defined; example fields are representative.

curl -H "Authorization: Token <RESELLER_API_KEY>" \
  "https://spyderproxy.com/services/v1/reseller/products/lte/available/?country=US"
[
  { "country_code": "US", "city": "New York", "carrier": "AT&T", "available": true },
  { "country_code": "US", "city": "Dallas", "carrier": "T-Mobile", "available": true }
]

GET/reseller/products/lte/countries/

List the country codes that currently have LTE proxies available (US first, then the unique non-US countries).

Request — No path params, query params, or body.

Response — Array of country-code strings, with "US" always inserted first followed by the de-duplicated set of non-US country codes that have stock. The order of the non-US codes is not deterministic (built from a set). Returns {} (empty object) if the provider status is not ok.

curl -H "Authorization: Token <RESELLER_API_KEY>" \
  https://spyderproxy.com/services/v1/reseller/products/lte/countries/
["US", "CA", "GB", "DE", "FR"]

Orders

Mounted at /reseller/orders/. Orders are always scoped to your reseller account — you only ever see and act on your own orders.

OrderSchema (returned by most order endpoints):

Field Type Description
id int Order id.
product str Product name.
plan str Plan name.
subuser int | null Assigned sub-user id.
location str | int | null Location.
quantity int Quantity ordered.
total_amount decimal Amount charged.
status str Human display, e.g. "Completed", "In Progress", "Pending", "Rejected".
data object Provider payload, including proxies.

GET/reseller/orders/

List the authenticated reseller's orders (paginated), optionally filtered to one sub-user.

Request

Param In Type Default Description
subuser query int — Restrict to orders whose subuser_id matches.
limit query int 100 Pagination page size.
offset query int 0 Pagination offset.

Response — Paginated envelope {"items": [OrderSchema...], "count": int}.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/orders/?subuser=42&limit=20&offset=0' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{
  "items": [
    {
      "id": 10231,
      "product": "Datacenter Proxies",
      "plan": "30 Days",
      "subuser": 42,
      "location": "US",
      "quantity": 5,
      "total_amount": 12.50,
      "status": "Completed",
      "data": {"location": "US", "proxies": [{"ip": "1.2.3.4", "port_http": 12323, "port_socks": 12324, "username": "u", "password": "p"}]}
    }
  ],
  "count": 1
}

POST/reseller/orders/lte/

Purchase a single LTE (mobile 1-by-1) proxy order.

Request — JSON body:

Field Type Required Description
plan int Yes The LTE offer/proxy_id from the LTE availability listing (stored as data.plan_id).
subuser int | null No Assign the order to one of your sub-users.

Quantity is hard-coded to 1. The wallet is charged the plan price (ResellerPlan override else Plan.bulk_price) × 1. The subuser, if given, must belong to you or you get 404 SUBUSER_404.

Response — OrderSchema. location is always null for LTE and quantity is always 1. data carries {"plan_id": <id>} and, once provisioned, a "proxies" string "host:port:user:pass". If the provider does not return the proxy immediately, the order can come back "In Progress" — poll GET /reseller/orders/{id}/.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/orders/lte/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"plan": 88123, "subuser": 42}'
{
  "id": 10240,
  "product": "LTE Mobile Proxy",
  "plan": "LTE",
  "subuser": 42,
  "location": null,
  "quantity": 1,
  "total_amount": 4.00,
  "status": "Completed",
  "data": {"plan_id": 88123, "proxies": "45.66.77.88:12000:user123:pass123"}
}

POST/reseller/orders/

Create/purchase a proxy order for a given plan (datacenter/ISP-style products).

Request — JSON body:

Field Type Required Description
product int Yes DB Product.id.
plan int Yes The DB Plan.id (not ref_id).
location int Yes A provider location id (e.g. from GET /reseller/products/countries/{id}/).
quantity int Yes Quantity to order.
subuser int | null No Assign to a sub-user.
port str No "socks5" or "http\|https". Accepted by the schema but NOT used by this handler — it has no effect.

The plan is looked up by DB id and the product is derived from it. Product-specific rules (e.g. minimum quantity) are enforced by validate_order(). The wallet is charged reseller price × quantity; a gateway/out-of-stock failure during processing rejects the order and refunds the wallet.

Rejected product types: - TOPUP → 400 "Can't order this product." - MOBILE → 400 "Use LTE Purchase endpoint." (use POST /reseller/orders/lte/) - RESIDENTIAL → 401 OUT_OF_STOCK (residential bandwidth is topped up through the dedicated residential / budget-residential / exresidential modules, not here).

Response — OrderSchema. data = {"location": <str>, "proxies": <list|null>}; proxies is populated when the provider confirms, otherwise null while status is "In Progress". Poll GET /reseller/orders/{id}/.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/orders/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"product": 3, "plan": 17, "location": 12, "quantity": 5, "subuser": 42, "port": "http|https"}'
{
  "id": 10251,
  "product": "Datacenter Proxies",
  "plan": "30 Days",
  "subuser": 42,
  "location": "12",
  "quantity": 5,
  "total_amount": 12.50,
  "status": "In Progress",
  "data": {"location": "US", "proxies": null}
}

GET/reseller/orders/{id}/

Fetch one order's details; refreshes the proxy list from the provider if the order is still provisioning.

Request

Param In Type Description
id path int The ResellerOrder id.

Only returns orders you own; otherwise 404 ORDER_404. If the status is IN_PROGRESS it calls the provider live: for MOBILE it re-pulls the latest LTE proxy; for other products it reads the provider order and, once confirmed, fills data.proxies (http port 12323, socks port 12324) and flips the status to "Completed". This is the polling endpoint after an order comes back "In Progress".

Response — OrderSchema for the order.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/orders/10251/' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{
  "id": 10251,
  "product": "Datacenter Proxies",
  "plan": "30 Days",
  "subuser": 42,
  "location": "US",
  "quantity": 5,
  "total_amount": 12.50,
  "status": "Completed",
  "data": {"location": "US", "proxies": [{"ip": "1.2.3.4", "port_http": 12323, "port_socks": 12324, "username": "u", "password": "p"}]}
}

POST/reseller/orders/{id}/extend/

Extend/renew an existing completed, time-based order for another plan period.

Request

Param In Type Default Description
id path int — The order id.
plan_id body int | null null Omit (or send null) to renew on the order's existing plan; supply it to renew onto a different duration of the same product. If given, it must belong to the same product and be active.

The JSON body is optional.

Preconditions (each a 400 GENERIC_ERROR unless noted): - Order must be COMPLETED — "Only completed orders can be extended." - Order must have a provider ref_id — "...cannot be extended." - Product must be time-extendable — not one of TOPUP, RESIDENTIAL, PREMIUM_RESIDENTIAL, BUDGET_RESIDENTIAL, MOBILE, ROTATING_MOBILE, ROTATING_DATACENTER (bandwidth-pool products — buy more GB instead).

Pricing mirrors order creation: ResellerPlan override else Plan.bulk_price, × order.quantity; 402 INSUFFICIENT_BALANCE if the wallet is too low. The wallet is debited before the provider extend call; on any provider exception the wallet is refunded and 400 GATEWAY_FAIL is raised (the order is left untouched). On success, expired_at = max(current expiry, now) + plan.duration and the renewal is appended to data.extends[].

Response — OrderSchema for the extended order.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/orders/10251/extend/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"plan_id": 18}'
{
  "id": 10251,
  "product": "Datacenter Proxies",
  "plan": "30 Days",
  "subuser": 42,
  "location": "US",
  "quantity": 5,
  "total_amount": 12.50,
  "status": "Completed",
  "data": {"location": "US", "proxies": [{"ip": "1.2.3.4", "port_http": 12323, "port_socks": 12324, "username": "u", "password": "p"}], "extends": [{"date": "2026-08-21T10:00:00+00:00", "price": "12.50", "plan": "30 Days", "plan_id": 18}]}
}

Residential

Mounted at /reseller/residential/. This is the current residential network (host resi.spyderproxy.com). Bandwidth quantities are in GB.

POST/reseller/residential/{id}/traffic/give/

Allocate residential bandwidth (GB) to a sub-user from your residential pool, charging your wallet. Auto-creates the residential sub-user on first top-up.

Request

Param In Type Description
id path int Your own Subuser.id.
quantity body int Amount of residential bandwidth to add, in GB.

Internally converted to bytes as quantity × 1024³. Price = quantity × (ResellerPlan price if set, else plan.bulk_price) for the residential product's first plan; the wallet is checked and debited inside a DB transaction and a ResellerOrder (expiring in 90 days) is recorded COMPLETED (or REJECTED on failure). If the sub-user has no residential account yet, one is created (a proxy_type residential identity) and the returned credentials are saved back onto the sub-user. No explicit minimum quantity beyond the integer type.

Response — 200 {"success": true}.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/residential/42/traffic/give/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"quantity": 5}'
{"success": true}

POST/reseller/residential/{id}/traffic/take/

Reclaim residential bandwidth (GB) from a sub-user and credit it back to your wallet.

Request

Param In Type Description
id path int Your Subuser.id.
quantity body int Amount of residential bandwidth to pull back, in GB.

Gotcha: unlike /traffic/give/ (which uses our residential network), the take path reads availability and performs the reclaim through the legacy residential gateway (get_subuser for traffic_available, then take_traffic). Available bandwidth must be >= quantity or you get 403 INSUFFICIENT_BANDWIDTH. The wallet is credited quantity × (ResellerPlan price if set, else plan.bulk_price) inside a DB transaction. No ResellerOrder row is created for a take.

Errors — 404 SUBUSER_404; 400 SUBUSER_400 "This subuser cant be used for this request" when the sub-user has no provider account (no sub_id); 403 INSUFFICIENT_BANDWIDTH "Insufficient Residential Bandwidth.".

Response — 200 {"success": true}.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/residential/42/traffic/take/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"quantity": 2}'
{"success": true}

POST/reseller/residential/generate/

Generate ready-to-use residential proxy connection strings for a sub-user. residential sub-users are built client-side; legacy sub-users go through the old residential gateway.

Request — JSON body (GenerateSchema):

Field Type Required Description
format str Yes One of the four literal templates: "{hostname}:{port}:{username}:{password}", "{hostname}:{port}@{username}:{password}", "{username}:{password}:{hostname}:{port}", "{username}:{password}@{hostname}:{port}".
port str Yes "socks5" or "http\|https".
subuser int Yes Your Subuser.id.
country str No Country target (default null).
location str No Legacy location string (ignored for our residential network).
region str No Region/state target, e.g. california (our residential network; from /regions/).
city str No City target, e.g. los+angeles (our residential network; from /cities/).
isp str No ISP target, e.g. comcast (our residential network; from /isps/).
lifetime str No Default null.
rotation str Yes "random" or "sticky".
quantity int Yes Number of proxy lines to generate.

Routing is by subuser.provider: - residential sub-users — proxies are built locally from the sub-user's stored credentials, no upstream call. Host/port are fixed to resi.spyderproxy.com:5000. rotation is normalized to "random" or "sticky" (anything not exactly "random" ⇒ sticky). Sticky lines append a per-line session id (-sid-<12 hex>) so each line is a distinct sticky session; random lines omit it (rotating IP per request). Geo targeting is encoded into the username: country (a 2-letter code is upper-cased, e.g. -country-US; a country name is resolved to its code), plus optional region, city, and isp — each appended as -region-…, -city-…, -isp-… with values lowercased and spaces turned into + (exactly the form returned by the lookup endpoints below). The location and lifetime fields are ignored on this route; format selects the output template. Discover valid values by cascading the lookups: country → region → city/ISP. - Legacy (non-our residential network, non-the premium residential network) sub-users — location = location or country and must contain an underscore (e.g. country_region) or you get 400 GENERIC_ERROR "Please format the location field properly". Proxies are fetched via the legacy residential gateway and the host the legacy proxy host is rewritten to the configured proxy address. - premium residential sub-users — rejected with 400 SUBUSER_400.

Response — 200: JSON array of proxy strings (List[str]) in the chosen format. Errors: 404 SUBUSER_404; 400 SUBUSER_400 (the premium residential network); 400 GENERIC_ERROR (legacy location formatting).

curl -X POST 'https://spyderproxy.com/services/v1/reseller/residential/generate/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "format": "{username}:{password}@{hostname}:{port}",
    "port": "http|https",
    "subuser": 42,
    "country": "US",
    "region": "california",
    "city": "los+angeles",
    "isp": "comcast",
    "rotation": "sticky",
    "quantity": 3
  }'
[
  "resiUser-country-US-region-california-city-los+angeles-isp-comcast-sid-a1b2c3d4e5f6:[email protected]:5000",
  "resiUser-country-US-region-california-city-los+angeles-isp-comcast-sid-f6e5d4c3b2a1:[email protected]:5000"
]

GET/reseller/residential/countries/

List the residential target countries available on our residential network.

Request — No path or query params, no body.

Response — 200: verbatim passthrough of our residential network /countries?connection_type=residential response. Use the returned country_code values as the country field on /reseller/residential/generate/ (the 2-letter code is emitted verbatim, upper-cased, into the proxy username). The exact JSON shape is whatever our residential network returns; the example below is representative. For finer targeting, use the /regions/, /cities/, and /isps/ endpoints below to discover valid region, city, and isp values to pass to /generate/.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/residential/countries/' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{
  "data": [
    {"country_code": "US", "country": "United States"},
    {"country_code": "GB", "country": "United Kingdom"},
    {"country_code": "DE", "country": "Germany"}
  ]
}

GET/reseller/residential/regions/

List our residential network regions/states available for a country (residential targeting).

Request

Param In Type Required Description
country query str Yes 2-letter country code (or country name) — resolved to a our residential network country code.

Response — 200: {"regions": ["alabama", "california", "new+jersey", …]}. Pass a value verbatim as the region field on /generate/.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/residential/regions/?country=US' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{"regions": ["alabama", "alaska", "arizona", "arkansas", "california", "colorado", "florida"]}

GET/reseller/residential/cities/

List our residential network cities for a country, optionally narrowed by region.

Request

Param In Type Required Description
country query str Yes Country code or name.
region query str No Region/state to narrow the list, e.g. california.

Response — 200: {"cities": ["acton", "los+angeles", "san+diego", …]}. Pass a value verbatim as the city field on /generate/.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/residential/cities/?country=US&region=california' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{"cities": ["acton", "alameda", "alhambra", "los+angeles", "san+diego", "san+francisco"]}

GET/reseller/residential/isps/

List our residential network ISPs for a country, optionally narrowed by region and/or city.

Request

Param In Type Required Description
country query str Yes Country code or name.
region query str No Region/state to narrow the list.
city query str No City to narrow the list.

Response — 200: {"isps": ["at&t+internet", "comcast", "verizon", …]}. Pass a value verbatim as the isp field on /generate/.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/residential/isps/?country=US&region=california' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{"isps": ["at&t+internet", "astound+broadband", "comcast", "spectrum", "verizon"]}

Budget Residential

Mounted at /reseller/budget-residential/. Provider: the budget residential network. Router-level auth applies to every endpoint. Bandwidth quantities are in GB at the API surface (converted to MB internally, since the budget residential network uses MB while our residential network-backed residential uses bytes).

POST/reseller/budget-residential/{id}/traffic/give/

Add bandwidth to an existing the budget residential network budget-residential sub-user, charging your wallet.

Request

Param In Type Description
id path int The Subuser.id (must belong to you; 404 SUBUSER_404 otherwise).
quantity body int Bandwidth to add, in GB (converted to MB as quantity × 1024 and added to the sub-user's the budget residential network traffic_limit).

The charge + top-up run in a DB transaction with the reseller's user row locked. Price = ResellerPlan.price if set, else plan.bulk_price, × quantity. A ResellerOrder is created (budget product/first plan, expired_at = now + 730 days — a 2-year expiry): COMPLETED on success (wallet debited), REJECTED on failure (error re-raised). 402 INSUFFICIENT_BALANCE if the wallet is too low.

Response — 200 {"success": true}.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/budget-residential/812/traffic/give/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"quantity": 10}'
{
  "success": true
}

POST/reseller/budget-residential/generate/

Generate rotating/sticky the budget residential network budget-residential proxy connection strings for a sub-user.

Request — JSON body (GenerateSchema):

Field Type Required Description
format str Yes One of the four literal templates (see Residential /generate/).
port str Yes "socks5" or "http\|https".
subuser int Yes The Subuser.id — must be a the budget residential network/budget-provider sub-user owned by you.
country str No Geo target (default null).
location str No Geo target (default null).
lifetime str No Sticky-session lifetime (default null).
rotation str Yes "random" or "sticky".
quantity int Yes Number of proxy lines to generate.

This endpoint only formats existing credentials into proxy lines — it does not add bandwidth or charge the wallet (use /traffic/give/ to top up). If the sub-user is not a the budget residential network/budget sub-user owned by you, it raises 404 SUBUSER_404.

Response — 200: a list of proxy connection strings, one per requested quantity, each in the chosen format. The exact strings are produced by the the budget residential network gateway; the example values below are illustrative.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/budget-residential/generate/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "format": "{hostname}:{port}:{username}:{password}",
    "port": "http|https",
    "subuser": 812,
    "country": "US",
    "rotation": "sticky",
    "quantity": 3
  }'
[
  "geo.spyderproxy.com:9000:budgetuser812:pass_abc123",
  "geo.spyderproxy.com:9001:budgetuser812:pass_abc123",
  "geo.spyderproxy.com:9002:budgetuser812:pass_abc123"
]

GET/reseller/budget-residential/countries/

List the countries/locations available for the budget residential network budget-residential proxies.

Request — No path params, query params, or body.

Response — 200: read-only passthrough of the the budget residential network locations list, used for the country/location fields of /generate/. The exact object shape is produced by the the budget residential network gateway; fields shown are illustrative.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/budget-residential/countries/' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
[
  {"code": "US", "name": "United States"},
  {"code": "GB", "name": "United Kingdom"},
  {"code": "DE", "name": "Germany"}
]

Legacy Residential

Mounted at /reseller/old-residential/. This is the deprecated legacy residential module. Every route still requires a valid, active reseller token (401 otherwise).

Gotcha: despite the module name (\"old residential / legacy the legacy residential network\"), the generate and countries handlers use the the premium residential network gateway, not the legacy residential network.

POST/reseller/old-residential/{id}/traffic/give/

Allocate/top-up residential traffic (GB) to an existing reseller order — DISABLED, always returns 400.

Request

Param In Type Description
id path int The ResellerOrder id (ignored).
quantity body int Traffic in GB (ignored).

The handler unconditionally raises 400 before any of the body/path is used, so the success path is unreachable and old-residential traffic top-ups can no longer be purchased.

Response — Always HTTP 400 with {"detail": "Can't purchase old residential!"}.

curl -X POST "https://spyderproxy.com/services/v1/reseller/old-residential/123/traffic/give/" \
  -H "Authorization: Token <RESELLER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"quantity": 5}'
{
  "detail": "Can't purchase old residential!"
}

POST/reseller/old-residential/generate/

Generate residential proxy connection strings for one of your sub-users (via the the premium residential network gateway).

Request — JSON body (GenerateSchema):

Field Type Required Description
format str Yes One of the four literal templates (see Residential /generate/).
port str Yes "socks5" or "http\|https".
subuser int Yes Subuser.id, must belong to you.
country str No Default null.
location str No Default null.
lifetime str No Default null.
rotation str Yes "random" or "sticky".
quantity int Yes Number of proxy lines to generate.

Uses the sub-user's own username/password. If no matching sub-user exists for you, raises 404 SUBUSER_404. This is a generation-only endpoint — it does not consume or charge bandwidth.

Response — Passthrough of the the premium residential network gateway's raw output: a list of proxy connection strings in the requested format. Element shape is provider-defined.

curl -X POST "https://spyderproxy.com/services/v1/reseller/old-residential/generate/" \
  -H "Authorization: Token <RESELLER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "{hostname}:{port}:{username}:{password}",
    "port": "http|https",
    "subuser": 42,
    "country": "us",
    "rotation": "sticky",
    "quantity": 3
  }'
[
  "gateway.example.net:8080:subuser42:s3cr3t",
  "gateway.example.net:8080:subuser42:s3cr3t",
  "gateway.example.net:8080:subuser42:s3cr3t"
]

GET/reseller/old-residential/countries/

List available residential proxy locations/countries (via the the premium residential network gateway).

Request — No path params, query params, or body.

Response — Passthrough of the the premium residential network gateway's raw locations list; the codes feed the country/location params of POST /generate/. Element shape is provider-defined.

curl -X GET "https://spyderproxy.com/services/v1/reseller/old-residential/countries/" \
  -H "Authorization: Token <RESELLER_API_KEY>"
[
  {"code": "us", "name": "United States"},
  {"code": "gb", "name": "United Kingdom"},
  {"code": "de", "name": "Germany"}
]

ExResidential

Mounted at /reseller/exresidential/. An extended residential tier. This module currently exposes a single route. Bandwidth is in GB at the API surface.

POST/reseller/exresidential/{id}/traffic/give/

Allocate (give) bandwidth to an existing ExResidential (our residential network) sub-user.

Request

Param In Type Description
id path int The target Subuser.id (must belong to you; it is a sub-user id, not an order id).
quantity body int Amount of bandwidth to allocate, in GB (whole GB).

The service resolves the sub-user scoped to your reseller account and raises SUBUSER_404 if it is not yours. Insufficient wallet balance raises INSUFFICIENT_BALANCE; an upstream our residential network rejection surfaces as a gateway failure ({"code": int, "message": str}).

Response — The raw result of the service call (untyped — no response schema is declared, so field names come from the service layer). The example fields below are illustrative.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/exresidential/512/traffic/give/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"quantity": 5}'
{
  "success": true,
  "subuser": 512,
  "quantity": 5
}

Subusers

Mounted at /reseller/subusers/. Sub-users are your end-customers. All queries are scoped to your reseller account — you only ever see your own sub-users.

GET/reseller/subusers/

List all sub-users belonging to the authenticated reseller (paginated).

Request

Param In Type Default Description
limit query int 100 Pagination page size.
offset query int 0 Pagination offset.

Response — Pagination envelope { items: [SubuserSchema], count: int }. SubuserSchema fields: id (int), username (str | null), password (str | null), created_at (datetime ISO-8601).

This endpoint does not return bandwidth/GB usage, and username/password may be null for placeholder residential sub-users created with gb=0. Use GET /reseller/subusers/{id}/ for live usage.

curl 'https://spyderproxy.com/services/v1/reseller/subusers/?limit=50&offset=0' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{
  "items": [
    {
      "id": 87,
      "username": "pe_user_x9f2",
      "password": "pe_pass_abcd12",
      "created_at": "2026-08-21T14:32:00Z"
    },
    {
      "id": 42,
      "username": null,
      "password": null,
      "created_at": "2026-08-10T09:15:00Z"
    }
  ],
  "count": 2
}

GET/reseller/subusers/{id}/

Retrieve one sub-user with live used/unused bandwidth (GB) fetched from its upstream provider.

Request

Param In Type Description
id path int The Subuser.id, must belong to you.

Response — SubuserDetails: id (int), username (str | null), password (str | null), created_at (datetime), unused_gb (float), used_gb (float). Returns 404 {"detail": "Subuser does not exist."} if not found or not owned by you.

used_gb/unused_gb are live-queried from the sub-user's provider gateway. If the sub-user has no upstream sub_id (e.g. a placeholder created with gb=0), both are 0 with no gateway call. Traffic is read live from whichever residential network the sub-user is provisioned on. A provider outage on the traffic lookup can surface as an error on this call.

curl 'https://spyderproxy.com/services/v1/reseller/subusers/87/' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{
  "id": 87,
  "username": "pe_user_x9f2",
  "password": "pe_pass_abcd12",
  "created_at": "2026-08-21T14:32:00Z",
  "unused_gb": 4.53,
  "used_gb": 0.47
}

POST/reseller/subusers/

Create a new sub-user. The type selects the provider.

Request — JSON body (SubuserCreateSchema):

Field Type Required Default Description
email str Yes — Required for all types; only meaningfully used for budget (the budget residential network) and to key placeholder/budget records. Residential auto-generates its own provider email.
gb int No 0 Meaning depends on type (see below).
type enum No "residential" One of "residential", "exresidential", "budget".

Behavior by type: - residential — if gb > 0, provisions bandwidth on our residential network (gb × 1024³ bytes) with an auto-generated email resi-<hex>@sub.spyderproxy.io, sets the residential provider, and charges the wallet. If gb = 0, creates a bare placeholder record (no provider call; provider defaults to the legacy residential network; username/password null). - exresidential — gb is treated as a quantity of premium residential units (must be > 0); provisions the extended residential units, creates a COMPLETED ResellerOrder with a 90-day expiry, and charges the wallet. - budget — gb is ignored; creates a the budget residential network (provider=budget) sub-user with 0 traffic using email.

Pricing uses your ResellerPlan price when set, otherwise plan.bulk_price.

Response — SubuserSchema: id, username, password, created_at. username/password carry the provider proxy credentials for provisioned sub-users; both are null for a residential placeholder (gb=0).

Errors — 400 GATEWAY_FAIL "Gateway failed to process your request, try later." if the provider returns no id/credentials; 402 INSUFFICIENT_BALANCE "Insufficient Balance." if the wallet cannot cover the charge (residential gb>0 and exresidential both pre-check and debit); 400 GENERIC_ERROR for exresidential when gb <= 0.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/subusers/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","gb":5,"type":"residential"}'
{
  "id": 88,
  "username": "pe_user_k3d9",
  "password": "pe_pass_ffa210",
  "created_at": "2026-08-21T15:04:11Z"
}

Rotating Mobile

Mounted at /reseller/rotating-mobile/. Provider: Fleet mobile gateway. Location/ISP catalog responses are returned verbatim from the provider; example shapes are illustrative.

GET/reseller/rotating-mobile/details/

Get a sub-user's rotating-mobile account credentials and traffic usage.

Request

Param In Type Required Description
subuser query int Yes The Subuser.id owned by you.

Response — { username, password, available_traffic, used_traffic }. Traffic values pass through unchanged from the Fleet gateway.

404 SUBUSER_404 if not found/owned; 400 SUBUSER_400 "This subuser has no active rotating mobile account." when sub_id is empty; 400 SUBUSER_400 "An error has occured, please try again later." when the gateway returns no data or omits username.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/rotating-mobile/details/?subuser=42' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
{
  "username": "mob_a1b2c3",
  "password": "s3cretPass",
  "available_traffic": 5.0,
  "used_traffic": 1.23
}

GET/reseller/rotating-mobile/countries/

List all countries available for rotating-mobile proxies.

Request — No path params, query params, or body.

Response — Passthrough of the Fleet gateway's mobile country list.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/rotating-mobile/countries/' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
[
  {"code": "US", "name": "United States"},
  {"code": "GB", "name": "United Kingdom"}
]

GET/reseller/rotating-mobile/regions/{country}/

List regions/states within a country for rotating-mobile targeting.

Request

Param In Type Required Description
country path str Yes Country code/identifier.

Response — Passthrough of the Fleet gateway's region list.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/rotating-mobile/regions/US/' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
[
  {"code": "CA", "name": "California"},
  {"code": "NY", "name": "New York"}
]

GET/reseller/rotating-mobile/cities/{country}/

List cities within a country (optionally scoped to a region) for rotating-mobile targeting.

Request

Param In Type Required Default Description
country path str Yes — Country code/identifier.
region query str No null Narrow to one region; omit to list cities across the whole country.

Response — Passthrough of the Fleet gateway's city list.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/rotating-mobile/cities/US/?region=CA' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
[
  {"name": "Los Angeles"},
  {"name": "San Francisco"}
]

GET/reseller/rotating-mobile/isps/{country}/

List mobile carriers/ISPs available in a country (optionally scoped to region and city).

Request

Param In Type Required Default Description
country path str Yes — Country code/identifier.
region query str No null Narrow the ISP list to a region.
city query str No null Narrow the ISP list to a city.

Response — Passthrough of the Fleet gateway's ISP/carrier list.

curl -X GET 'https://spyderproxy.com/services/v1/reseller/rotating-mobile/isps/US/?region=CA&city=Los%20Angeles' \
  -H 'Authorization: Token <RESELLER_API_KEY>'
[
  {"name": "AT&T"},
  {"name": "Verizon"},
  {"name": "T-Mobile"}
]

POST/reseller/rotating-mobile/generate/

Generate rotating-mobile proxy connection strings for a sub-user with geo/rotation targeting.

Request — JSON body (RotatingMobileGenerateSchema):

Field Type Required Default Description
subuser int Yes — Subuser.id.
count int No 10 Number of proxies to generate.
country_id str No null Forwarded to the generator's country argument.
region str No null Region target.
city str No null City target.
isp str No null ISP/carrier target.
ttl int No null Sticky-session time-to-live (used when rotation="sticky").
rotation enum No "random" "random" or "sticky".
proxy_format int No 0 Index selecting the output proxy string format.

Generation does not charge or allocate — it only builds proxy strings for an existing mobile sub-user (use /purchase/ to buy quantity). If the sub-user is not found/owned, raises 404 SUBUSER_404.

Response — { proxies: [...], subuser_details: {...} }. proxies is a list of generated proxy strings (layout determined by proxy_format); subuser_details is the Fleet gateway account info. Example is illustrative.

curl -X POST 'https://spyderproxy.com/services/v1/reseller/rotating-mobile/generate/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "subuser": 42,
    "count": 5,
    "country_id": "US",
    "region": "CA",
    "city": "Los Angeles",
    "isp": "AT&T",
    "ttl": 600,
    "rotation": "sticky",
    "proxy_format": 0
  }'
{
  "proxies": [
    "mob_a1b2c3:[email protected]:8000",
    "mob_a1b2c3:[email protected]:8001"
  ],
  "subuser_details": {
    "username": "mob_a1b2c3",
    "password": "s3cretPass",
    "available_traffic": 5.0,
    "used_traffic": 1.23
  }
}

POST/reseller/rotating-mobile/purchase/

Purchase rotating-mobile traffic/quantity, optionally assigned to a sub-user and with a coupon.

Request — JSON body (ResellerRotatingMobilePurchaseSchema):

Field Type Required Default Description
quantity int Yes — Amount to purchase.
subuser int No null Subuser.id to assign the purchase to.
coupon str No null Coupon code.
product int No — Present on the schema but NOT used by this route.
plan int No — Present on the schema but NOT used by this route.

Only quantity, subuser, and coupon are read. Charging debits your wallet via the order flow; insufficient balance raises INSUFFICIENT_BALANCE.

Response — The order/result object from the reseller-orders service (shape defined there; the example is illustrative).

curl -X POST 'https://spyderproxy.com/services/v1/reseller/rotating-mobile/purchase/' \
  -H 'Authorization: Token <RESELLER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -d '{
    "quantity": 2,
    "subuser": 42,
    "coupon": "SAVE10"
  }'
{
  "id": 1001,
  "subuser": 42,
  "quantity": 2,
  "total_amount": "18.00",
  "status": "completed"
}

Rotating Datacenter

Mounted at /reseller/rotating-datacenter/. Provider: Fleet gateway. Traffic figures are reported in the Fleet account's native units (GB).

GET/reseller/rotating-datacenter/details/

Get the rotating datacenter sub-user's Fleet credentials and traffic usage.

Request

Param In Type Required Description
subuser query int Yes The Subuser.id owned by you; must already have a provisioned datacenter account (non-empty sub_id).

Response — { username, password, available_traffic, used_traffic }, pulled live from the Fleet account.

Errors: 404 SUBUSER_404 "Subuser was not found"; 400 SUBUSER_400 "This subuser has no active rotating datacenter account." if there is no sub_id; 400 SUBUSER_400 "An error has occured, please try again later." if the Fleet lookup returns nothing / no username.

curl -X GET "https://spyderproxy.com/services/v1/reseller/rotating-datacenter/details/?subuser=118" \
  -H "Authorization: Token <RESELLER_API_KEY>"
{
  "username": "fleet_dc_84213",
  "password": "Xq9v2Lm7Ptz",
  "available_traffic": 42.5,
  "used_traffic": 7.5
}

GET/reseller/rotating-datacenter/countries/

List the datacenter countries available for proxy generation.

Request — No path params, query params, or body.

Response — JSON array of datacenter country entries from the Fleet gateway. Each entry carries a country identifier/code (usable as country_id in /generate/) and a display name. Field names are provider-defined; the example is illustrative.

curl -X GET "https://spyderproxy.com/services/v1/reseller/rotating-datacenter/countries/" \
  -H "Authorization: Token <RESELLER_API_KEY>"
[
  {"id": "us", "name": "United States"},
  {"id": "de", "name": "Germany"},
  {"id": "gb", "name": "United Kingdom"}
]

POST/reseller/rotating-datacenter/generate/

Generate rotating datacenter proxy connection strings for an existing sub-user and return its account details.

Request — JSON body (RotatingDatacenterGenerateSchema):

Field Type Required Default Description
subuser int Yes — Subuser.id owned by you.
count int No 10 Number of proxy strings to generate.
country_id str | null No null Country code from /countries/ (null = default/any).
rotation enum No "random" "random" or "sticky".
proxy_format int No 0 Index into the provider's format templates (0 = default).

This endpoint does not buy anything and does not consume balance — it only builds proxy strings for a sub-user that already has a datacenter account and echoes back its usage. Internally uses the sub-user's Fleet username/password (not the DB id). Error: 404 SUBUSER_404 "Subuser was not found" if the sub-user isn't owned by you.

Response — { proxies: [...], subuser_details: {...} }. proxies layout is determined by proxy_format (the example strings are illustrative); subuser_details matches GET /details/.

curl -X POST "https://spyderproxy.com/services/v1/reseller/rotating-datacenter/generate/" \
  -H "Authorization: Token <RESELLER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"subuser": 118, "count": 10, "country_id": "us", "rotation": "sticky", "proxy_format": 0}'
{
  "proxies": [
    "dc.spyderproxy.io:12323:fleet_dc_84213:Xq9v2Lm7Ptz",
    "dc.spyderproxy.io:12323:fleet_dc_84213:Xq9v2Lm7Ptz"
  ],
  "subuser_details": {
    "username": "fleet_dc_84213",
    "password": "Xq9v2Lm7Ptz",
    "available_traffic": 42.5,
    "used_traffic": 7.5
  }
}

POST/reseller/rotating-datacenter/buy/

Purchase or top up rotating datacenter traffic, provisioning a Fleet sub-user and charging your wallet.

Request — JSON body (ResellerRotatingDatacenterPurchaseSchema):

Field Type Required Default Description
product int Yes — Effectively ignored server-side — the rotating-datacenter product is always resolved internally.
plan int Yes — Plan id (resolved by id, then by ref_id) — this is what selects pricing.
quantity int Yes — Number of plan units to buy. Passed to the gateway as-is with no GB/byte conversion — it represents plan units.
subuser int | null No null Existing Subuser.id to top up; null creates a new sub-user.
coupon str | null No null Coupon code; unknown/invalid codes are silently ignored (no discount, no error).

Pricing = ResellerPlan.price if you have one for the plan, else Plan.bulk_price, × quantity, minus any valid coupon discount. If subuser is supplied and already has a Fleet sub_id, traffic is added to that account; otherwise a brand-new sub-user (provider = Fleet) plus a new Fleet datacenter account is created. The whole charge + provision runs in a DB transaction; on any failure the order is marked REJECTED and the charge is rolled back.

Errors — 400 GENERIC_ERROR "Plan not found!" (plan id/ref_id doesn't match the datacenter product); 400 GENERIC_ERROR "Can't purchase bandwidth right now!" (plan inactive); 402 INSUFFICIENT_BALANCE "Insufficient Balance." (wallet < total after discount); 400 GATEWAY_FAIL if Fleet doesn't return an account id.

Response — {"success": true} on completion. No credentials are returned here — retrieve them afterward via GET /details/ or POST /generate/.

curl -X POST "https://spyderproxy.com/services/v1/reseller/rotating-datacenter/buy/" \
  -H "Authorization: Token <RESELLER_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"product": 12, "plan": 34, "quantity": 5, "subuser": 118, "coupon": "SAVE10"}'
{
  "success": true
}

Errors

Errors are returned with an HTTP status and a JSON body carrying either a detail or a message field. Authentication failures short-circuit before the handler runs.

HTTP Code / condition Message Notes
401 Auth failure — Missing, invalid, or inactive reseller token. Applies to every endpoint.
401 OUT_OF_STOCK — Attempting to order a RESIDENTIAL product via POST /reseller/orders/ — top up via the residential/budget-residential/exresidential modules instead.
402 INSUFFICIENT_BALANCE Insufficient Balance. Wallet balance is lower than the total charge.
403 INSUFFICIENT_BANDWIDTH Insufficient Residential Bandwidth. On residential/{id}/traffic/take/ when quantity exceeds the sub-user's available traffic.
404 SUBUSER_404 Subuser was not found / Subuser does not exist. Sub-user id not found or not owned by the reseller.
404 ORDER_404 — Order id does not belong to this reseller.
404 Product not found — GET /products/countries/{id}/ when the Product.id does not exist.
400 SUBUSER_400 This subuser cant be used for this request / This subuser has no active rotating mobile account. / This subuser has no active rotating datacenter account. / An error has occured, please try again later. Sub-user has no provider account, no sub_id, is a the premium residential network sub-user on our residential network generate route, or the upstream returned no usable data.
400 GATEWAY_FAIL Gateway failed to process your request, try later. Upstream provider rejected the request or returned no id/credentials. On order extend, the wallet is refunded first.
400 GENERIC_ERROR e.g. Please format the location field properly / Only completed orders can be extended. / ...cannot be extended. / Plan not found! / Can't purchase bandwidth right now! Validation and precondition failures. Also covers exresidential sub-user creation with gb <= 0.
400 Order product rejected Can't order this product. (TOPUP) / Use LTE Purchase endpoint. (MOBILE) From POST /reseller/orders/.
400 Old residential disabled Can't purchase old residential! POST /reseller/old-residential/{id}/traffic/give/ always returns this.
422 Validation error — Missing required query params (e.g. country on /products/lte/available/) or malformed request bodies.

Provider-status-related non-errors: /products/lte/available/ and /products/lte/countries/ return an empty object {} (HTTP 200) when the upstream LTE provider status is not ok, rather than raising an error.

Spyderproxy Reseller API · Base URL https://spyderproxy.com/services/v1/reseller · Authenticate every request with Authorization: Token <YOUR_RESELLER_API_KEY>