Skip to main content

Python SDK for the EasyTargetBot ad-delivery API

Project description

easytarget

Python SDK for the EasyTargetBot ad-delivery API. Forward a Telegram update; the platform picks an eligible campaign, sends it into the user's chat via your bot, and bills the advertiser. You get a typed result.

English | Русский | Oʻzbekcha

Install

pip install easytarget

Usage (sync)

from easytarget import EasyTargetClient

client = EasyTargetClient(api_key="…")     # base_url defaults to the platform
result = client.show_ad(update)            # dict | aiogram Update | PTB Update
if result.sent:
    print(result.campaign_id, result.message_id)
else:
    print(result.reason)                   # DeliveryReason.NO_AD / NOT_PRIVATE / …

Usage (async, aiogram)

from aiogram.types import Message, Update
from easytarget import AsyncEasyTargetClient

client = AsyncEasyTargetClient(api_key="…")       # create once, reuse for every update

@dp.message(F.text & ~F.text.startswith("/"))     # real messages, not commands
async def on_message(message: Message, event_update: Update) -> None:
    await handle(message)                         # do your bot's actual work first
    result = await client.show_ad(event_update)   # pass the whole Update, not the Message
    if not result.sent:
        logging.debug("no ad shown: %s", result.reason)

@dp.shutdown()
async def on_shutdown():
    await client.aclose()                         # close the HTTP pool on shutdown

Pass the whole Update, not the Message. aiogram injects the parent update into every handler as event_update — declare that parameter and the SDK forwards the full update, including the update_id the platform requires. Handing it message instead drops update_id, and the request is rejected (DeliveryReason.MISSING_UPDATE_ID).

Only api_key is required. base_url defaults to the platform endpoint shown alongside your API key; pass base_url=… only if you were given a dedicated URL or are pinned to an older one. Both fall back to the EASYTARGET_API_KEY / EASYTARGET_BASE_URL environment variables.

Best practices — show ads on real actions, don't spam

⚠️ Spamming ads can get your bot banned. Showing an ad on every update, on bot commands (/start, /help, …), or pumping a low-traffic / artificial-traffic bot violates Telegram's rules and EasyTargetBot's policy and will get the bot flagged and removed. Show one ad in response to a genuine user action, and let the platform's eligibility filter + per-user frequency cap do the rest.

Good — tied to a real action, deliberate:

# After the user finishes something meaningful, offer a single ad.
@dp.message(Command("results"))
async def on_results(message: Message, event_update: Update):
    await send_results(message)
    await client.show_ad(event_update)     # ✅ one ad, on a real action

Avoid — spammy placement that risks a ban:

@dp.message()                              # fires on EVERYTHING, including commands
async def on_any(message: Message, event_update: Update):
    await client.show_ad(event_update)     # ❌ an ad on every message = spam
    await client.show_ad(event_update)     # ❌ never show several in a row

The server already filters out commands, non-private chats, and bots, and caps how often a given user is shown an ad — but those are a safety net, not a license to call show_ad indiscriminately. Be intentional about where you place it.

Results & errors

show_ad returns an AdResult(sent, campaign_id, unique, message_id, reason, raw). When sent is False, reason is a DeliveryReason — a filter reason (UNSUPPORTED_UPDATE, NO_USER, BOT_SENDER, NOT_PRIVATE, COMMAND) or a delivery reason (BOT_NOT_SERVING, FREQUENCY_CAP, NO_AD, SEND_FAILED).

It raises InvalidApiKey / APIError on a rejected request and RequestError on a network/timeout failure (all subclasses of EasyTargetError). APIError carries status_code, code, message, detail, and retry_after.

Configuration

from easytarget import EasyTargetClient, RetryPolicy

client = EasyTargetClient(
    api_key="…",
    base_url="https://easytarget.jakhongir.dev",  # optional override
    timeout=10.0,                                # per-request timeout (seconds)
    retries=2,                                   # extra attempts after the first
)

Retries are deliberately conservative. Because /api/v1/ad/send delivers an ad and charges the advertiser, the SDK only retries failures where the request provably never reached the server (connection/pool errors) plus 429 / 503 responses (honoring Retry-After, with exponential backoff + jitter). It never retries read/write timeouts or other 5xx, which could mean the ad was already sent — retrying those would double-deliver and double-charge. Set retries=0 to disable.

For full control pass a RetryPolicy:

client = EasyTargetClient(
    api_key="…",
    retry_policy=RetryPolicy(max_retries=3, backoff_factor=0.5, max_backoff=30.0),
)

Bring your own httpx client (proxies, custom TLS, connection-pool tuning); the SDK still applies auth and the request path, and will not close a client you own:

import httpx
from easytarget import EasyTargetClient

http = httpx.Client(proxy="http://localhost:8080", timeout=5.0)
client = EasyTargetClient(api_key="…", http_client=http)

The package ships type hints (py.typed) for mypy/pyright.

Logging

Both clients log each ad request — on by default, to the stdlib easytarget logger. Logging is privacy-safe: only the update_id and the outcome are logged, never the update payload or message text.

import logging
logging.basicConfig(level=logging.INFO)        # see the default "easytarget" logger

client = EasyTargetClient(api_key="…")
# INFO  easytarget: ad sent update_id=85123456 campaign=cmp_42 unique=True
# INFO  easytarget: ad not sent update_id=85123457 reason=frequency_cap
# WARNING easytarget: retry 1/2 after 503 update_id=85123458
# ERROR easytarget: request failed update_id=85123459 [401] invalid_api_key

The logger is pluggable — pass any object with debug/info/warning/error methods. Stdlib logging.Logger and loguru's logger both work directly, no adapter needed:

from loguru import logger
client = EasyTargetClient(api_key="…", logger=logger)              # loguru
client = EasyTargetClient(api_key="…", logger=logging.getLogger("mybot"))  # stdlib

Turn it off entirely with logging_enabled=False (wins even if a logger is passed). Request start is logged at DEBUG, outcomes at INFO, retries at WARNING, and final failures at ERROR. The LoggerLike protocol is exported if you want to type your own logger. Both options work identically on AsyncEasyTargetClient.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

easytarget-0.2.0b1.tar.gz (155.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

easytarget-0.2.0b1-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file easytarget-0.2.0b1.tar.gz.

File metadata

  • Download URL: easytarget-0.2.0b1.tar.gz
  • Upload date:
  • Size: 155.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.19

File hashes

Hashes for easytarget-0.2.0b1.tar.gz
Algorithm Hash digest
SHA256 db1db2ee48b8709e4f32e9cac6db0cf4f7806a051dd9e5645bce37185d76fe1b
MD5 9675d1e8c5b1bd1bf5aec34886cb0866
BLAKE2b-256 9982315fcd0cc1748602a8339431e1996c8a6053771ce807a6dfaca2527e0634

See more details on using hashes here.

File details

Details for the file easytarget-0.2.0b1-py3-none-any.whl.

File metadata

File hashes

Hashes for easytarget-0.2.0b1-py3-none-any.whl
Algorithm Hash digest
SHA256 815fe60979ee56ad8741193781bff1259ebf5b56b7c0866be8c3dc3b804b9479
MD5 2ee3477da04777389565b95f43a49032
BLAKE2b-256 88e07d7bf60190376b352385d26a10c33540e7d4fdc3d44e177e90464e10d5cb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page