Skip to content

sendPoll

Use this method to send a native poll. On success, the sent Message is returned.

Parameters

business_connection_idStringOptional
Unique identifier of the business connection on behalf of which the message will be sent
chat_idIntegerStringRequired
Unique identifier for the target chat or username of the target channel (in the format @channelusername). Polls can't be sent to channel direct messages chats.
message_thread_idIntegerOptional
Unique identifier for the target message thread (topic) of a forum; for forum supergroups and private chats of bots with forum topic mode enabled only
questionStringRequired✏️ FormattableminLen 1maxLen 300
Poll question, 1-300 characters
question_parse_modeStringOptional
Mode for parsing entities in the question. See formatting options for more details. Currently, only custom emoji entities are allowed
question_entitiesMessageEntity[]Optional
A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of question\parse\mode
optionsInputPollOption[]Required
A JSON-serialized list of 2-12 answer options
is_anonymousBooleanOptional
True, if the poll needs to be anonymous, defaults to True
typeStringOptional
Values:quizregular
Poll type, "quiz" or "regular", defaults to "regular"
allows_multiple_answersBooleanOptional
True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False
correct_option_idIntegerOptional
0-based identifier of the correct answer option, required for polls in quiz mode
explanationStringOptional✏️ FormattableminLen 0maxLen 200
Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing
explanation_parse_modeStringOptional
Mode for parsing entities in the explanation. See formatting options for more details.
explanation_entitiesMessageEntity[]Optional
A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of explanation\parse\mode
open_periodIntegerOptional
Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close\_date.
close_dateIntegerOptional
Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with open\_period.
is_closedBooleanOptional
Pass True if the poll needs to be immediately closed. This can be useful for poll preview.
disable_notificationBooleanOptional
Sends the message silently. Users will receive a notification with no sound.
protect_contentBooleanOptional
Protects the contents of the sent message from forwarding and saving
allow_paid_broadcastBooleanOptional
Pass True to allow up to 1000 messages per second, ignoring broadcasting limits for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance
message_effect_idStringOptional
Unique identifier of the message effect to be added to the message; for private chats only
reply_parametersReplyParametersOptional
Description of the message to reply to
Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove a reply keyboard or to force a reply from the user

Returns

On success, the Message object is returned.

GramIO Usage

ts
// Simple anonymous regular poll with 30-second timer
bot
.
command
("poll", (
ctx
) =>
ctx
.
sendPoll
({
question
: "What is your favorite runtime?",
options
: [
{
text
: "Bun" },
{
text
: "Node.js" },
{
text
: "Deno" },
],
is_anonymous
: false,
open_period
: 30,
}) );
ts
// Quiz mode with correct answer and explanation
bot
.
command
("quiz", (
ctx
) =>
ctx
.
sendPoll
({
question
: "Which HTTP status code means 'Too Many Requests'?",
options
: [
{
text
: "400" },
{
text
: "404" },
{
text
: "429" },
{
text
: "500" },
],
type
: "quiz",
correct_option_id
: 2,
explanation
: "429 is the standard rate-limit code defined in RFC 6585.",
is_anonymous
: true,
}) );
ts
// Multiple-choice poll that closes at a specific Unix timestamp
bot
.
command
("multivote", (
ctx
) =>
ctx
.
sendPoll
({
question
: "Which features do you use? (pick all that apply)",
options
: [
{
text
: "Inline keyboards" },
{
text
: "Scenes" },
{
text
: "Sessions" },
{
text
: "i18n" },
],
allows_multiple_answers
: true,
close_date
:
Math
.
floor
(
Date
.
now
() / 1000) + 300, // closes in 5 min
}) );
ts
// Reply to a message with a poll and immediately stop it (useful for previews)
bot
.
command
("preview", async (
ctx
) => {
const
msg
= await
ctx
.
replyWithPoll
({
question
: "Draft poll — closed for preview",
options
: [{
text
: "Option A" }, {
text
: "Option B" }],
is_closed
: true,
}); // msg.messageId is available on the returned MessageContext return
msg
;
});

Errors

CodeErrorCause
400Bad Request: chat not foundInvalid or inaccessible chat_id — verify the chat exists and the bot is a member
400Bad Request: POLL_OPTION_INVALIDFewer than 2 or more than 12 options provided, or an option text is empty
400Bad Request: POLL_ANSWERS_TOO_MUCHMore than 12 options in the options array
400Bad Request: POLL_QUESTION_INVALIDquestion is empty or exceeds 300 characters
400Bad Request: poll can't be sent to channel direct messages chatsPolls are not supported in channel direct message threads
400Bad Request: POLL_OPTION_CORRECT_ID_INVALIDcorrect_option_id is out of range or used without type: "quiz"
400Bad Request: can't use both open_period and close_dateopen_period and close_date are mutually exclusive
403Forbidden: bot was blocked by the userThe target user has blocked the bot
403Forbidden: not enough rightsBot lacks can_send_polls permission in the group/channel
429Too Many Requests: retry after NFlood control triggered — use the auto-retry plugin to handle this automatically

Auto-retry for 429 errors

Install the @gramio/auto-retry plugin to transparently handle flood-wait errors without manual retry logic.

Tips & Gotchas

  • 2–12 options are required. Sending fewer than 2 or more than 12 InputPollOption objects throws a POLL_OPTION_INVALID error. Each option's text must be 1–100 characters.
  • Quiz mode requires correct_option_id. Omitting it when type: "quiz" is set causes a 400 error. The ID is 0-based (first option = 0).
  • open_period and close_date are mutually exclusive. Use one or the other. Both valid ranges map to 5–600 seconds from the time of sending.
  • Polls are anonymous by default. Set is_anonymous: false to make votes public. This cannot be changed after the poll is created.
  • Multiple answers are not allowed in quiz mode. allows_multiple_answers: true is silently ignored when type: "quiz" is set.
  • Stop a poll later with stopPoll. Use ctx.stopPoll(messageId) to close a poll early and receive the final Poll object with vote counts.

See Also