answerShippingQuery
Returns: TrueOfficial docs ↗
If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned.
Parameters
shipping_query_idStringRequiredUnique identifier for the query to be answered
okBooleanRequiredPass True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible)
Required if ok is True. A JSON-serialized array of available shipping options.
error_messageStringOptionalRequired if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable"). Telegram will display this message to the user.
Returns
On success, True is returned.
GramIO Usage
typescript
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string);
// Handle shipping_query — provide available shipping methods
bot.shippingQuery(async (ctx) => {
const query = ctx.shippingQuery;
const address = query.shipping_address;
// Determine if we can ship to this address
const canShip = await checkShippingAvailability(address.country_code);
if (canShip) {
await ctx.answerShippingQuery({
ok: true,
shipping_options: [
{
id: "standard",
title: "Standard Shipping (5-7 days)",
prices: [{ label: "Shipping", amount: 499 }], // amount in smallest currency unit
},
{
id: "express",
title: "Express Shipping (1-2 days)",
prices: [{ label: "Express shipping", amount: 1499 }],
},
],
});
} else {
await ctx.answerShippingQuery({
ok: false,
error_message: `Sorry, we don't deliver to ${address.country_code} at this time.`,
});
}
});
// Direct API call
await bot.api.answerShippingQuery({
shipping_query_id: "query_id_here",
ok: true,
shipping_options: [
{
id: "free",
title: "Free Shipping",
prices: [{ label: "Free shipping", amount: 0 }],
},
],
});Errors
| Code | Error | Cause |
|---|---|---|
| 400 | Bad Request: query is too old and response timeout expired or query id is invalid | Did not answer within 10 seconds, or query was already answered — respond immediately upon receiving the update |
| 400 | Bad Request: shipping_options is required when ok is True | Approved shipping but omitted shipping_options — always provide at least one option when ok: true |
| 400 | Bad Request: error_message is required when ok is False | Declined shipping without a user-facing explanation — always provide error_message when ok: false |
| 400 | Bad Request: SHIPPING_OPTIONS_EMPTY | shipping_options is an empty array — must contain at least one option |
| 429 | Too Many Requests: retry after N | Rate limit hit — check retry_after, use auto-retry plugin |
TIP
Use GramIO's auto-retry plugin to handle 429 errors automatically.
Tips & Gotchas
- This method is only triggered when
is_flexible: truewas set insendInvoice. Without that flag, Telegram handles shipping internally and never sends ashipping_queryupdate to your bot. - Answer within 10 seconds. Like all query-answer methods in the payments flow, shipping queries expire quickly. Run shipping availability checks ahead of time or use cached results when possible.
- Prices are in the smallest currency unit.
amountinShippingOption.pricesis in cents for USD, pence for GBP, etc. A value of499means $4.99 for USD. - Provide multiple shipping options when possible. Showing standard and express options gives users a real choice and increases conversion. Each option's ID is sent back in the pre-checkout query for order fulfillment.
error_messageis shown directly to the user. Write a clear, actionable explanation (e.g., mention supported countries). Avoid generic error messages like "delivery not available" with no context.- The
shipping_addressin the query is user-entered. Always validate country code and postal format server-side — users can enter partial or incorrect addresses.
See Also
sendInvoice— Send the invoice that triggers this flow (withis_flexible: true)answerPreCheckoutQuery— Final payment confirmation step (sent after shipping)ShippingQuery— The update object containing the user's addressShippingOption— Structure for a single shipping option with prices