Polls¶
Bot API 9.6 — April 2026
Polls grew from “pick one of ten strings” into something closer to a form: quizzes can have more than one right answer, options can be added after the poll is live, and results can stay hidden until it closes.
Sending a poll¶
from pyrogram import enums
await app.send_poll(
chat_id="me",
question="Which layer are we on?",
options=["226", "227", "228"],
type=enums.PollType.QUIZ,
correct_option_ids=[2],
explanation="228 since July.",
open_period=600,
)
Options are plain strings or InputPollOption objects. The question and
the explanation accept a FormattedText if you want entities in them.
Multiple correct answers¶
correct_option_ids takes a list, so a quiz can accept several answers as right. The older
single-valued correct_option_id still works and is folded into the list for you.
await app.send_poll(
chat_id="me",
question="Which of these are async?",
options=["send_message", "rnd_id", "get_chat"],
type=enums.PollType.QUIZ,
correct_option_ids=[0, 2],
)
Poll behaviour flags¶
send_poll carries the switches that decide how the poll behaves once it is live:
allows_multiple_answers— a regular poll where voters pick several optionsallows_revoting— a voter may change their mindshuffle_options— each voter sees a different orderallow_adding_options— voters may append options of their ownhide_results_until_closes— nobody sees the tally until the poll endsmembers_only— only subscribers may votecountry_codes— restrict voting by countryopen_period/close_date— close it automatically
Voting and reading results¶
await app.vote_poll(chat_id, message_id, options=[0])
await app.retract_vote(chat_id, message_id)
poll = await app.get_poll_results(chat_id, message_id)
for option in poll.options:
print(option.text, option.voter_count, f"{option.vote_percentage}%")
await app.stop_poll(chat_id, message_id)
get_poll_stats() returns who voted for what in a non-anonymous poll,
and add_poll_option() appends an option to a live poll that allows it.
New poll votes reach a bot through on_poll().
Gotchas¶
correct_option_idsmust be a list ofint. It was a bytes field before layer 228 and a leftoverbytesthere fails deep inside serialisation, not at the call.A quiz with no
explanationis legal but wastes the one moment the voter is paying attention to why they were wrong.send_pollacceptsexplanation_media,descriptionanddescription_media, and currently drops them: nothing in this layer carries a poll description, and the solution media field is not wired up. They are accepted so code written against the Bot API shape does not break, not because they do anything.Option media has the same limitation:
InputPollOptiontakes amedia, but only its text reaches the wire.