PiarFlowAPI v1
Открыть бота

Продажа трафика

PiarFlow API

Подключите задания PiarFlow к своему Telegram-боту: получите спонсоров для пользователя, покажите ссылки, проверьте выполнение и только после этого откройте доступ.

BASEhttps://piarflow.com/v1

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

1Добавьте ботаПодключите бота в боте PiarFlow в разделе продажи трафика.
2Возьмите API key ботаБот PiarFlow → ваш бот → API key.
3Выдайте задания/sponsors возвращает ссылки для пользователя.
4Проверьте выполнение/sponsors/check — и откройте доступ.

Обзор методов

Для интеграции достаточно двух методов: получить задания и проверить их выполнение.

Основные

Получение спонсоров

POST/sponsors

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

Request Body

{
  "user_id": 123456789,
  "chat_id": 123456789,
  "max_sponsors": 5
}

string — текстовое значение, integer — целое число без дробной части.

ПараметрТипОбязательноОписание
user_idintegerДаTelegram ID пользователя
chat_idintegerДаTelegram ID чата
max_sponsorsintegerНетКоличество заданий
first_namestring | null УсловныйДа, без токенаИмя пользователя. При подключении без токена поле нужно передать; если данных нет, укажите null. При подключении с токеном не передаётся.
usernamestring | null УсловныйДа, без токенаUsername пользователя без @. При подключении без токена поле нужно передать; если данных нет, укажите null. При подключении с токеном не передаётся.
language_codestring | null УсловныйДа, без токенаЯзык интерфейса пользователя, например ru или en. При подключении без токена поле нужно передать; если данных нет, укажите null. При подключении с токеном не передаётся.
biostring | null УсловныйДа, без токенаОписание профиля пользователя в Telegram. При подключении без токена поле нужно передать; если данных нет, укажите null. При подключении с токеном не передаётся.

Response

{
  "status": "ok",
  "message": "Sponsors returned",
  "sponsors": [
    {
      "link": "https://t.me/cryptonews",
      "status": "unsubscribed",
      "price": 5
    },
    {
      "link": "https://t.me/PiarFlowBot",
      "status": "unsubscribed",
      "price": 0
    }
  ]
}

Проверка заданий

POST/sponsors/check

Request Body

{
  "user_id": 123456789,
  "links": [
    "https://t.me/cryptonews"
  ]
}
ПараметрТипОбязательноОписание
user_idintegerДаTelegram ID пользователя
linksstring[]ДаСсылки на задания

Response

{
  "status": "ok",
  "sponsors": [
    {
      "link": "https://t.me/cryptonews",
      "status": "subscribed"
    }
  ]
}

Статусы заданий

СтатусОписание
subscribedПользователь выполнил задание
unsubscribedЗадание не выполнено
not_countedВыполнение засчитано, но деньги не начислено

Авторизация

Методы /sponsors и /sponsors/check работают по API key бота. Ключ передаётся в заголовке Authorization; взять его можно в боте PiarFlow в карточке вашего бота.

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Вебхук отписок

PiarFlow отправляет POST запрос на ваш HTTPS endpoint, когда пользователь отписался от задания. URL вебхука настраивается в боте PiarFlow в разделе продажи трафика.

Ваш endpoint должен принять JSON и вернуть любой успешный HTTP статус 200-299. Тело ответа не используется, поэтому достаточно вернуть {"ok": true}.

Request от PiarFlow

{
  "tg_user_id": 12344566,
  "offer_link": "https://t.me/telegram",
  "status": "unsubscribed",
  "chat_id": -1001234567890,
  "bot_id": 1234567890
}
ПолеТипОписание
tg_user_idintegerTelegram ID пользователя, который отписался
offer_linkstringСсылка, по которой пользователь выполнил задание. Для заданий с трекингом — персональная трек-ссылка: https://t.me/PiarFlowBot?startapp=...&bot=<ваш_бот> (открывается как Mini App; bot — только видимая метка, Telegram её не передаёт) или https://piarflow.com/track?...; иначе ссылка на ресурс или канал.
statusstringСтатус события. Сейчас отправляется unsubscribed
chat_idintegerTelegram ID чата, если он известен
bot_idintegerTelegram ID бота, если он известен
testbooleanЕсть только в тестовом запросе при подключении вебхука

Как должен отвечать ваш webhook

HTTP/1.1 200 OK
Content-Type: application/json

{
  "ok": true
}

Если endpoint ответит ошибкой или не ответит за 10 секунд, PiarFlow считает отправку неуспешной.

Пример обработчика на aiohttp

from aiohttp import web


async def piarflow_webhook(request: web.Request) -> web.Response:
    payload = await request.json()
    if payload.get("test"):
        return web.json_response({"ok": True})

    if payload.get("status") == "unsubscribed":
        tg_user_id = int(payload["tg_user_id"])
        offer_link = str(payload["offer_link"])
        # Здесь можно закрыть доступ пользователю или обновить свою базу.

    return web.json_response({"ok": True})

Ошибки

При ошибке API возвращает HTTP-код из списка ниже и JSON с полем message.

{
  "status": "error",
  "message": "Invalid API key"
}
400Bad RequestНеверный запрос: нет обязательного поля или неверный формат
401UnauthorizedНе передан или неверный API key / API token
403ForbiddenПользователь не может выполнить задание (заблокировал бота, не запускал его)
404Not FoundЗадания или бот не найдены
409ConflictБот уже зарегистрирован другим пользователем
429Too Many RequestsСлишком много запросов — повторите позже
500Server ErrorВнутренняя ошибка сервера

Примеры запросов

Ниже один и тот же базовый сценарий на разных языках: запросить задания для пользователя и затем проверить выполнение. API key передаётся в заголовке Authorization.

В production храни API key в переменных окружения или secret manager. Не вставляй ключ прямо в frontend-код.

Получить задания: POST /sponsors

cURLshell
curl -X POST "https://piarflow.com/v1/sponsors" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 123456789,
    "chat_id": 123456789,
    "max_sponsors": 5
  }'
JavaScriptNode.js / fetch
const response = await fetch("https://piarflow.com/v1/sponsors", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.PIARFLOW_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    user_id: 123456789,
    chat_id: 123456789,
    max_sponsors: 5,
  }),
});

const data = await response.json();
if (!response.ok) throw new Error(data.message || "PiarFlow error");
console.log(data.sponsors);
Pythonrequests
import os
import requests

response = requests.post(
    "https://piarflow.com/v1/sponsors",
    headers={
        "Authorization": f"Bearer {os.environ['PIARFLOW_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "user_id": 123456789,
        "chat_id": 123456789,
        "max_sponsors": 5,
    },
    timeout=15,
)
response.raise_for_status()
sponsors = response.json()["sponsors"]
PHPcURL extension
$payload = json_encode([
    "user_id" => 123456789,
    "chat_id" => 123456789,
    "max_sponsors" => 5,
]);

$ch = curl_init("https://piarflow.com/v1/sponsors");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("PIARFLOW_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => $payload,
]);

$data = json_decode(curl_exec($ch), true);
curl_close($ch);

Проверить выполнение: POST /sponsors/check

cURLshell
curl -X POST "https://piarflow.com/v1/sponsors/check" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 123456789,
    "links": ["https://t.me/cryptonews"]
  }'
JavaScriptNode.js / fetch
const response = await fetch("https://piarflow.com/v1/sponsors/check", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.PIARFLOW_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    user_id: 123456789,
    links: ["https://t.me/cryptonews"],
  }),
});

const result = await response.json();
const completed = result.sponsors.every(
  (item) => item.status === "subscribed",
);
Pythonrequests
response = requests.post(
    "https://piarflow.com/v1/sponsors/check",
    headers={"Authorization": f"Bearer {os.environ['PIARFLOW_API_KEY']}"},
    json={
        "user_id": 123456789,
        "links": ["https://t.me/cryptonews"],
    },
    timeout=15,
)
response.raise_for_status()
completed = all(
    item["status"] == "subscribed"
    for item in response.json()["sponsors"]
)
PHPcURL extension
$payload = json_encode([
    "user_id" => 123456789,
    "links" => ["https://t.me/cryptonews"],
]);

$ch = curl_init("https://piarflow.com/v1/sponsors/check");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("PIARFLOW_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => $payload,
]);

$result = json_decode(curl_exec($ch), true);
curl_close($ch);

Полный пример aiogram + aiohttp

Готовая схема для бота: запросить задания, показать кнопки, проверить выполнение. API key храни в переменных окружения, а ClientSession переиспользуй.

import asyncio
import os

import aiohttp
from aiogram import Bot, Dispatcher, F, Router
from aiogram.filters import CommandStart
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message


BOT_TOKEN = os.getenv("BOT_TOKEN")
PIARFLOW_API_KEY = os.getenv("PIARFLOW_API_KEY")
PIARFLOW_API_URL = "https://piarflow.com/v1"

bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
router = Router()
pending_links: dict[int, list[str]] = {}


async def piarflow_request(
    session: aiohttp.ClientSession,
    method: str,
    path: str,
    payload: dict | None = None,
) -> dict:
    async with session.request(
        method,
        f"{PIARFLOW_API_URL}{path}",
        json=payload,
        headers={
            "Authorization": f"Bearer {PIARFLOW_API_KEY}",
            "Content-Type": "application/json",
        },
    ) as response:
        data = await response.json(content_type=None)
        if response.status >= 400:
            raise RuntimeError(data.get("message") or f"PiarFlow error {response.status}")
        return data


def sponsors_keyboard(sponsors: list[dict]) -> InlineKeyboardMarkup:
    buttons = [
        [InlineKeyboardButton(text="Выполнить задание", url=sponsor["link"])]
        for sponsor in sponsors
    ]
    buttons.append([InlineKeyboardButton(text="Проверить", callback_data="piarflow:check")])
    return InlineKeyboardMarkup(inline_keyboard=buttons)


@router.message(CommandStart())
async def start_handler(message: Message, session: aiohttp.ClientSession) -> None:
    user_id = message.from_user.id

    tasks = await piarflow_request(
        session,
        "POST",
        "/sponsors",
        {
            "user_id": user_id,
            "chat_id": message.chat.id,
            "max_sponsors": 5,
        },
    )
    sponsors = tasks.get("sponsors") or []
    if not sponsors:
        await message.answer("Сейчас нет доступных заданий.")
        return

    links = [sponsor["link"] for sponsor in sponsors]
    await message.answer(
        "Подпишись на спонсоров, затем нажми Проверить.",
        reply_markup=sponsors_keyboard(sponsors),
    )
    pending_links[user_id] = links


@router.callback_query(F.data == "piarflow:check")
async def check_handler(callback: CallbackQuery, session: aiohttp.ClientSession) -> None:
    user_id = callback.from_user.id
    links = pending_links.get(user_id, [])

    result = await piarflow_request(
        session,
        "POST",
        "/sponsors/check",
        {"user_id": user_id, "links": links},
    )
    sponsors = result.get("sponsors") or []
    if sponsors and all(item.get("status") == "subscribed" for item in sponsors):
        await callback.message.answer("Все задания выполнены. Доступ открыт.")
        pending_links.pop(user_id, None)
    else:
        await callback.message.answer("Не все задания выполнены. Проверь подписки и попробуй снова.")
    await callback.answer()


async def main() -> None:
    timeout = aiohttp.ClientTimeout(total=15)
    async with aiohttp.ClientSession(timeout=timeout) as session:
        dp.workflow_data["session"] = session
        dp.include_router(router)
        await dp.start_polling(bot)


if __name__ == "__main__":
    asyncio.run(main())

Схема работы

  1. 1Пользователь заходит в ботаНажимает /start или открывает раздел, доступ к которому вы закрыли заданиями.
  2. 2Бот запрашивает заданияВаш сервер отправляет POST /sponsors с Telegram ID пользователя.
  3. 3PiarFlow подбирает спонсоровВ ответе — список ссылок на каналы и ботов, на которые нужно подписаться.
  4. 4Бот показывает заданияКаждая ссылка — отдельная кнопка, под ними кнопка «Проверить».
  5. 5Пользователь подписываетсяПереходит по ссылкам, подписывается и нажимает «Проверить».
  6. 6Бот проверяет подпискиВаш сервер отправляет POST /sponsors/check и получает статус каждого задания.
  7. 7Доступ открытВсе задания subscribed — пускаем дальше. Если нет — снова показываем невыполненные.

Продажа трафика · Для фабрик ботов

PiarFlow Factory API

Для конструкторов и фабрик ботов: подключайте сотни ботов клиентов одним API token, управляйте их настройками и получайте отдельный API key на каждого бота. Задания и проверка работают по ключу бота, как в разделе «Для ботов».

BASEhttps://piarflow.com/v1/factory

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

1Возьмите API tokenБот PiarFlow → Личный кабинет → Интеграция API.
2Добавьте бота клиентаPOST /factory/bots вернёт его API key.
3Выдавайте заданияВ боте клиента вызывайте /sponsors с его API key.
4Следите за ботамиGET /factory/bots — список, продажи и заработок.

Авторизация

Все методы фабрики работают по API token аккаунта-владельца и видят только его ботов (чужие → 404). Токен передаётся в заголовке Authorization:

Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json

Получение заданий (/sponsors, /sponsors/check) описано во вкладке «Для ботов» и работает по API key бота.

Обзор методов

Добавить бота

POST/v1/factory/bots

Регистрирует бота по Telegram bot token и возвращает объект бота с API key. Владельцем становится аккаунт, которому принадлежит API token. Повторный вызов для своего бота обновляет его токен и настройки и не создаёт дубль.

Headers

Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json

Request Body

{
  "bot_token": "1234567890:AAExampleBotToken",
  "max_sponsors": 3,
  "reset_time": 180
}
ПараметрТипОбязательноОписание
bot_tokenstringДаТокен Telegram-бота от BotFather
max_sponsorsintegerНетСколько заданий выдавать пользователю, от 1 до 10. Если не указано — настройка бота не меняется (для нового бота 3)
reset_timeintegerНетЧерез сколько минут пользователю снова выдаются задания, от 5 до 5760. Если не указано — настройка не меняется (для нового бота 60)

Response

{
  "status": "ok",
  "message": "Bot registered",
  "bot": {
    "bot_id": 1234567890,
    "chat_id": 123456789,
    "username": "example_ref_bot",
    "title": "Example Ref Bot",
    "topic": "Финансы",
    "is_active": true,
    "max_sponsors": 3,
    "reset_time": 60,
    "sold_subs": 12,
    "not_counted": 1,
    "earned": "24.50",
    "api_key": "YOUR_TRAFFIC_BOT_API_KEY"
  }
}

Список ботов

GET/v1/factory/bots?offset=0&limit=20

Боты аккаунта, новые первыми.

ПараметрТипОбязательноОписание
offsetintegerНетСмещение, по умолчанию 0
limitintegerНетБотов на странице, от 1 до 100, по умолчанию 20
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  "https://piarflow.com/v1/factory/bots?offset=0&limit=20"

Response

{
  "status": "ok",
  "total": 120,
  "offset": 0,
  "limit": 20,
  "bots": [
    {
      "bot_id": 1234567890,
      "chat_id": 123456789,
      "username": "example_ref_bot",
      "title": "Example Ref Bot",
      "topic": "Финансы",
      "is_active": true,
      "max_sponsors": 3,
      "reset_time": 60,
      "sold_subs": 12,
      "not_counted": 1,
      "earned": "24.50",
      "api_key": "YOUR_TRAFFIC_BOT_API_KEY"
    }
  ]
}

Чтобы обойти все боты, увеличивайте offset на limit, пока он меньше total.

Информация о боте

GET/v1/factory/bots/{bot_id}
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://piarflow.com/v1/factory/bots/1234567890

Response

{
  "status": "ok",
  "bot": {
    "bot_id": 1234567890,
    "chat_id": 123456789,
    "username": "example_ref_bot",
    "title": "Example Ref Bot",
    "topic": "Финансы",
    "is_active": true,
    "max_sponsors": 3,
    "reset_time": 60,
    "sold_subs": 12,
    "not_counted": 1,
    "earned": "24.50",
    "api_key": "YOUR_TRAFFIC_BOT_API_KEY"
  }
}
ПолеТипОписание
bot_idintegerTelegram ID бота
chat_idintegerTelegram ID владельца
username, titlestringЮзернейм и имя бота
topicstringТематика бота
is_activebooleanВыдаются ли боту задания
max_sponsorsintegerСколько заданий выдаётся пользователю
reset_timeintegerИнтервал сброса заданий, минуты
sold_subsintegerЗасчитанные подписки
not_countedintegerВыполнено, но не оплачено
earnedstringЗаработок бота, ₽
api_keystringAPI key бота для /sponsors и /sponsors/check

Обновить бота

PATCH/v1/factory/bots/{bot_id}

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

Headers

Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json

Request Body

{
  "is_active": false,
  "max_sponsors": 5,
  "reset_time": 120
}
ПараметрТипОбязательноОписание
is_activebooleanНетВключить или выключить выдачу заданий. Бот на модерации, отклонённый или заблокированный включить нельзя — 403
max_sponsorsintegerНетОт 1 до 10
reset_timeintegerНетМинуты, от 5 до 5760

Response

{
  "status": "ok",
  "message": "Bot updated",
  "bot": {
    "bot_id": 1234567890,
    "chat_id": 123456789,
    "username": "example_ref_bot",
    "title": "Example Ref Bot",
    "topic": "Финансы",
    "is_active": true,
    "max_sponsors": 3,
    "reset_time": 60,
    "sold_subs": 12,
    "not_counted": 1,
    "earned": "24.50",
    "api_key": "YOUR_TRAFFIC_BOT_API_KEY"
  }
}

Сменить API key

POST/v1/factory/bots/{bot_id}/api_key

Генерирует новый API key бота. Старый ключ перестаёт работать сразу — обновите его в боте клиента. Тело запроса не нужно.

curl -X POST -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://piarflow.com/v1/factory/bots/1234567890/api_key

Response

{
  "status": "ok",
  "message": "API key changed",
  "api_key": "NEW_TRAFFIC_BOT_API_KEY"
}

Ошибки

При ошибке возвращается HTTP-код и JSON с полем message.

400Bad RequestНет обязательного поля, значение вне диапазона или неверный токен бота
401UnauthorizedНе передан или неверный API token
403ForbiddenБот заблокирован, на модерации или отклонён — включить нельзя
404Not FoundБот не найден или принадлежит другому аккаунту
409ConflictБот уже зарегистрирован другим пользователем
502Bad GatewayTelegram не подтвердил токен бота — повторите позже

Схема работы

  1. Клиент подключает бота в вашей фабрике — вы вызываете POST /factory/bots с токеном его бота и сохраняете api_key.
  2. Бот клиента запрашивает задания: POST /sponsors с его api_key.
  3. После выполнения бот вызывает POST /sponsors/check и открывает доступ.
  4. Ваш сервис периодически читает GET /factory/bots и показывает клиентам продажи и заработок.

Рекламодатели

API заказов

Всё, что есть в боте в разделе «Купить трафик → Подписчики»: создание заказов, цена, лимиты, таргетинг, запуск и остановка — теперь через API.

BASEhttps://piarflow.com/v1

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

1Возьмите API tokenБот PiarFlow → Личный кабинет → Интеграция API.
2Посмотрите справочникGET /orders/meta — темы, страны, мин. цена.
3Создайте заказPOST /orders с типом и ссылкой.
4Настройте и запуститеPATCH /orders/{id}, затем /start.

Авторизация

Каждый запрос передаёт API token из кабинета. Без токена — 401 API token is required, с неверным или сменённым — 401 Invalid API token. Доступны только ваши заказы: чужой заказ отвечает 404.

Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json

Все методы

Справочник

GET/v1/orders/meta

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

{
  "status": "ok",
  "types": ["channel", "resource", "bot"],
  "resource_check_modes": ["assumption", "track"],
  "topics": ["Треш", "Игры", "..."],
  "excludable_topics": ["Игры", "Финансы", "..."],
  "places": ["chats", "bots", "contests"],
  "countries": [{"code": "RU", "name": "Россия"}, {"code": "OTHER", "name": "Другое"}],
  "genders": ["any", "male", "female"],
  "age_categories": [{"id": 0, "name": "Все"}, {"id": 1, "name": "до 13"}, {"id": 2, "name": "14-17"}, {"id": 3, "name": "18+"}],
  "tg_premium_modes": ["any", "only"],
  "min_price": "1.20",
  "min_price_tg_premium_only": "3.50",
  "total_limit_min": 50,
  "limit_max": 10000000,
  "rate_interval_max_minutes": 1440,
  "creation_cooldown_seconds": 60,
  "checker_bot": "PiarFlow_CheckSubBot"
}

Список заказов

GET/v1/orders
{
  "status": "ok",
  "orders": [
    {"id": 4082, "type": "channel", "title": "Crypto News", "link": "https://t.me/cryptonews", "is_active": true}
  ]
}

Создать заказ

POST/v1/orders

Цена при создании подбирается автоматически по рынку, таргетинг — по умолчанию (все места показа, Беларусь/Россия/Украина). Поменять всё можно через PATCH. Деньги при создании не списываются — оплата идёт за каждого подписчика.

{
  "type": "channel",
  "link": "https://t.me/cryptonews",
  "topic": "Экономика",
  "total_limit": 1000
}
ПараметрТипОбязательноОписание
typestringДаchannel — канал или чат, bot — Telegram-бот, resource — внешний сайт
linkstringДаКанал: @name или https://t.me/.... Бот: реф-ссылка https://t.me/your_bot?start=.... Ресурс: https://...
bot_tokenstringДля botТокен бота от BotFather — нужен для проверки, что пользователь запустил бота
resource_check_modestringНетТолько для resource. assumption (по умолчанию) — ресурс показывается в паре со спонсором с проверкой и засчитывается, когда тот выполнен. track — ресурс выдаётся отдельным заданием по трек-ссылке и засчитывается через 3 секунды после её прохождения
topicstringНетТематика из meta.topics. Используется, если ваши заказы проходят без ручной модерации
total_limitintegerНетОбщий лимит подписчиков: 0 — без лимита, иначе от 50
Для канала или чата сначала добавьте бота проверки (meta.checker_bot) в администраторы с правом приглашать пользователей. Иначе ответ 409 с полем checker_bot.

Ответ — 201 и полный заказ, как в GET /orders/{id}, плюс created_for_moderation: true — заказ ждёт проверки администратором и запустится после неё. Если заказ с этой ссылкой уже есть, он запускается заново вместо создания дубля. Новый заказ можно создавать раз в 60 секунд.

Заказ

GET/v1/orders/{id}
{
  "status": "ok",
  "order": {
    "id": 4082,
    "type": "channel",
    "title": "Crypto News",
    "link": "https://t.me/cryptonews",
    "topic": "Экономика",
    "is_active": true,
    "is_awaiting_moderation": false,
    "can_run": true,
    "price": "1.77",
    "effective_price": "2.12",
    "payment_source": "deposited",
    "stats": {
      "subscribers": 312,
      "subscribers_today": 40,
      "paid_subscribers": 312,
      "spent": "552.24",
      "avg_subscriber_price": "1.77"
    },
    "limits": {
      "total_limit": 1000,
      "hourly_limit": 0,
      "rate": {"min": 3, "max": 7, "interval_minutes": 1},
      "distribute_by_days": false
    },
    "targeting": {
      "places": ["bots", "chats", "contests"],
      "gender": "any",
      "age_category": 1,
      "tg_premium": "any",
      "countries": ["BY", "RU", "UA"],
      "disabled_topics": []
    }
  }
}
ПолеОписание
priceВаша цена за подписчика
effective_priceИтоговая цена: базовая плюс фиксированные надбавки (возраст +0.20 ₽, для 18+ +0.80 ₽; каждое требование к профилю подписчика +0.20 ₽, у счётчиков +0.10 ₽ за каждый следующий уровень)
can_runХватает ли баланса для показов прямо сейчас
payment_sourceБаланс, с которого списывается оплата: deposited — пополненный, earned — заработанный
stats.subscribersСейчас подписаны через PiarFlow

Изменить заказ

PATCH/v1/orders/{id}

Передайте только поля, которые меняете. Ответ — обновлённый заказ.

{
  "price": 2.5,
  "rate": {"min": 3, "max": 7, "interval_minutes": 1},
  "total_limit": 5000,
  "gender": "female",
  "age_category": 3,
  "countries": ["RU", "KZ"],
  "places": ["chats", "bots"]
}
ПолеТипОписание
pricenumberЦена за подписчика. Ниже минимальной поднимается до meta.min_price
linkstringНовая ссылка. Для канала она должна вести в тот же канал
total_limitintegerОбщий лимит: 0 — без лимита, иначе от 50
rateobjectСкорость: от min до max подписчиков за interval_minutes (1–1440)
hourly_limitintegerМаксимум подписчиков в час, 0 — без ограничения
distribute_by_daysbooleanРавномерно распределять лимит по дням
placesstring[]Где показывать: chats, bots, contests
genderstringany, male, female
age_categoryinteger0 — все, 1 — до 13, 2 — 14-17, 3 — 18+
tg_premiumstringany или only. С only минимальная цена — meta.min_price_tg_premium_only
countriesstring[]Коды из meta.countries; пустой массив — все страны
disabled_topicsstring[]Тематики площадок из meta.excludable_topics, где не показывать

Запуск и остановка

POST/v1/orders/{id}/start
POST/v1/orders/{id}/stop

Перед запуском PiarFlow проверяет, что бот проверки всё ещё админ канала (или токен бота рабочий), лимит не исчерпан и баланса хватает. Если нет — 409 с причиной в message. Повторный запуск уже запущенного заказа ничего не меняет.

Удалить заказ

DELETE/v1/orders/{id}

Заказ останавливается и пропадает из списка. Уже выданные задания досчитываются. Ответ: {"status": "ok"}.

Ошибки

{
  "status": "error",
  "message": "Order not found"
}
400Bad RequestНеверное поле или значение — подробности в message
401UnauthorizedНет API token или он неверный
403ForbiddenАккаунт заблокирован или тип заказа временно отключён
404Not FoundЗаказ не найден или удалён
409ConflictБот проверки не админ канала, ссылка ведёт в другой канал, заказ нельзя запустить
422UnprocessableTelegram не принял канал или токен бота
429Too Many RequestsНовый заказ раньше, чем через 60 секунд — см. поле retry_after

Пример: заказ на канал

import os
import requests

API = "https://piarflow.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PIARFLOW_API_TOKEN']}"}

order = requests.post(
    f"{API}/orders",
    headers=HEADERS,
    json={"type": "channel", "link": "https://t.me/cryptonews", "total_limit": 1000},
    timeout=30,
).json()["order"]

requests.patch(
    f"{API}/orders/{order['id']}",
    headers=HEADERS,
    json={"price": 2.5, "rate": {"min": 3, "max": 7, "interval_minutes": 1}},
    timeout=30,
).raise_for_status()

requests.post(f"{API}/orders/{order['id']}/start", headers=HEADERS, timeout=30).raise_for_status()

Общее

Общие методы

Методы аккаунта: одинаково нужны и тем, кто продаёт трафик, и рекламодателям.

BASEhttps://piarflow.com/v1

Авторизация — API token из бота: Личный кабинет → Интеграция API.

Authorization: Bearer YOUR_API_TOKEN

Баланс

GET/v1/balance

Текущие балансы аккаунта, итоги за всё время и сводка по вашим ботам и чатам, которые продают трафик. Параметров нет.

curl -H "Authorization: Bearer YOUR_API_TOKEN" \
  https://piarflow.com/v1/balance

Response

{
  "status": "ok",
  "balance": {
    "deposited": "1500.00",
    "earned": "219.21",
    "available_to_withdraw": "219.21",
    "max_order_charge": "1500.00"
  },
  "totals": {
    "earned_all_time": "14207.76",
    "referral_earned": "120.00",
    "withdrawn": "13988.55",
    "spent_on_traffic": "552.24"
  },
  "bots": [
    {
      "bot_id": 1234567890,
      "username": "example_ref_bot",
      "title": "Example Ref Bot",
      "is_active": true,
      "subscribers": 60667,
      "earned": "12054.45"
    }
  ],
  "chats": [
    {
      "chat_id": -1001234567890,
      "username": "example_chat",
      "title": "Example Chat",
      "is_active": true,
      "subscribers": 812,
      "earned": "640.10"
    }
  ]
}
ПолеОписание
balance.depositedПополненный баланс — из него оплачиваются заказы
balance.earnedЗаработанный баланс — тоже может оплачивать заказы
balance.available_to_withdrawСколько можно вывести сейчас
balance.max_order_chargeСамое дорогое списание за подписчика, которое вы можете оплатить: каждое списание берётся целиком с одного баланса
totals.earned_all_timeЗаработано за всё время
totals.referral_earnedИз них — по партнёрской программе
totals.withdrawnВыведено за всё время
totals.spent_on_trafficПотрачено на покупку трафика
bots[], chats[]Ваши площадки: subscribers — приведено подписчиков, earned — заработано

Ошибки: 401 — нет API token или он неверный.