Suggested Posts

Bot API 9.2 — August 2025

A suggested post is a paid pitch. Someone proposes a message to a channel, naming a price and a publication time; the channel’s admins approve it, and the post goes out at that time with the payment settled. Declining costs nothing.

Suggestions travel through the channel’s direct messages chat, so this feature and Direct Messages in Channels are two halves of the same mechanism.


Proposing a post

Every send_* method takes suggested_post_parameters. Passing it turns an ordinary send into a proposal:

from datetime import datetime, timedelta
from pyrogram.types import SuggestedPostParameters, SuggestedPostPriceStar

await app.send_photo(
    chat_id=channel_direct_messages_id,
    photo="ad.jpg",
    caption="Our new collection — 20% off this week",
    suggested_post_parameters=SuggestedPostParameters(
        price=SuggestedPostPriceStar(star_count=500),
        send_date=datetime.now() + timedelta(days=1),
    ),
)

Price is a union, matching how Telegram settles the payment: SuggestedPostPriceStar for Stars, and SuggestedPostPriceTon with toncoin_nano_count for TON. Omit price for a free suggestion, and omit send_date to leave the timing to the admin.

Approving and declining

@app.on_message()
async def review(client, message):
    if not message.suggested_post_info:
        return

    info = message.suggested_post_info

    if info.price and info.price.star_count >= 500:
        await app.approve_suggested_post(
            chat_id=message.chat.id,
            message_id=message.id,
        )
    else:
        await app.decline_suggested_post(
            chat_id=message.chat.id,
            message_id=message.id,
            comment="Below our minimum rate.",
        )

approve_suggested_post() takes an optional send_date — pass it to publish at a different time than the one proposed, omit it to accept the proposed one. It must be within 30 days.

decline_suggested_post() takes an optional comment that reaches the person who proposed it.

Following the outcome

A suggestion’s state is on the message itself, in SuggestedPostInfo:

info = message.suggested_post_info

print(info.state)      # PENDING, APPROVED or DECLINED
print(info.send_date)  # when it will be published
print(info.price)

Telegram also posts service messages as the suggestion moves along, and wzgram parses each into its own field on Message: suggested_post_approved, suggested_post_declined, suggested_post_paid, suggested_post_refunded and suggested_post_approval_failed — the last one being an approval that could not be charged.

Gotchas

  • Send the proposal to the channel’s direct messages chat, not to the channel. Sending to the channel itself either fails or posts immediately, depending on your rights.

  • price is a union, not a number. SuggestedPostPriceStar and SuggestedPostPriceTon carry different fields, so check which one you have before reading star_count.

  • An approved post is not a sent post. It is scheduled; the payment settles when it publishes, which is when suggested_post_paid arrives.