Bot API · сеть

Сеть ботов: один ключ на все боты

Если у вас десятки, сотни или тысячи ботов, их не нужно подключать по одному и не нужно передавать токены. Вы получаете один API-ключ на всю сеть; каждый бот сам запрашивает рекламу и сам отправляет её пользователю. Новый бот начинает работать с первого запроса.

POST https://hiviews.net/api/partner/ad

Быстрый старт

  1. Войдите в кабинет

    Откройте веб-кабинет под тем Telegram-аккаунтом, на который оформлена сеть. В «Моих ботах» есть карточка вашей сети.

  2. Получите ключ

    В карточке сети откройте «Интеграция» и нажмите «Новый ключ». Ключ показывается один раз — сохраните его на сервере и не публикуйте. Если ключ утёк, выпустите новый там же: старый сразу перестанет работать.

  3. Вставьте код в ботов

    Возьмите готовый код ниже. Он один для всех ботов сети: имя бота подставляется само.

  4. Вызывайте в двух местах

    При /start — приветственный показ, после того как бот выдал человеку результат, — обычный. Подробно — в разделе «Когда вызывать».

  5. Проверьте

    Пройдите проверку из пяти пунктов на одном боте, затем выкатывайте на остальные.

Регистрировать ботов заранее не нужно. Бот появляется в сети в момент первого запроса с его именем и сразу получает рекламу: отдельной модерации каждого бота нет.

Запрос

POST https://hiviews.net/api/partner/ad с JSON-телом.

Заголовки

ЗаголовокЗначение
AuthorizationКлюч сети как есть, без префикса Bearer
Content-Typeapplication/json

Поля тела

ПолеТипОписание
UserIdint, обязательноеID пользователя Telegram, которому бот покажет рекламу. Только личные чаты.
Botstring, обязательноеИмя бота, для которого запрашивается реклама, без @ (регистр не важен). Это должен быть настоящий username бота: латиница, цифры и подчёркивание, оканчивается на bot.
StartPlacebooltrue — приветственный показ (при /start), false — обычный показ после выдачи результата. Если поле не передать, запрос считается обычным показом.
UserFirstNamestringИмя пользователя: подставляется в текст поста вместо $name.
UsernamestringUsername пользователя без @, если есть.
LanguagestringЯзык пользователя из Telegram (language_code). По нему подбираются кампании. Если не передать, мы попробуем определить язык сами (по прошлым показам этому человеку и по имени), но точнее — передавать.
HasPhotobooleanНеобязательно. true, если у пользователя есть аватар (getUserProfilePhotos → total_count > 0; результат стоит кешировать). Нужно для кампаний с отметкой «Verified»: их получают только люди с аватаром и обычным именем. Если поле не передавать, мы сами проверяем тех, у кого есть username, — поэтому Username передавайте всегда. Не ставьте true всем подряд: такие боты видны по переходам и лишаются оплаты показов.

Пример

curl -X POST https://hiviews.net/api/partner/ad \
  -H "Authorization: YOUR_NETWORK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"UserId": 123456789, "UserFirstName": "Ivan", "Language": "ru", "StartPlace": true, "Bot": "my_first_bot"}'

Ответ

Ответ всегда JSON. Если реклама есть — "ok": true и объект ad; если нет — "ok": false и поле reason (все значения).

{
  "ok": true,
  "ad": {
    "text": "<a href=\"https://hiviews.net/api/go/v/…\">&#8203;</a>Ivan, …",
    "parse_mode": "HTML",
    "reply_markup": {"inline_keyboard": [[{"text": "Open", "url": "https://hiviews.net/api/go/…"}]]},
    "photo": "https://hiviews.net/api/go/m/…",
    "delete_after": 60
  }
}
Поле adЧто с ним делать
textТекст поста в разметке HTML. Отправьте без изменений. Если есть медиа — это подпись к нему.
parse_modeВсегда HTML — передайте в метод отправки.
reply_markupКлавиатура с кнопками-ссылками. Передайте как есть.
photo / video / animationПрисутствует не больше одного: ссылка на файл. Отправьте методом sendPhoto, sendVideo или sendAnimation, передав ссылку как файл. Если поля нет — sendMessage.
delete_afterЕсли есть — удалите сообщение через столько секунд. Так помечается внешняя реклама, которая показывается недолго.

Как отправить пост, чтобы показ оплатили

Мы не видим, что бот отправил пользователю, поэтому показ подтверждает сам Telegram. В начале текста каждого поста стоит невидимая ссылка: когда сообщение отправлено, серверы Telegram открывают её, и показ засчитывается.

  • Отправляйте пост ровно в том виде, в каком получили. Не вырезайте начало текста, не пересобирайте разметку, не меняйте ссылки в тексте и кнопках.
  • Не отключайте превью ссылок. Не передавайте disable_web_page_preview и link_preview_options с отключением. Во многих библиотеках превью выключают глобально в настройках бота — для рекламного сообщения включите.
  • Отправляйте сразу. Подтверждение принимается в течение 3 минут после выдачи поста. Пост, отправленный позже, не оплачивается.
  • Один запрос — одно сообщение одному человеку. Не рассылайте полученный пост другим пользователям и не запрашивайте рекламу без действия пользователя.

Неподтверждённый пост не оплачивается: ни показ, ни переходы по нему. Если бот берёт посты и не отправляет больше половины из них, он перестаёт получать рекламу — ответ unconfirmed_sends.

Когда вызывать

МоментStartPlaceФормат
Человек нажал /starttrueПриветственный показ. Вызывайте до своего приветствия или сразу после него.
Бот выдал результат: нашёл трек, скачал файл, ответил на запросfalseОбычный показ. Вызывайте после того, как результат уже отправлен, чтобы реклама была последним сообщением.
  • Вызывайте в обоих местах — так боты зарабатывают на обоих форматах.
  • Не ставьте StartPlace: true везде: приветственный показ одному человеку в одном боте выдаётся не чаще раза в сутки, лишние запросы вернутся без рекламы.
  • Запрос не должен задерживать бота: ставьте таймаут 5 секунд и ловите ошибки. Готовый код так и делает.

Готовый код

Код один для всех ботов сети. Имя бота в поле Bot берётся у самого бота, поэтому при запуске нового бота ничего менять не нужно.

Python, aiogram 3

# HiViews, сеть ботов: один ключ на все боты. Бот сам отправляет рекламу.
# pip install aiogram aiohttp
import asyncio, aiohttp
from aiogram import Bot
from aiogram.types import InlineKeyboardMarkup

HIVIEWS_KEY = "YOUR_NETWORK_KEY"

async def show_ad(bot: Bot, user, start: bool = False) -> None:
    """При /start вызывайте с start=True, после выдачи результата — с start=False."""
    me = await bot.me()  # имя бота, который сейчас отвечает пользователю
    payload = {"UserId": user.id, "UserFirstName": user.first_name, "Username": user.username,
               "Language": user.language_code, "StartPlace": start, "Bot": me.username}
    try:
        async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=5)) as s:
            async with s.post("https://hiviews.net/api/partner/ad", json=payload,
                              headers={"Authorization": HIVIEWS_KEY}) as r:
                ad = (await r.json()).get("ad")
        if not ad:
            return  # сейчас рекламы для этого человека нет
        kw = {"parse_mode": "HTML", "reply_markup": InlineKeyboardMarkup.model_validate(ad["reply_markup"])}
        if ad.get("photo"):
            msg = await bot.send_photo(user.id, ad["photo"], caption=ad["text"], **kw)
        elif ad.get("video"):
            msg = await bot.send_video(user.id, ad["video"], caption=ad["text"], **kw)
        elif ad.get("animation"):
            msg = await bot.send_animation(user.id, ad["animation"], caption=ad["text"], **kw)
        else:
            msg = await bot.send_message(user.id, ad["text"], **kw)  # превью ссылок не отключайте
        if ad.get("delete_after"):  # такой пост убираем через минуту, если по нему не перешли
            asyncio.get_running_loop().call_later(
                ad["delete_after"], lambda: asyncio.ensure_future(_quiet_delete(bot, user.id, msg.message_id)))
    except Exception:
        pass  # реклама никогда не должна ломать бота

async def _quiet_delete(bot: Bot, chat_id: int, message_id: int) -> None:
    try:
        await bot.delete_message(chat_id, message_id)
    except Exception:
        pass

# Пример:  await show_ad(bot, message.from_user, start=True)   # /start
#         await show_ad(bot, message.from_user)               # после выдачи результата

Node.js, Telegraf

// HiViews, сеть ботов: один ключ на все боты. Node.js 18+, Telegraf.
const HIVIEWS_KEY = process.env.HIVIEWS_KEY; // YOUR_NETWORK_KEY

async function showAd(ctx, start = false) {
  // При /start вызывайте showAd(ctx, true), после выдачи результата — showAd(ctx, false).
  try {
    const u = ctx.from;
    const res = await fetch("https://hiviews.net/api/partner/ad", {
      method: "POST",
      headers: { Authorization: HIVIEWS_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({ UserId: u.id, UserFirstName: u.first_name, Username: u.username,
                             Language: u.language_code, StartPlace: start,
                             Bot: ctx.botInfo.username }), // имя бота, который сейчас отвечает пользователю
      signal: AbortSignal.timeout(5000),
    });
    const { ad } = await res.json();
    if (!ad) return; // сейчас рекламы для этого человека нет
    const extra = { parse_mode: "HTML", reply_markup: ad.reply_markup };
    let msg;
    if (ad.photo) msg = await ctx.telegram.sendPhoto(u.id, ad.photo, { caption: ad.text, ...extra });
    else if (ad.video) msg = await ctx.telegram.sendVideo(u.id, ad.video, { caption: ad.text, ...extra });
    else if (ad.animation) msg = await ctx.telegram.sendAnimation(u.id, ad.animation, { caption: ad.text, ...extra });
    else msg = await ctx.telegram.sendMessage(u.id, ad.text, extra); // превью ссылок не отключайте
    if (ad.delete_after) { // такой пост убираем через минуту, если по нему не перешли
      setTimeout(() => ctx.telegram.deleteMessage(u.id, msg.message_id).catch(() => {}), ad.delete_after * 1000);
    }
  } catch (e) { /* реклама никогда не должна ломать бота */ }
}

// Пример:  bot.start(async (ctx) => { await showAd(ctx, true); /* ... */ });

PHP

<?php
// HiViews, сеть ботов: один ключ на все боты.
const HIVIEWS_KEY = 'YOUR_NETWORK_KEY';

/** $user — массив from из апдейта Telegram, $botUsername — имя бота без @. При /start: $start = true, после выдачи результата: false.
 *  Возвращает [message_id, delete_after] — если delete_after не null, удалите сообщение через столько секунд. */
function hiviews_show_ad(string $botToken, string $botUsername, array $user, bool $start = false): ?array
{
    $ch = curl_init('https://hiviews.net/api/partner/ad');
    curl_setopt_array($ch, [
        CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5,
        CURLOPT_HTTPHEADER => ['Authorization: ' . HIVIEWS_KEY, 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode([
            'UserId' => $user['id'], 'UserFirstName' => $user['first_name'] ?? '',
            'Username' => $user['username'] ?? null, 'Language' => $user['language_code'] ?? null,
            'StartPlace' => $start, 'Bot' => $botUsername, // имя бота, который сейчас отвечает пользователю
        ]),
    ]);
    $ad = json_decode((string) curl_exec($ch), true)['ad'] ?? null;
    curl_close($ch);
    if (!$ad) {
        return null; // сейчас рекламы для этого человека нет
    }
    $params = ['chat_id' => $user['id'], 'parse_mode' => 'HTML', 'reply_markup' => json_encode($ad['reply_markup'])];
    if (!empty($ad['photo'])) {
        $method = 'sendPhoto'; $params += ['photo' => $ad['photo'], 'caption' => $ad['text']];
    } elseif (!empty($ad['video'])) {
        $method = 'sendVideo'; $params += ['video' => $ad['video'], 'caption' => $ad['text']];
    } elseif (!empty($ad['animation'])) {
        $method = 'sendAnimation'; $params += ['animation' => $ad['animation'], 'caption' => $ad['text']];
    } else {
        $method = 'sendMessage'; $params += ['text' => $ad['text']]; // превью ссылок не отключайте
    }
    $ch = curl_init('https://api.telegram.org/bot' . $botToken . '/' . $method);
    curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10,
                            CURLOPT_POSTFIELDS => $params]);
    $sent = json_decode((string) curl_exec($ch), true);
    curl_close($ch);
    return [$sent['result']['message_id'] ?? null, $ad['delete_after'] ?? null];
}

// Пример:  hiviews_show_ad($botToken, 'my_first_bot', $update['message']['from'], true);   // /start

Если рекламы нет: значения reason

При "ok": false ничего отправлять не нужно — бот просто продолжает работу. Поле reason помогает понять причину.

HTTPreasonЧто означает и что делать
200no_adsСейчас нет подходящей рекламы для этого человека. Обычная ситуация.
200cap_viewer_window, cap_hello_window, cap_daily_cap, cap_externalЭтот человек недавно уже видел рекламу. Лимиты защищают пользователей: не больше одного объявления в час и шести в сутки по всей сети, приветственное — раз в сутки в одном боте.
200placement_offЭтот формат показа выключен в настройках сети в кабинете.
200parallelДля этого человека одновременно пришёл другой запрос. Ничего делать не нужно.
200rate_limitedСлишком много запросов в минуту (см. лимиты). Запросы сверх лимита просто остаются без рекламы.
200unconfirmed_sendsБольше половины последних постов этого бота Telegram не подтвердил. Проверьте, что пост отправляется без изменений и с превью ссылок. Выдача возобновится сама, когда доля подтверждений восстановится.
400bad_requestТело не JSON, нет UserId или он не положительное число.
400bad_keyНет заголовка Authorization или ключ неверный.
400bad_botПоле Bot не передано или не похоже на имя бота.
400bot_takenБот с таким именем уже подключён к HiViews отдельно (с токеном или без). Чтобы он работал через сеть, удалите его отдельное подключение в кабинете.
400too_many_botsВ сети больше 50 000 ботов — напишите в поддержку.
403bot_pausedСеть поставлена на паузу в кабинете.
403bot_moderation, bot_bannedСеть на проверке или заблокирована — напишите в поддержку.

За что и как платим

  • За показы. Оплачивается каждый показ платной кампании, отправку которого подтвердил Telegram, — и приветственный, и обычный. Ставка за показ без токена ниже, чем у ботов, подключённых с токеном.
  • За переходы. По кампаниям с оплатой за клик — $0.04 за переход. Оплачивается первый переход по показу, не чаще одного с одного адреса в час.
  • Внешняя реклама. Показывается, когда своих подходящих кампаний нет, чтобы бот не простаивал. Оплата только за переходы, цена зависит от страны и аудитории, доход начисляется на следующий день.
  • Вывод. Доход всех ботов сети идёт на один баланс. Он становится доступен к выводу через 7 дней (проверка на накрутку), вывод от $50 в USDT (TRC20), комиссия за вывод — $3. Подробнее — в гайде по финансам.

Статистика в карточке сети показывает показы, переходы и доход по всем ботам вместе.

Контроль качества

Все ссылки в рекламе идут через наше отслеживание, и мы знаем, как часто по тем же кампаниям переходят в ботах, где доставку отправляем сами. Поэтому для каждого бота и для сети целиком известно, сколько переходов должно быть на такое число показов.

  • Если показов много, а переходов почти нет или их неправдоподобно много, оплата за показы отключается автоматически — у этого бота или у всей сети, если расхождение по сети в целом. Остаётся оплата за переходы.
  • Переходы считаются по источнику: много ссылок, открытых с одного адреса, — это один переход.
  • Доход за накрученные показы возвращается рекламодателям. Не запрашивайте рекламу на выдуманные UserId и не отправляйте посты в служебные чаты.

Лимиты

ЧтоЗначение
Запросов в минуту на одного бота1 200
Запросов в минуту на всю сеть120 000
Ботов в сетидо 50 000
Время на отправку поста после получения3 минуты
Рекомендуемый таймаут запроса5 секунд

Нужно больше — напишите в поддержку, лимиты поднимаются.

Проверка интеграции

  1. Запрос проходит

    Отправьте пример запроса со своим ключом и именем одного из ботов. Ответ "ok": true с объектом ad или "ok": false с no_ads — оба означают, что ключ и имя бота приняты.

  2. Пост приходит целиком

    Нажмите /start в боте со своего аккаунта. Реклама должна прийти с кнопкой, а если в посте есть картинка или видео — с ними.

  3. Кнопка ведёт через hiviews.net

    Ссылка кнопки начинается с https://hiviews.net/api/go/. Если она другая — код меняет ссылки, переходы не будут засчитаны.

  4. Показ появился в статистике

    Через минуту откройте карточку сети в кабинете: показ должен быть в статистике. Если его нет — пост изменён или отключено превью ссылок.

  5. Обычный показ идёт после результата

    Сделайте в боте запрос и убедитесь, что реклама приходит после результата, а не перед ним.

Настройки сети

Настройки задаются один раз в карточке сети в кабинете и действуют для всех её ботов.

  • Форматы: можно выключить приветственный или обычный показ.
  • Категории рекламы: можно отказаться от любых — ставок, VPN, знакомств и других.
  • Внешняя реклама и реклама с оплатой за переходы: включаются и выключаются отдельно.
  • Пауза: останавливает рекламу во всех ботах сети сразу.
  • Тематики сети задаёт поддержка при создании сети; чтобы изменить их, напишите в поддержку.

Частые вопросы

Нужно ли передавать токены ботов?

Нет. Боты отправляют рекламу сами, своим токеном, который остаётся у вас. Нам нужен только ключ сети в заголовке запроса.

Как добавить нового бота в сеть?

Никак отдельно: начните отправлять запросы с его именем в поле Bot. Бот регистрируется при первом запросе и сразу получает рекламу.

Можно ли использовать ключ сети для бота, который уже подключён отдельно?

Нет, запрос вернёт bot_taken. Удалите отдельное подключение бота в кабинете — после этого он заработает через сеть. Учтите, что у бота, подключённого с токеном, ставка за показ выше.

Почему показы есть, а оплаты за них нет?

Оплачиваются показы, которые подтвердил Telegram. Проверьте, что пост отправляется без изменений, с превью ссылок и в течение 3 минут после получения.

Что будет, если один бот сети нарушит правила?

Оплата за показы отключится у этого бота, остальные продолжат работать. Если расхождение между показами и переходами заметно по сети в целом, оплата за показы отключается у всей сети.

Нужен ключ сети или остались вопросы? Сеть оформляется по запросу: напишите в поддержку, сколько у вас ботов и какой они тематики.