Перепродажа VPN-ключей через один HTTP API
Единый HTTP API для двух тарифов ключей — с вашим личным балансом, ценой за ГБ и списанием сразу после каждой успешной операции. Простой предсказуемый контракт: одинаковая логика ошибок и биллинга на всех эндпоинтах, чтобы интеграция занимала часы, а не дни.
Аутентификация
Каждый запрос требует заголовок Authorization с вашим персональным токеном. Токен неизменен и виден только вам — посмотреть его можно в Telegram-боте на панели партнёра, кнопка «Токен».
Authorization: Bearer ВАШ_ТОКЕН
Токен один на всю организацию — если нужно раздать сотрудникам ограниченный доступ к самому API (а не к боту), такой возможности сейчас нет: обратитесь к нам, если это критично.
Набор доступных вам действий настраивается индивидуально под аккаунт. Если какое-то действие не активировано, вызов вернёт FORBIDDEN (см. «Коды ошибок») — просто напишите нам, чтобы включить нужное.
Формат ответов
Бизнес-результат операции всегда в поле error тела ответа, а не в HTTP-статусе. Настоящий HTTP-код отличный от 200 означает только транспортную/авторизационную проблему.
{
"error": null,
"info": "KEY_EXPANDED",
"charged": 150.0,
"balance": 4200.5
}
{
"error": "INSUFFICIENT_FUNDS",
"info": "Not enough balance...",
"required": 150.0,
"available": 42.5
}
Всегда сначала проверяйте error. Точный текст info — человекочитаемое пояснение и может меняться, не парсите его как код ошибки.
Коды ошибок
Два независимых уровня: транспортные (настоящий HTTP-код) и бизнес-ошибки (HTTP 200, смотрите поле error).
HTTP-уровень
| Код | Когда |
|---|---|
| 401 UNAUTHORIZED | Заголовок Authorization отсутствует/не в формате Bearer …, либо токен не найден. |
| 403 PARTNER_DISABLED | Доступ к API приостановлен для вашего аккаунта («Работа API» выключена). |
| 403 RENT_OVERDUE | Просрочена оплата аренды — доступ заблокирован до оплаты. |
| 422 | Тело запроса не прошло валидацию (например, limit <= 0 или пропущено обязательное поле). Стандартный ответ FastAPI, форма отличается от бизнес-ошибок ниже. |
| 500 INTERNAL_ERROR | Внутренняя ошибка на нашей стороне. Детали не раскрываются в ответе — если повторяется, напишите нам. |
Бизнес-уровень (HTTP 200, поле error)
| Код | Когда |
|---|---|
| FORBIDDEN | Действие не разрешено для вашего аккаунта. Список включённых действий настраивается индивидуально — напишите нам, чтобы включить нужное. |
| INSUFFICIENT_FUNDS | Не хватает баланса + кредитного лимита. В ответе есть required и available. |
| GB_LIMIT_EXCEEDED | Запрошенный объём превышает разовый лимит на операцию. В ответе есть requested и max. |
| SERVICE_UNAVAILABLE | Высокоскоростной тариф временно не настроен на нашей стороне. |
| NOTHING_TO_RESET | У ключа нет накопленного трафика — сбрасывать нечего, списания не было. |
| (другое) | Прочие коды — сообщение в info поясняет причину. |
Тарификация
Ваши price_per_gb и price_fast — индивидуальные, уточняйте актуальные значения через /balance. Мы всегда сначала выполняем саму операцию и списываем деньги только при успехе — при сбое баланс не трогается.
| Действие | Классический тариф | Высокоскоростной |
|---|---|---|
| create | limit × price_per_gb | price_fast (фикс.) |
| expand | добавляемые ГБ × price_per_gb | — |
| edit | limit × months × price_per_gb | — |
| renew | текущий лимит ключа × price_per_gb (читаем сами) | price_fast (фикс.) |
| reset | уже использованный трафик × price_per_gb (читаем сами) | — |
| info / activation / deactivate / revoke / delete / get_trial | бесплатно | — |
Перед списанием мы проверяем, что баланс + кредитный лимит покрывают стоимость — если нет, апстрим не вызывается вовсе (сразу INSUFFICIENT_FUNDS, без побочных эффектов).
Ключи · классический тариф
Объём трафика в ГБ и срок действия задаются явно. Здесь и далее key — это id, полученный при создании ключа. Передавайте именно короткий id — полные ссылки-подписки эндпоинты не распознают.
Выпускает новый ключ с заданным объёмом трафика и сроком действия.
Параметры
| Поле | Тип | Описание | |
|---|---|---|---|
| limit | int | обязательно | Объём трафика в ГБ (1 ГБ = 1 073 741 824 байт), > 0. |
| months | int | обязательно | Срок действия в месяцах, > 0. |
Есть индивидуальный лимит ГБ на одну операцию (max_gb_per_purchase). При превышении — GB_LIMIT_EXCEEDED с полями requested/max. Текущее значение лимита уточняйте у нас — через API оно не отдаётся.
Пример запроса
curl -X POST https://api.singularityvpn.ru/api/v1/key/create \
-H "Authorization: Bearer ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"limit": 100, "months": 1}'Пример ответа
{
"error": null,
"info": "KEY_CREATED",
"id": "N9Wg8t58qNo_azod",
"expireAt": 1755590400,
"charged": 150.0,
"balance": 4350.0
}id — сохраните его: это key для всех остальных операций с этим ключом.
Фиксированный пробный ключ: 2 ГБ, без сброса трафика, срок действия около 5 дней. Тело запроса не требуется.
Пример запроса
curl -X POST https://api.singularityvpn.ru/api/v1/key/get_trial \
-H "Authorization: Bearer ВАШ_ТОКЕН"Пример ответа
{
"error": null,
"info": "TRIAL_CREATED",
"id": "Tr1al_9kZpQ2vw",
"expireAt": 1755158400
}Полная информация о ключе: статус, трафик, срок действия, ссылка-подписка, устройства. Доступно всегда, без отдельного разрешения.
Параметры
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа, полученный при создании. |
Пример запроса
curl -X POST https://api.singularityvpn.ru/api/v1/key/info \
-H "Authorization: Bearer ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"key": "N9Wg8t58qNo_azod"}'Пример ответа (сокращённо)
{
"error": null,
"shortUuid": "N9Wg8t58qNo_azod",
"status": "ACTIVE",
"trafficLimitBytes": 107374182400,
"trafficLimitStrategy": "MONTH_ROLLING",
"expireAt": "2026-08-19T00:00:00Z",
"subscriptionUrl": "https://singularityvpn.fun/N9Wg8t58qNo_azod",
"userTraffic": {
"usedTrafficBytes": 2147483648,
"lifetimeUsedTrafficBytes": 2147483648
},
"devices": { "total": 2 }
}Добавляет объём к уже существующему лимиту ключа (не задаёт новый лимит целиком — именно добавляет).
Параметры
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа. |
| limit | int | обязательно | Сколько ГБ добавить (не итоговое значение), > 0. |
Пример запроса
curl -X POST https://api.singularityvpn.ru/api/v1/key/expand \
-H "Authorization: Bearer ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"key": "N9Wg8t58qNo_azod", "limit": 50}'Пример ответа
{ "error": null, "info": "KEY_EXPANDED", "charged": 75.0, "balance": 4275.0 }В отличие от expand, задаёт лимит и срок как абсолютные значения (перезаписывает, а не добавляет). id ключа при этом сохраняется.
Параметры
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа. |
| limit | int | обязательно | Новый лимит трафика в ГБ (абсолютное значение), > 0. |
| months | int | обязательно | Новый срок действия в месяцах, > 0. |
Стоимость считается как limit × months × price_per_gb — как за новый ключ такого же объёма и срока, при сохранении id.
Пример запроса
curl -X POST https://api.singularityvpn.ru/api/v1/key/edit \
-H "Authorization: Bearer ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"key": "N9Wg8t58qNo_azod", "limit": 200, "months": 3}'Пример ответа
{ "error": null, "info": "KEY_EDITED", "charged": 900.0, "balance": 3375.0 }Продлевает ключ. Объём ГБ передавать не нужно — перед списанием мы сами читаем текущий лимит ключа через /key/info и тарифицируем по нему.
Параметры
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа. |
Пример запроса
curl -X POST https://api.singularityvpn.ru/api/v1/key/renew \
-H "Authorization: Bearer ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"key": "N9Wg8t58qNo_azod"}'Пример ответа
{ "error": null, "expireAt": 1758268800, "charged": 150.0, "balance": 3225.0 }Снимает ключ с паузы. Чистый переключатель статуса, без влияния на трафик/срок.
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа. |
{ "error": null, "info": "…" }Ставит ключ на паузу — тот же переключатель статуса, в обратную сторону.
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа. |
{ "error": null, "info": "…" }Название вводит в заблуждение: это не отзыв/удаление ключа, а перевыпуск его учётных данных. Старый id перестаёт существовать сразу же, выдаётся новый.
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | Текущий id ключа. |
Обязательно сохраните NEW_KEY из ответа и обновите его у себя — старый id больше не работает ни в одном запросе.
Пример ответа
{ "error": null, "NEW_KEY": "aB3xQpLk92mZrTs1" }Обнуляет использованный трафик ключа. Перед сбросом мы читаем фактически использованный объём и списываем за него — иначе этот трафик остался бы неоплаченным.
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа. |
Если у ключа использовано 0 байт — запрос отклоняется с NOTHING_TO_RESET, списания не происходит (*поэтому «платно» — только когда есть что списывать).
Пример ответа
{ "error": null, "info": "…", "charged": 37.5, "balance": 3187.5 }Безвозвратное удаление. Последующий вызов /key/info по этому id вернёт ошибку.
| Поле | Тип | Описание | |
|---|---|---|---|
| key | string | обязательно | id ключа. |
{ "error": null, "info": "KEY_DELETED" }Ключи · высокоскоростной тариф
Тариф для клиентов с повышенными требованиями к скорости. Без выбора объёма (безлимит) и без выбора срока — фиксировано 1 месяц. Цена фиксированная за операцию (price_fast), а не за ГБ.
Выпускает ключ высокоскоростного тарифа.
| Поле | Тип | Описание | |
|---|---|---|---|
| period | string | необязательно | Сейчас поддерживается только "1m" — значение по умолчанию. |
Пример ответа
{
"error": null,
"key": "MWI3OGJlZjg0MzhkNDEsMTc4NDQ5NjI5NQPTS8KWejU9",
"username": "1b78bef8438d41",
"expireAt": 1787088294,
"charged": 300.0,
"balance": 2887.5
}Готовая ссылка для клиента собирается по шаблону https://i.singularityvpn.ru/{key}, например https://i.singularityvpn.ru/MWI3OGJlZjg0MzhkNDEsMTc4NDQ5NjI5NQPTS8KWejU9. username сохраните отдельно — он нужен для продления.
Продлевает высокоскоростной ключ ещё на период. Цена та же, что при создании.
| Поле | Тип | Описание | |
|---|---|---|---|
| username | string | обязательно | Значение поля username, полученное при создании ключа. |
| period | string | необязательно | Сейчас только "1m". |
Пример запроса
curl -X POST https://api.singularityvpn.ru/api/v1/key/extend_fast \
-H "Authorization: Bearer ВАШ_ТОКЕН" \
-H "Content-Type: application/json" \
-d '{"username": "1b78bef8438d41"}'Пример ответа
{ "error": null, "status": "extended", "charged": 300.0, "balance": 2587.5 }Баланс
Без тела запроса — только заголовок авторизации. Полезно вызывать перед крупной операцией, чтобы заранее прикинуть, хватит ли средств.
Пример запроса
curl https://api.singularityvpn.ru/api/v1/balance \
-H "Authorization: Bearer ВАШ_ТОКЕН"Пример ответа
{
"balance": 2587.5,
"credit_limit": 500.0,
"price_per_gb": 1.5,
"price_fast": 300.0
}credit_limit — насколько баланс может уйти в минус. Реально доступная сумма на операцию = balance + credit_limit.
Частые вопросы
"error": null?revoke отличается от delete?revoke не удаляет ключ, а перевыпускает его учётные данные: старый id умирает, выдаётся новый (NEW_KEY), которым нужно заменить сохранённую ссылку у клиента. delete удаляет ключ безвозвратно.renew не принимает объём ГБ?/key/create_fast отдаёт поле key. Ссылка для клиента — https://i.singularityvpn.ru/{key}.key, а username — отдельное поле в ответе /key/create_fast. Сохраняйте оба поля при создании: key для ссылки, username для /key/extend_fast.{"error": "FORBIDDEN"}. Список включённых действий настраивается индивидуально под ваш аккаунт — если нужно больше, напишите нам.limit и в расчёте стоимости.