AdaptGroupOpen dashboard
VPN infrastructure

VPN subscriptions inside your product.

Create and manage subscriptions from your website, application, Telegram bot or internal service while AdaptGroup operates the VPN infrastructure.

Base URL
https://network-api.adaptgroup.app
Authentication
X-Api-Key
Capabilities
Plans, subscriptions, balance, webhooks
Rate limit
100 requests / 60 seconds

Create a subscription

The plan price is deducted from the integration balance. Trial plans cannot be created through the API.

POST/subs/create
cURL
export ADAPTGROUP_API_KEY="YOUR_API_KEY"

curl --request POST \
  --url https://network-api.adaptgroup.app/subs/create \
  --header "Content-Type: application/json" \
  --header "X-Api-Key: $ADAPTGROUP_API_KEY" \
  --data '{
    "api_key_id": 1,
    "plan_uuid": "550e8400-e29b-41d4-a716-446655440000",
    "external_user_id": "user_123"
  }'
Python
import os
import requests

response = requests.post(
    "https://network-api.adaptgroup.app/subs/create",
    headers={
        "Content-Type": "application/json",
        "X-Api-Key": os.environ["ADAPTGROUP_API_KEY"],
    },
    json={
        "api_key_id": 1,
        "plan_uuid": "550e8400-e29b-41d4-a716-446655440000",
        "external_user_id": "user_123",
    },
    timeout=10,
)

response.raise_for_status()
subscription = response.json()
Node.js
const response = await fetch("https://network-api.adaptgroup.app/subs/create", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Api-Key": process.env.ADAPTGROUP_API_KEY,
  },
  body: JSON.stringify({
    api_key_id: 1,
    plan_uuid: "550e8400-e29b-41d4-a716-446655440000",
    external_user_id: "user_123",
  }),
});

if (!response.ok) {
  throw new Error(`VPN API request failed: ${response.status}`);
}

const subscription = await response.json();
PHP
<?php

$payload = json_encode([
    'api_key_id' => 1,
    'plan_uuid' => '550e8400-e29b-41d4-a716-446655440000',
    'external_user_id' => 'user_123',
]);

$request = curl_init('https://network-api.adaptgroup.app/subs/create');

curl_setopt_array($request, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-Api-Key: ' . getenv('ADAPTGROUP_API_KEY'),
    ],
    CURLOPT_POSTFIELDS => $payload,
]);

$response = curl_exec($request);

if ($response === false) {
    throw new RuntimeException(curl_error($request));
}

curl_close($request);
$subscription = json_decode($response, true, flags: JSON_THROW_ON_ERROR);
Go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "time"
)

func main() {
    payload, err := json.Marshal(map[string]any{
        "api_key_id":      1,
        "plan_uuid":       "550e8400-e29b-41d4-a716-446655440000",
        "external_user_id": "user_123",
    })
    if err != nil {
        panic(err)
    }

    req, err := http.NewRequest(
        http.MethodPost,
        "https://network-api.adaptgroup.app/subs/create",
        bytes.NewReader(payload),
    )
    if err != nil {
        panic(err)
    }

    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-Api-Key", os.Getenv("ADAPTGROUP_API_KEY"))

    client := &http.Client{Timeout: 10 * time.Second}
    response, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer response.Body.Close()

    if response.StatusCode != http.StatusOK {
        panic(fmt.Sprintf("VPN API request failed: %s", response.Status))
    }
}
Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("ADAPTGROUP_API_KEY");

        String payload = """
            {
              "api_key_id": 1,
              "plan_uuid": "550e8400-e29b-41d4-a716-446655440000",
              "external_user_id": "user_123"
            }
            """;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://network-api.adaptgroup.app/subs/create"))
            .header("Content-Type", "application/json")
            .header("X-Api-Key", apiKey)
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient().send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );

        if (response.statusCode() != 200) {
            throw new IllegalStateException(response.body());
        }

        String subscription = response.body();
        System.out.println(subscription);
    }
}
C#
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add(
    "X-Api-Key",
    Environment.GetEnvironmentVariable("ADAPTGROUP_API_KEY")
);

var payload = JsonSerializer.Serialize(new
{
    api_key_id = 1,
    plan_uuid = "550e8400-e29b-41d4-a716-446655440000",
    external_user_id = "user_123",
});

using var body = new StringContent(
    payload,
    Encoding.UTF8,
    "application/json"
);

using var response = await client.PostAsync("https://network-api.adaptgroup.app/subs/create", body);
response.EnsureSuccessStatusCode();

var subscription = await response.Content.ReadFromJsonAsync<JsonElement>();
Ruby
require "json"
require "net/http"
require "uri"

uri = URI("https://network-api.adaptgroup.app/subs/create")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["X-Api-Key"] = ENV.fetch("ADAPTGROUP_API_KEY")
request.body = {
  api_key_id: 1,
  plan_uuid: "550e8400-e29b-41d4-a716-446655440000",
  external_user_id: "user_123"
}.to_json

response = Net::HTTP.start(
  uri.hostname,
  uri.port,
  use_ssl: true
) { |http| http.request(request) }

unless response.is_a?(Net::HTTPSuccess)
  raise "VPN API request failed: #{response.code}"
end

subscription = JSON.parse(response.body)
Rust
use reqwest::blocking::Client;
use serde_json::{json, Value};
use std::env;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let subscription: Value = Client::new()
        .post("https://network-api.adaptgroup.app/subs/create")
        .header("Content-Type", "application/json")
        .header("X-Api-Key", env::var("ADAPTGROUP_API_KEY")?)
        .json(&json!({
            "api_key_id": 1,
            "plan_uuid": "550e8400-e29b-41d4-a716-446655440000",
            "external_user_id": "user_123"
        }))
        .send()?
        .error_for_status()?
        .json()?;

    println!("{subscription:#}");
    Ok(())
}

Keep X-Api-Key on the server. These examples do not send requests from the browser.

200 OKResponse
{
  "success": true,
  "subscription_uuid": "770fa622-a4bd-63f6-c938-668877662222",
  "subscription_url": "https://network-api.adaptgroup.app/sub/770fa622-a4bd-63f6-c938-668877662222",
  "plan_uuid": "550e8400-e29b-41d4-a716-446655440000",
  "external_user_id": "user_123",
  "end_date": "2026-06-01T12:00:00",
  "devices": 3,
  "days": 30,
  "traffic_limit_bytes": null,
  "balance": "96.50"
}

The API returns a ready subscription URL.

Pass subscription_url directly to the user's VPN client. The response also includes the subscription ID, end date, device limit, traffic limit and remaining balance.