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

To send a Telegram bot message with PHP cURL, POST a JSON payload to the Bot API’s sendMessage endpoint, then check the result in four separate layers: cURL transport, HTTP status, JSON decoding, and Telegram’s ok field. A response body—or even a successful curl_exec()—does not by itself mean Telegram accepted the message.

Send a message and check Telegram’s response

This example sends chat_id and text as JSON. Set $token, $chatId, and $text from your application’s configuration and input.

<?php

$url = 'https://api.telegram.org/bot' . $token . '/sendMessage';
$payload = json_encode([
    'chat_id' => $chatId,
    'text' => $text,
], JSON_THROW_ON_ERROR);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 20,
]);

$body = curl_exec($ch);
if ($body === false) {
    $errno = curl_errno($ch);
    $error = curl_error($ch);
    curl_close($ch);
    throw new RuntimeException("cURL transport failure ($errno): $error");
}

$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

try {
    $response = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    throw new RuntimeException('Telegram response was not valid JSON', 0, $e);
}

if (!is_array($response)) {
    throw new RuntimeException('Telegram response JSON was not an object');
}

if (($response['ok'] ?? false) !== true) {
    $code = $response['error_code'] ?? 'unknown';
    $description = $response['description'] ?? 'No description supplied';
    throw new RuntimeException("Telegram API error ($code): $description; HTTP $httpStatus");
}

$message = $response['result'];

The 20-second timeout is an example setting, not a Telegram requirement. Choose timeouts, handling for non-2xx HTTP responses, and any retry policy to fit your application. Telegram supports GET and POST requests, with query-string, form-encoded, JSON, or multipart data; multipart is used for file uploads. The endpoint pattern is https://api.telegram.org/bot<token>/METHOD_NAME.

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

Check each failure layer separately

1. cURL transport result

With CURLOPT_RETURNTRANSFER enabled, curl_exec() returns the response body when the transfer completes, or false when cURL encounters a transport failure. Capture curl_errno() and curl_error() before closing the handle so you can diagnose connection or transfer problems.

2. HTTP status

Read the HTTP status independently with curl_getinfo($ch, CURLINFO_HTTP_CODE) before closing the handle. An HTTP error status such as 404 does not make curl_exec() return false; cURL can complete the transfer and return an error response body. See the PHP curl_exec() manual.

Keep the status alongside the response body. It provides useful transport-level context, but it is not a substitute for decoding Telegram’s response or checking its API-level result.

3. JSON validity and shape

Telegram normally responds with JSON, but code should not assume every body is valid JSON. Passing JSON_THROW_ON_ERROR to json_decode() makes malformed JSON raise a JsonException, which you can catch and report separately. The example also checks that the decoded value is an array before reading fields. See the PHP json_decode() manual.

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

4. Telegram’s ok field

A valid JSON response still may represent an unsuccessful API request. Telegram’s response object always includes a Boolean ok field. When it is true, the method result is in result; when it is false, inspect description for the explanation. Telegram may also return error_code and optional parameters. Its documentation cautions that error_code values may change, so avoid treating a numeric mapping as permanent. See the Telegram Bot API reference.

Log enough to diagnose failures safely

For an operational log, retain the failure layer and its useful context: cURL error number and message for transport failures; HTTP status for completed transfers; and Telegram’s error_code and description when available. If JSON decoding fails, a safely bounded excerpt of the response may help investigation, subject to your data-handling rules.

The bot token appears in the endpoint URL. Do not write that URL, or other token-bearing request details, to logs. Avoid logging message text or other sensitive payload data unless your application has a clear need and appropriate safeguards.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choose retries based on the failure

Do not retry every failure automatically. A transport failure, an HTTP error, invalid JSON, and a Telegram response with ok: false are different situations and provide different evidence. Inspect the available status and response fields, including parameters when present, then apply a policy suited to the specific operation. The Bot API documentation does not make error_code values a permanent catalogue, so avoid relying on an assumed universal mapping.

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

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.