CFGCLOUD API

Базовый URL: https://api.cfg.ceo

Аутентификация

Все запросы требуют API-ключ в заголовке X-API-Key. Ключ выдаётся через бота cfgcloud.

curl -H "X-API-Key: YOUR_KEY" https://api.cfg.ceo/stats

Формат ответов

Все ответы — JSON. Ошибки возвращаются на русском.

Успех
{"success": true, ...данные...}
Ошибка
{"success": false, "error": "Текст ошибки."}
HTTP-коды 400 — невалидный запрос · 401 — нет ключа · 403 — нет доступа · 404 — не найдено · 409 — конфликт · 429 — лимит · 500 — внутренняя ошибка · 503 — сервис недоступен

Эндпоинты

GET /stats

Статистика аккаунтов

{
  "success": true,
  "stats": {"free": 42, "busy": 108, "total": 150}
}
GET /me

Информация о владельце API ключа

{
  "success": true,
  "user": {"user_id": 5770559689, "username": "cfg"}
}
POST /accounts/issue

Взять аккаунт. Тело запроса не требуется — пользователь определяется по API ключу.

Проверки Наличие в БД, бан, наличие локов и т.п.
curl -X POST -H "X-API-Key: YOUR_KEY" https://api.cfg.ceo/accounts/issue
Ответ
{
  "success": true,
  "tag": "TAG123",
  "account": {
    "email": "[email protected]",
    "password": "password123",
    "time": "2026-01-20 20:00:00",
    "lifetime_minutes": 60
  }
}
Ошибки 400: Ваш ID не найден в базе данных
403: Вы заблокированы в системе
403: Для получения доступа необходимо сделать хотя бы 1 лок
409: У вас уже есть активный аккаунт: {tag}
429: Вы достигли лимита аккаунтов. Отмените один из активных
503: Нет доступных аккаунтов
POST /accounts/cancel

Отменить аккаунт. Только владелец может отменить свой аккаунт.

ПолеТипОписание
tagstringТег аккаунта обязательно
nicknamestringНикнейм обязательно
Ответ
{"success": true, "tag": "TAG123"}
Ошибки 403: Вы не являетесь владельцем этого аккаунта
404: Аккаунт не найден
GET /accounts/{tag}/time

Оставшееся время аккаунта в минутах. Доступно только холдеру или админу.

ПараметрТипОписание
tagstringТег аккаунта (путь) обязательно
Ответ
{"success": true, "minutes_left": 45}
Ошибки 403: Нет доступа · 404: Аккаунт не найден / не выдан
GET /accounts/{tag}/locks

Проверить наличие успешного лока. Доступно холдеру, пользователям из истории холдеров и админам.

ПараметрТипОписание
tagstringТег аккаунта (путь) обязательно
Ответ
{"success": true, "tag": "TAG123", "has_lock": true}
has_lock: true — для аккаунта был совершен успешный лок
Ошибки 403: Нет доступа · 404: Аккаунт не найден
GET /services/imei/{imei}

Проверка IMEI — возвращает модель устройства, маску email и статус.

ПараметрТипОписание
imeistringIMEI устройства (15 цифр) обязательно
Найден
{
  "success": true,
  "found": true,
  "device": "iPhone 17 Pro",
  "imei": "350344890446295",
  "email": "s•••••@icloud.com",
  "status": "жив"
}
Не найден
{"success": true, "found": false}
Статусы жив — аккаунт активен · деактивирован — аккаунт мёртв · уточнить — заблокирован или ограничен, требуется проверка
Ошибки 400: Невалидный IMEI (требуется 15 цифр)
GET /work/logs

Логи рабочих процессов из кольцевого буфера (2000 записей, TTL 24 часа). Поддерживает инкрементальный поллинг.

ПараметрТипОписание
afterintЗаписи с id > after по умолчанию 0
limitintМаксимум записей, 1-500 по умолчанию 200
Ответ
{
  "logs": [
    {"id": 1723891200001, "tag": "#CL123", "message": "БАМ! Зверь повержен!", "level": "SUCCESS", "time": 1723891200001},
    {"id": 1723891200002, "tag": "#CL124", "message": "Поиск устройств...", "level": "INFO", "time": 1723891200002}
  ],
  "last_id": 1723891200002
}
Сохраняйте last_id и передавайте как after в следующем запросе для инкрементального поллинга. Авторизация: X-API-Key или X-Monitor-Secret (localhost — без авторизации).

Примеры

cURL
Python
JavaScript
# Статистика
curl -H "X-API-Key: YOUR_KEY" https://api.cfg.ceo/stats

# Взять аккаунт
curl -X POST -H "X-API-Key: YOUR_KEY" https://api.cfg.ceo/accounts/issue

# Сгенерировать ссылку (rave)
curl -X POST \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"service":"rave"}' \
  https://api.cfg.ceo/accounts/link

# Отменить аккаунт
curl -X POST \
  -H "X-API-Key: YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tag":"TAG123","nickname":"cfg"}' \
  https://api.cfg.ceo/accounts/cancel

# Время / лок / IMEI / логи
curl -H "X-API-Key: YOUR_KEY" https://api.cfg.ceo/accounts/TAG123/time
curl -H "X-API-Key: YOUR_KEY" https://api.cfg.ceo/accounts/TAG123/locks
curl -H "X-API-Key: YOUR_KEY" https://api.cfg.ceo/services/imei/350344890446295
curl -H "X-API-Key: YOUR_KEY" "https://api.cfg.ceo/work/logs?after=0&limit=100"
import aiohttp

API = "https://api.cfg.ceo"
KEY = "YOUR_API_KEY"
H   = {"X-API-Key": KEY}

async def stats():
    async with aiohttp.ClientSession() as s:
        async with s.get(f"{API}/stats", headers=H) as r:
            return await r.json()

async def issue():
    async with aiohttp.ClientSession() as s:
        async with s.post(f"{API}/accounts/issue", headers=H) as r:
            return await r.json()

async def link(service, service_type=None):
    body = {"service": service}
    if service_type:
        body["service_type"] = service_type
    async with aiohttp.ClientSession() as s:
        async with s.post(f"{API}/accounts/link",
                          headers={**H, "Content-Type": "application/json"},
                          json=body) as r:
            return await r.json()
const API = "https://api.cfg.ceo";
const KEY = "YOUR_API_KEY";
const h   = {"X-API-Key": KEY};

async function stats() {
  return (await fetch(`${API}/stats`, {headers: h})).json();
}

async function issue() {
  return (await fetch(`${API}/accounts/issue`, {method: "POST", headers: h})).json();
}

async function link(service, serviceType) {
  const body = {service};
  if (serviceType) body.service_type = serviceType;
  return (await fetch(`${API}/accounts/link`, {
    method: "POST",
    headers: {...h, "Content-Type": "application/json"},
    body: JSON.stringify(body)
  })).json();
}

Свой бот

При загрузке собственного бота через Сервисы доступен встроенный клиент для работы с аккаунтами. Авторизация подключается автоматически — указывать ключ не нужно.

import cfgcloud_module as client

Доступные функции

client.issue_account(tag, user_id, username, lifetime_minutes=60)

Выдать аккаунт пользователю. Возвращает True / False.

ПараметрТипОписание
tagstrПустая строка "" — сервер выберет сам
user_idintTelegram ID пользователя
usernamestrНикнейм пользователя
lifetime_minutesintВремя жизни аккаунта 60
client.get_last_issued()

Возвращает (tag, data) последнего выданного аккаунта. Вызывать сразу после issue_account.

tag, data = client.get_last_issued()
email = data["email"]
password = data["login_password"]
client.update_acc_fields(tag, **fields)

Обновить поля аккаунта или вернуть его.

# Привязать message_id к аккаунту
client.update_acc_fields(tag, bot_message_id=msg.message_id, client_chat_id=user_id)

# Вернуть аккаунт
client.update_acc_fields(tag, status="free", holder_name=username)
client.load_accs()

Загрузить все активные аккаунты. Возвращает dict — {tag: {email, minutes_left, ...}}.

client.get_acc(tag)

Получить данные конкретного аккаунта с актуальным minutes_left.

Пример бота

import asyncio
from aiogram import Bot, Dispatcher, Router, types, F
from aiogram.filters import Command
from aiogram.fsm.storage.memory import MemoryStorage
from aiogram.types import InlineKeyboardMarkup as IKM, InlineKeyboardButton as IKB
import cfgcloud_module as client

BOT_TOKEN = "СЮДА_ТОКЕН"
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(storage=MemoryStorage())
router = Router()
dp.include_router(router)
_extended = set()

def acc_kb(tag, extended=False):
    rows = [[IKB(text="Время ⏳", callback_data=f"time:{tag}")]]
    if not extended:
        rows[0].append(IKB(text="Продлить 🔄", callback_data=f"ext:{tag}"))
    rows.append([IKB(text="Отменить аккаунт", callback_data=f"free:{tag}")])
    return IKM(inline_keyboard=rows)

def ext_kb(tag):
    return IKM(inline_keyboard=[
        [IKB(text="15 мин", callback_data=f"ext15:{tag}"),
         IKB(text="30 мин", callback_data=f"ext30:{tag}"),
         IKB(text="60 мин", callback_data=f"ext60:{tag}")]
    ])

@router.message(Command("start"))
async def cmd_start(msg: types.Message):
    kb = IKM(inline_keyboard=[[IKB(text="Получить аккаунт", callback_data="get_acc")]])
    await msg.answer("Нажми кнопку чтобы получить аккаунт.", reply_markup=kb)

@router.callback_query(F.data == "get_acc")
async def get_account(cb: types.CallbackQuery):
    ok = client.issue_account("", cb.from_user.id, cb.from_user.username or "", 60)
    if not ok:
        return await cb.answer("Нет свободных аккаунтов", show_alert=True)
    tag, data = client.get_last_issued()
    msg = await cb.message.answer(
        f"Аккаунт: {tag}\nEmail: <code>{data['email']}</code>\n"
        f"Пароль: <code>{data['login_password']}</code>",
        parse_mode="HTML", reply_markup=acc_kb(tag))
    client.update_acc_fields(tag, bot_message_id=msg.message_id,
                             client_chat_id=cb.from_user.id)
    await cb.answer()

@router.callback_query(F.data.startswith("time:"))
async def show_time(cb: types.CallbackQuery):
    acc = client.get_acc(cb.data.split(":", 1)[1])
    await cb.answer(f"Осталось: {acc.get('minutes_left', 0)} мин." if acc
                    else "Аккаунт не найден", show_alert=True)

@router.callback_query(F.data.startswith("ext:"))
async def extend_menu(cb: types.CallbackQuery):
    tag = cb.data.split(":", 1)[1]
    if tag in _extended:
        return await cb.answer("Уже продлено", show_alert=True)
    await cb.message.edit_reply_markup(reply_markup=ext_kb(tag))
    await cb.answer()

@router.callback_query(F.data.regexp(r"^ext(15|30|60):"))
async def extend_do(cb: types.CallbackQuery):
    parts = cb.data.split(":", 1)
    mins, tag = int(parts[0][3:]), parts[1]
    if tag in _extended:
        return await cb.answer("Уже продлено", show_alert=True)
    acc = client.get_acc(tag)
    if not acc or acc.get("minutes_left", 0) <= 0:
        return await cb.answer("Аккаунт не найден или истёк", show_alert=True)
    client.update_acc_fields(tag, lifetime_minutes=acc["minutes_left"] + mins)
    _extended.add(tag)
    await cb.message.edit_reply_markup(reply_markup=acc_kb(tag, extended=True))
    await cb.answer(f"+{mins} мин.", show_alert=True)

@router.callback_query(F.data.startswith("free:"))
async def free_account(cb: types.CallbackQuery):
    tag = cb.data.split(":", 1)[1]
    client.update_acc_fields(tag, status="free", holder_name=cb.from_user.username or "user")
    _extended.discard(tag)
    try: await cb.message.delete()
    except: pass
    note = await cb.message.answer(f"Аккаунт {tag} отменен")
    await cb.answer()
    await asyncio.sleep(3)
    try: await note.delete()
    except: pass

if __name__ == "__main__":
    dp.run_polling(bot, skip_updates=True)

Ограничения и безопасность

Rate Limiting

  • 180 запросов в минуту на ключ
  • Превышение: 429

Безопасность

  • Только HTTPS
  • Не передавайте ключи третьим лицам
  • Перевыпуск ключей через бота cfgcloud