Getting 400 error when sending custom keyboard through Telegram bot sendMessage method

I’m having trouble with my Telegram bot. When I try to send a message with a custom keyboard using the sendMessage API, I keep getting an HTTP 400 error. Here’s the JSON payload I’m sending:

{
  "chat_id": 987654321,
  "text": "Choose your preferred language",
  "reply_markup": {
    "keyboard": [
      [{"text": "Spanish"}, {"text": "German"}],
      [{"text": "Italian"}, {"text": "Russian"}],
      [{"text": "Portuguese"}, {"text": "Chinese"}]
    ]
  }
}

The request fails every time with a 400 status code. I’ve double-checked the chat ID and the bot token, but I can’t figure out what’s wrong with the keyboard structure. Has anyone encountered this issue before? What could be causing this error?

Had this exact problem last month - drove me crazy for hours. Your keyboard structure’s missing the resize_keyboard parameter. Telegram’s API needs this field set explicitly or it throws a 400 error. Add "resize_keyboard": true to your reply_markup object. Also check you’re sending the Content-Type header as application/json - I’ve seen the API reject valid JSON when this header’s missing or wrong. Test your chat_id first with a simple text message without any keyboard markup to make sure that’s not the issue.

Double-check your bot token in the URL - it should be https://api.telegram.org/bot<YOUR_BOT_TOKEN>/sendMessage. I’ve made this mistake and got 400 errors. Also try wrapping your payload in another layer - the API sometimes expects different nesting for reply_markup depending on your client library.

Had the same issue with my first bot. Your JSON looks fine, but Telegram’s API is picky about character encoding. Make sure you’re sending UTF-8, especially with language names that might have special characters. Also double-check you’re actually using POST - I wasted hours debugging once because I was accidentally sending GET requests. Try adding “one_time_keyboard”: false to your reply_markup too. Some API versions need that parameter explicitly set.