Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Use Telegram’s getUpdates method to run a bot from a local PHP CLI process without exposing a public webhook endpoint. The process makes outbound HTTPS requests, waits for updates, handles each update, and advances an offset so Telegram confirms updates already received. This tutorial uses PHP cURL and defensive response handling; the code is an implementation example, not a tested deployment.

How does Telegram long polling work?

Telegram offers two mutually exclusive ways to deliver bot updates: long polling with getUpdates, where your process asks Telegram for updates, and webhooks with setWebhook, where Telegram sends updates to your HTTPS URL. For local development without a publicly reachable endpoint, polling is the straightforward fit: your machine initiates outbound HTTPS requests to the Bot API.

Telegram describes getUpdates as the method for receiving updates using long polling. Its timeout parameter is measured in seconds. The default is zero, which produces short polling; Telegram says that mode should only be used for testing. Set a positive timeout so the request can wait for updates rather than repeatedly checking immediately. See the live Telegram Bot API documentation for current method parameters and update types; the API reference may change over time.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A webhook is a different setup: Telegram must be able to reach a configured HTTPS URL. Telegram currently lists webhook ports 443, 80, 88, and 8443, with certificate and host requirements described in its Bots FAQ. Polling avoids that inbound endpoint requirement, but it does not eliminate the need for your local process to reach Telegram over the network.

What do you need before running the PHP bot?

  • A bot token created through Telegram’s @BotFather flow, described in the Bots FAQ.
  • PHP CLI with the cURL extension enabled. The PHP cURL examples document the request workflow using curl_init(), curl_setopt(), and curl_exec().
  • Network access from the local machine to Telegram’s Bot API over HTTPS.

Keep the token outside committed source code. It appears in the Bot API endpoint path, so avoid printing or logging request URLs. For a local shell session, provide it as an environment variable rather than embedding it in the script.

How do you implement getUpdates in PHP?

Save the following as bot.php. It reads the token from the TELEGRAM_BOT_TOKEN environment variable, calls the Bot API with query parameters, checks cURL and HTTP errors, validates the JSON response, processes message updates, and only advances the offset after a handler completes without throwing an exception.

<?php
declare(strict_types=1);

$token = getenv('TELEGRAM_BOT_TOKEN');
if ($token === false || $token === '') {
    fwrite(STDERR, "Set TELEGRAM_BOT_TOKEN before starting the bot.n");
    exit(1);
}

$apiBase = 'https://api.telegram.org/bot' . $token . '/';
$offset = 0;
$longPollSeconds = 30;

/** Make one Bot API request and return its decoded JSON object. */
function telegramRequest(string $url, int $longPollSeconds): array
{
    $ch = curl_init($url);
    if ($ch === false) {
        throw new RuntimeException('Could not initialize cURL.');
    }

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 5,
        // Must exceed the Telegram long-poll wait, with room for network delay.
        CURLOPT_TIMEOUT => $longPollSeconds + 15,
    ]);

    $body = curl_exec($ch);
    if ($body === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException('Telegram request failed: ' . $error);
    }

    $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($status < 200 || $status >= 300) {
        throw new RuntimeException('Telegram returned HTTP status ' . $status);
    }

    try {
        $result = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
    } catch (JsonException $e) {
        throw new RuntimeException('Telegram returned invalid JSON.', 0, $e);
    }

    if (!is_array($result) || ($result['ok'] ?? false) !== true || !isset($result['result']) || !is_array($result['result'])) {
        $description = is_array($result) ? ($result['description'] ?? 'Unexpected Bot API response.') : 'Unexpected Bot API response.';
        throw new RuntimeException((string) $description);
    }

    return $result;
}

/** Replace this with your application logic. Throw if processing did not succeed. */
function handleUpdate(array $update): void
{
    if (isset($update['message']) && is_array($update['message'])) {
        $message = $update['message'];
        $text = $message['text'] ?? '';
        // Add application logic here. Do not assume every message has text.
        if (is_string($text)) {
            fwrite(STDOUT, "Received a text message.n");
        }
    }
}

while (true) {
    try {
        $query = http_build_query([
            'offset' => $offset,
            'timeout' => $longPollSeconds,
            'limit' => 100,
        ]);
        $response = telegramRequest($apiBase . 'getUpdates?' . $query, $longPollSeconds);

        foreach ($response['result'] as $update) {
            if (!is_array($update) || !isset($update['update_id']) || !is_int($update['update_id'])) {
                fwrite(STDERR, "Skipping an update with an invalid shape.n");
                continue;
            }

            try {
                handleUpdate($update);
            } catch (Throwable $e) {
                // Do not move the offset past a failed update; it can be returned again.
                fwrite(STDERR, 'Update processing failed: ' . $e->getMessage() . "n");
                break;
            }

            $offset = $update['update_id'] + 1;
        }
    } catch (Throwable $e) {
        // Keep the loop alive for transient request failures; avoid a tight retry loop.
        fwrite(STDERR, 'Polling error: ' . $e->getMessage() . "n");
        sleep(2);
    }
}

The 5-second connect timeout and the total timeout are illustrative settings, not universal requirements. Telegram’s official PHP HelloBot sample uses a 5-second cURL connect timeout and a 60-second total timeout. In this example, the total timeout is set to the long-poll wait plus 15 seconds; choose a margin that suits your network and ensure the HTTP client does not time out before Telegram’s wait ends. The PHP manual also shows cURL initialization, request options, execution, and error checks.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do you start the local polling process?

  1. In a terminal, set the token for the current shell session: export TELEGRAM_BOT_TOKEN='your-token'. Avoid sharing the terminal output or shell history containing a real token.
  2. From the directory containing bot.php, start PHP CLI: php bot.php.
  3. Send a message to the bot in Telegram. The example prints a short notice when it receives a text message; put the actual reply or other application behavior inside handleUpdate().
  4. Stop the process with your terminal’s interrupt command when finished. Let an outstanding long-poll request return if convenient; this example does not install special signal handlers.

Why is Telegram returning the same updates again?

Updates are confirmed when a later getUpdates request uses an offset higher than their update_id. Telegram’s FAQ explains that updates with IDs less than or equal to the offset are marked confirmed and will no longer be returned. Recalculate the offset from received updates and pass it on the next request; using update_id + 1 confirms that update.

The sample advances the offset after each successful handler call, so a processing exception leaves that update eligible to be returned again. This favors retrying over silently confirming work that failed, but it means your handler should be designed for possible repeated processing if it performs side effects. If a process stops after doing work but before sending the next offset, Telegram can deliver that update again.

What if getUpdates returns no updates or fails?

  • An existing webhook is configured: getUpdates will not work while a webhook is set. Check the current state with getWebhookInfo, then remove the webhook using deleteWebhook before polling. The methods and behavior are documented in the Bot API reference.
  • The token or connection is wrong: confirm the token is the one for this bot and that the local machine can make outbound HTTPS requests. The code reports transport errors, non-success HTTP status codes, malformed JSON, and Bot API errors to standard error.
  • Expected update types are missing: inspect the allowed_updates setting. It controls which update types are delivered. An empty list includes all types except chat_member, message_reaction, and message_reaction_count; when omitted, Telegram reuses the previous setting. A changed setting does not affect updates created before that call. Consult the live Bot API documentation for current details.
  • The process was offline too long: Telegram retains incoming updates until received, but no longer than 24 hours. The getUpdates batch limit is 1–100 updates per call and defaults to 100.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How much does getUpdates return at a time?

The limit parameter accepts 1 to 100 updates and defaults to 100, according to the Telegram Bot API reference. The example explicitly requests 100. If a response contains a batch, process its updates and carry the advanced offset into the next call so already handled updates are confirmed.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.