Skip to main content

Minimal Telegram alerting framework for bots and scripts.

Project description

PyPI Downloads Python PyPI version CI License

Pingram

Send Telegram messages with one line of Python. No webhooks. No bloat. Just pings.

Pingram is an ultra-lightweight Python wrapper for sending outbound Telegram messages via your bot. It's designed as a cost-free alternative to email and SMS, focused on one-way "ping"-style messaging — ideal for alerts, reports, logs, and automated notifications.

Looking for a minimal alternative to python-telegram-bot? Pingram avoids the event loop, handlers, and 7.8MB install size, focusing solely on outbound pings with just one method per use case.


Lightweight by Design

Pingram prioritizes size, speed, and clarity. Designed to be imported and deployed instantly.

Package Size
Pingram (core) ~20 KB
Pingram + AsyncPingram ~21 KB
Pingram + httpx ~800 KB
python-telegram-bot ~7.8 MB

Result:

  • Pingram is over 9× smaller than PTB even with httpx included.
  • Pingram core is ~390× smaller than PTB alone.
  • That's a ~90% reduction with dependencies, and ~99.7% without.

Perfect for:

  • Minimal Docker containers
  • Constrained environments
  • Clean, single-purpose automation

Features

  • Send messages, photos, documents, audio, and video
  • Direct method calls: bot.message(), bot.send_photo(), etc.
  • Sync and async clientsPingram (sync) + AsyncPingram (async) with identical APIs
  • Minimalistic architecture, no listeners or webhooks
  • Built on httpx (sync + async)
  • Retries with backoff on transient failures (429, 5xx, network errors)
  • Typed exception hierarchy you can opt into
  • No webhook setup required

Who is it for?

  • Developers who want zero-setup Telegram alerts
  • Sysadmins replacing email/SMS for cron/CI jobs
  • Raspberry Pi or IoT projects needing compact tooling
  • Traders, scrapers, and bots that need lightweight push

Installation

pip install pingram

Requires Python 3.9 or newer.

Quickstart

from pingram import Pingram

bot = Pingram(token="<BOT_TOKEN>")
bot.me()

A simple method for testing your bot's authentication token. Requires no parameters. Returns basic information about the bot in form of a User object. https://core.telegram.org/bots/api#getme

Since every high-level api function returns a httpx.Response object, you can append the end of a function call using .text to show the raw HTTP response instead of the status code.

bot.message(chat_id=123456789, text="Hello Friend").text

This call returns a success or error message from the Telegram API.

Async usage

For asyncio applications, FastAPI handlers, Jupyter notebooks, or anywhere you want to fire multiple pings concurrently, use AsyncPingram. It mirrors the sync API exactly — same methods, same kwargs, same typed errors.

import asyncio
from pingram import AsyncPingram

async def main():
    async with AsyncPingram(token="<BOT_TOKEN>") as bot:
        await asyncio.gather(
            bot.message(chat_id=123, text="step 1 done"),
            bot.message(chat_id=123, text="step 2 done"),
            bot.send_photo(chat_id=123, path="chart.png"),
        )

asyncio.run(main())

Three supported lifecycle shapes:

# 1. async with (recommended — guarantees client cleanup)
async with AsyncPingram(token="...") as bot:
    await bot.message(chat_id=123, text="hi")

# 2. manual aclose (parity with how Pingram is used)
bot = AsyncPingram(token="...")
try:
    await bot.message(chat_id=123, text="hi")
finally:
    await bot.aclose()

# 3. fire-and-forget (allowed but emits a resource warning at GC time)
bot = AsyncPingram(token="...")
await bot.message(chat_id=123, text="hi")

Retries, typed errors (PingramError, TelegramAPIError, RateLimitError, TransportError), and per-call _raise / _retries overrides all work identically to the sync Pingram. See the Error handling and Retry policy sections below.

Media Examples

All media-sending methods accept both local file paths and direct URLs. Ensure URLs are direct links (i.e. ending in .jpg, .mp4, .pdf) and serve correct Content-Type headers.

Send Photo

bot.send_photo(
    chat_id=123456789,
    path="https://example.com/image.jpg",
    caption="Test Photo"
)

From a local file:

bot.send_photo(
    chat_id=123456789,
    path="photo.jpg",
    caption="Local Image"
)

Send Document

bot.send_doc(
    chat_id=123456789,
    path="https://example.com/file.pdf",
    caption="Monthly Report"
)

From a local file:

bot.send_doc(
    chat_id=123456789,
    path="report.pdf",
    caption="Monthly Report"
)

Send Audio

bot.send_audio(
    chat_id=123456789,
    path="https://www.myinstants.com//media/sounds/hello-friend-mr-robot.mp3",
    caption="Greetings."
)

From a local file:

bot.send_audio(
    chat_id=123456789,
    path="audio.mp3",
    caption="Shower Thoughts"
)

Send Video

bot.send_video(
    chat_id=123456789,
    path="https://yourdomain.com/video.mp4",  # must be direct link to .mp4
    caption="Security Footage"
)

From a local file:

bot.send_video(
    chat_id=123456789,
    path="stranger.mp4",
    caption="Security Footage"
)

Additional Request Data

Including additional data such as a caption, description or any other key, value types supported by the Telegram API can be passed through any API call simply by including it in the params of the function.

bot.send_video(
    chat_id=123456789,
    path="hamsters.mp4",
    caption="Playful Hamsters",
    has_spoiler=True
)

The has_spoiler parameter is a native Telegram option. It must be passed as a bool.

Error handling

By default, pingram preserves the 0.3.x contract: methods return the final httpx.Response even if it carries a non-2xx status, and transport errors propagate as their underlying httpx exceptions.

Opt into typed exceptions with raise_on_error=True:

from pingram import Pingram, PingramError, RateLimitError, TelegramAPIError, TransportError

bot = Pingram(token="<BOT_TOKEN>", raise_on_error=True, retries=5)

try:
    bot.message(chat_id=123, text="hello")
except RateLimitError as exc:
    # exc.retry_after carries Telegram's suggested delay if provided
    ...
except TelegramAPIError as exc:
    # non-2xx response that retries couldn't fix
    print(exc.status_code, exc.description)
except TransportError as exc:
    # network/transport failure after retries exhausted
    ...
except PingramError:
    # catch-all base if you prefer
    ...

You can also override per call:

bot.message(chat_id=123, text="hello", _raise=True, _retries=0)

_raise and _retries kwargs are stripped before the payload is forwarded to Telegram.

MarkdownV2 escaping

Telegram's MarkdownV2 parse mode needs backslash-escaping for a long list of characters. Pingram ships three small helpers so you don't have to remember which is which:

from pingram import (
    escape_markdown_v2,
    escape_markdown_v2_code,
    escape_markdown_v2_link_url,
)

# Plain text body
bot.message(
    chat_id=123,
    text=f"⚠️ *{escape_markdown_v2(hostname)}* is down",
    parse_mode="MarkdownV2",
)

# Inside an inline code span (only backtick and backslash need escaping)
bot.message(
    chat_id=123,
    text=f"Run `{escape_markdown_v2_code(command)}` to retry",
    parse_mode="MarkdownV2",
)

# Inside a link URL (only `)` and backslash need escaping)
bot.message(
    chat_id=123,
    text=f"[details]({escape_markdown_v2_link_url(url)})",
    parse_mode="MarkdownV2",
)

The helpers are pure functions — no await, no client needed.

Retry policy

Condition Behaviour
2xx / 3xx Return immediately
400, 401, 403, 404 Fail-fast — no retry
429 Retry; honour parameters.retry_after if present, else exponential backoff
5xx Retry with exponential backoff
Connection error / timeout Retry with exponential backoff

Set retries=0 for pre-0.4.0 behaviour (single attempt, no retries).

Tests

Pingram ships a two-tier test suite:

  • Unit tests (tests/unit/) — fast, deterministic, mocked at the httpx transport layer with respx. These run on every PR via GitHub Actions across Python 3.9–3.13.
  • Integration tests (tests/integration/) — opt-in, real-API tests that send actual messages. Useful for edge-case detection (rate limits, content-type mismatches). Require BOT_TOKEN + CHAT_ID env vars and are run from main only.

To run them locally:

# Unit tests (no creds required)
pytest

# Integration tests (real-API, .env with BOT_TOKEN + CHAT_ID)
pytest tests/integration -m integration

Roadmap

  • Retry and error handling
  • Package tests and CI integration
  • Async mode (AsyncPingram)
  • Message templating engine
  • Std input/message collectors
  • Webhook-to-Telegram bridge

Maintained — issues and PRs welcome.

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

pingram-0.5.1.tar.gz (16.5 kB view details)

Uploaded Source

Built Distribution

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

pingram-0.5.1-py3-none-any.whl (15.1 kB view details)

Uploaded Python 3

File details

Details for the file pingram-0.5.1.tar.gz.

File metadata

  • Download URL: pingram-0.5.1.tar.gz
  • Upload date:
  • Size: 16.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pingram-0.5.1.tar.gz
Algorithm Hash digest
SHA256 1013d923b21ad8ed50159197aa5ed0375dbe8dcc9859ada90dd8772e6f210843
MD5 7d6dc7f6b52b4a65ce0d7bddd342846c
BLAKE2b-256 bb9eb2981cb95d2800278f3c7b552ba08c69a7932216650bd9ac6b833d3559d2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pingram-0.5.1.tar.gz:

Publisher: release.yml on zvizr/pingram

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pingram-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: pingram-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 15.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pingram-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 84f4fb1d1174fdb670f03bb0813bdeb7ec7e32deec3bcae01c07b4dc6487c56d
MD5 4cd9d1cbe1da2621657161149519c2f2
BLAKE2b-256 807949fb301614abc378c236f45810d524f32a504fdae0903e5defa2e74c5098

See more details on using hashes here.

Provenance

The following attestation bundles were made for pingram-0.5.1-py3-none-any.whl:

Publisher: release.yml on zvizr/pingram

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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