Handle inline keyboard callback responses in Python Telegram bot

I’m working on a Telegram bot using Python and having trouble with inline keyboard callbacks. When users click my inline buttons, nothing happens even though I have callback handling code.

class BotRequestHandler(webapp2.RequestHandler):
    def post(self):
        urlfetch.set_default_fetch_deadline(45)
        request_data = json.loads(self.request.body)
        logging.info('incoming data:')
        logging.info(request_data)
        self.response.write(json.dumps(request_data))

        update_number = request_data['update_id']
        msg = request_data.get('message', {})
        msg_id = msg.get('message_id')
        timestamp = msg.get('date')
        user_text = msg.get('text')
        sender = msg.get('from')
        conversation = msg.get('chat', {})
        conv_id = conversation.get('id')
        
        try:
            query_callback = request_data['callback_query']
            button_data = query_callback.get('data')
        except:
            button_data = None
            
        btn1 = u'\U0001F680' + ' Welcome'
        btn2 = u'\U0001F4DA' + ' Begin Game'
        btn3 = u'\U0001F4DC' + ' Chapter 1'
        
        keyboard_type = 'keyboard'
        menu_keyboard = json.dumps({keyboard_type: [[btn1]], 'resize_keyboard': True})

        def send_response(text_msg=None, photo=None, video=None):
            if text_msg:
                response = urllib2.urlopen(API_URL + 'sendMessage', urllib.urlencode({
                    'chat_id': str(conv_id),
                    'text': text_msg.encode('utf-8'),
                    'parse_mode': 'markdown',
                    'reply_markup': menu_keyboard,
                })).read()
            elif photo:
                response = multipart.post_multipart(API_URL + 'sendPhoto', [
                    ('chat_id': str(conv_id)),
                    ('reply_markup': menu_keyboard),
                ], [
                    ('photo', 'pic.jpg', photo),
                ])
            return response

        try:
            if button_data == 'next_step':
                next_btn = u'\U000026A1' + ' Continue'
                keyboard_type = 'inline_keyboard'
                menu_keyboard = json.dumps({keyboard_type: [[{'text': next_btn, 'callback_data': 'step_2'}]]})
                story_text = 'Chapter content goes here...'
                send_response(u'\U000026A1' + story_text)

        except:
            if user_text and user_text.startswith('/'):
                if user_text == '/start':
                    welcome_img = Image.open('assets/welcome.jpg')
                    img_buffer = StringIO.StringIO()
                    welcome_img.save(img_buffer, 'JPEG')
                    send_response(photo=img_buffer.getvalue())

The issue is that when users press my inline keyboard button, the callback isn’t being processed properly. I have the callback_query handling code but it seems like I’m missing something in the flow. How should I structure this to properly catch and respond to inline button presses?

you’re mixing up regular messages and callback queries. when someone clicks an inline button, there’s no message in the request - just callback_query. that’s why your conv_id is undefined for button clicks - you’re pulling it from the wrong spot. for callbacks, grab the chat ID from query_callback['message']['chat']['id'] instead.

Your send_response function always grabs conv_id from the message object, but that’s None for callback queries since they don’t have the same message structure. You’ve got to handle the chat ID differently depending on whether it’s a text message or button callback. I hit this exact bug and wasted hours on it. What fixed it for me: check the request type first, then set chat ID accordingly. For callbacks, use request_data['callback_query']['message']['chat']['id']. For regular messages, keep doing what you’re doing. Don’t forget to call answerCallbackQuery after processing the callback - skip this and users get stuck with that spinning loading button forever.

Your callback handling runs inside a try-except that catches everything - that’s probably hiding your errors. I had the same issue and it was usually the callback_query access causing problems. You’re trying to grab request_data['callback_query'] but if that key doesn’t exist, it just fails silently and sets button_data to None. Check if callback_query exists before you process it. Don’t forget to call answerCallbackQuery after processing - Telegram needs that acknowledgment. Here’s what tripped me up: callback queries have a totally different structure than regular messages. They don’t have the same message fields, so grabbing conv_id from the message will fail. For button presses, get the chat ID from callback_query['message']['chat']['id'] instead.