How to Upload Files and Reuse File IDs in Telegram Bot API with PHP

When building Telegram integrations in PHP, sending media and documents is a common requirement. While sending plain text messages is straightforward, transmitting files requires understanding how the Telegram Bot API handles binary payloads, file caching, and size limitations.

This guide demonstrates how to upload local files to Telegram using CURLFile (which sends a multipart/form-data request), how to extract and reuse the resulting file_id to save bandwidth, and when to use public URLs instead of direct uploads. We do not claim to build a complete media management system; this is a clean, production-ready implementation of Telegram's file transfer mechanics.

Uploading a Local File via multipart/form-data

To send a local file from your server to Telegram, you must use a POST request with the multipart/form-data content type. In PHP, this is achieved by passing an associative array to CURLOPT_POSTFIELDS containing a CURLFile object.

Here is a complete, robust script to upload a local PDF document using sendDocument:

<?php

$token = getenv('TELEGRAM_BOT_TOKEN');
$chatId = getenv('TELEGRAM_CHAT_ID');
$filePath = __DIR__ . '/receipt.pdf';

if (!$token || !$chatId) {
throw new Exception('Missing environment configuration.');
}

if (!file_exists($filePath)) {
throw new Exception('Target file does not exist: ' . $filePath);
}

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://api.telegram.org/bot{$token}/sendDocument");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// CURLFile automatically sets the content-type to multipart/form-data
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'chat_id' => $chatId,
'document' => new CURLFile($filePath, 'application/pdf', 'receipt.pdf'),
'caption' => 'Your requested receipt'
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if (curl_errno($ch)) {
$errorMsg = curl_error($ch);
curl_close($ch);
throw new Exception('cURL Error: ' . $errorMsg);
}

curl_close($ch);

if ($httpCode !== 200) {
throw new Exception('Telegram API returned non-200 status: ' . $httpCode . ' Response: ' . $response);
}

$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Failed to parse Telegram JSON response.');
}

if (!isset($data['ok']) || !$data['ok']) {
throw new Exception('Telegram API Error: ' . ($data['description'] ?? 'Unknown error'));
}

// Extract the file_id for future reuse
$fileId = $data['result']['document']['file_id'];
echo "File uploaded successfully. File ID: " . $fileId . "\n";

Reusing the file_id

Once a file is uploaded, Telegram stores it on their servers and assigns it a unique file_id. If you need to send the exact same file to another user, or to the same user again, do not re-upload the file.

Re-uploading wastes server CPU, memory, and bandwidth. Instead, pass the file_id as a simple string parameter. Telegram will resolve the file instantly on their backend.

<?php

$anotherChatId = getenv('TELEGRAM_ANOTHER_CHAT_ID');

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.telegram.org/bot{$token}/sendDocument");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Pass the file_id string directly instead of a CURLFile object
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'chat_id' => $anotherChatId,
'document' => $fileId,
'caption' => 'Shared copy of the receipt'
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if (curl_errno($ch)) {
$errorMsg = curl_error($ch);
curl_close($ch);
throw new Exception('cURL Error: ' . $errorMsg);
}
curl_close($ch);

$data = json_decode($response, true);
if (isset($data['ok']) && $data['ok']) {
echo "File sent successfully using cached file_id.\n";
}

When to Use a Public URL

Instead of uploading a file from your local disk, you can pass a public HTTP URL of the file to Telegram.

* Pros: Your PHP server does not need to download the file to its local disk first, saving disk space and local I/O. * Cons: Telegram must download the file from your server. If your server is slow or has strict firewall rules, the request will timeout. Additionally, Telegram caches files sent via URL. If the file content changes on your server but the URL remains the same, Telegram may continue to send the old, cached version.

To send via URL, simply pass the URL string in the payload:

curl_setopt($ch, CURLOPT_POSTFIELDS, [
'chat_id' => $chatId,
'document' => 'https://yourdomain.com/static/terms.pdf',
'caption' => 'Terms of Service'
]);

Production Limits and Considerations

1. Size Limits: * For regular bots using the default Telegram Bot API cloud servers, the maximum file upload size is 50 MB. * When sending files via a public URL, the limit is 20 MB. * If your application requires handling files up to 2000 MB, you must set up and run a self-hosted Local Telegram Bot API Server.

2. Timeout Configuration: Uploading large files (close to 50 MB) over slow connections can exceed default PHP and cURL execution timeouts. Always set explicit timeouts on your cURL handle when uploading files:

   curl_setopt($ch, CURLOPT_TIMEOUT, 60); // Allow up to 60 seconds for the upload

3. MIME Types: When using CURLFile, always specify the correct MIME type (second parameter) and filename (third parameter). Leaving them blank can cause Telegram to reject the file or display it with an generic, unreadable extension to the end-user.

If you need professional assistance designing, scaling, or deploying high-performance Telegram integrations, contact BotCreator — studio that ships Telegram bots / Mini Apps.

New articles on Telegram

We explain what to automate in your business and how it works in practice. No spam.