Sending photos and documents to Telegram via PHP: multipart, file_id and Bot API limits

Sending files is one of the basic operations when integrating Telegram-бот with a business: checks, catalogs, prices, photos of goods. It would seem that the Bot API covers everything in two methods — sendPhoto And sendDocument. But there are nuances: cURL does not send files like a regular post array, file_id can be reused, and the size of files is more restrictive than it seems. Let's break it down in order.

Why not file_get_contents for the API

In training examples, it is common to send data via file_get_contents with flow http://. This works for simple queries, but not for files. Telegram accepts photos and documents only through multipart/form-data — a format in which each parameter (including a file) is encoded as a separate part of the request body with headers Content-Disposition.

The cURL standard library in PHP does this natively: you just need to specify CURLOPT_POST, pass text fields through CURLOPT_POSTFIELDS as an array, and the file as a key with a prefix @ (or object CURLFile in PHP 5.5+).

sendPhoto: send a local file

The minimum working example of sending a photo that is already on the server disk is:

Please note: CURLFile accepts the MIME type and the original file name. Telegram will determine the format itself, but the indication image/jpeg or image/png accelerates validation.

sendDocument: send PDF, XLSX, ZIP

The principle is the same, but with two differences: the limit is greater (up to 50 MB against 10 MB in the photo) and can be transferred caption — text up to 1024 characters. For prices, invoices and contracts, this is the standard scenario:

file_id: reuse instead of reupload

When you send a file for the first time, Telegram returns an object Photo or Document with field file_id. This identifier is a string value of the form AgACAgIAAxkBAAIBZ2...It can be used in subsequent queries without re-uploading the file:

This is critical for catalogs: upload an image of the product once when creating a card, save file_id to the database, and when you send it to the client, you simply pass the ID. Save both traffic and API call time.

Important: file_id is linked to the bot. The ID obtained in one bot cannot be used in another. Lifetime file_id is not documented, but in practice remains working months.

Size limits: 10 MB for photos, 50 MB for documents

Telegram sets the following restrictions:

  • sendPhoto — local file up to 10 MB, by URL up to 10 MB;
  • sendDocument — local file up to 50 MB, by URL up to 50 MB (for some file types it may be less);
  • the photo is automatically compressed to 1280×1280px if it exceeds.

In practice, check the size filesize before sending. If the file is larger than the limit, send it as an archive or squeeze it.

When to pass the URL instead of uploading

U sendPhoto And sendDocument there is an alternative way: parameter Photo or Document accepts a direct link to the file on the Internet. In this case, Telegram downloads the content itself — your server does not spend traffic on downloading and sending.

Use a URL when:

  • the file is already in CDN, S3, or public storage.
  • you don't want to upload the same file again each time you upload it.
  • your server is limited in outgoing traffic or runtime.

Use multipart-loading when:

  • the file is generated dynamically (PDF invoice, report) and is not stored publicly;
  • you need to guarantee the availability of the file (the URL may "die");
  • required caption with markup (emoji, formatting) — works with both URLs and file_id.

Common mistakes

Error: “Bad Request: wrong URL host”. Telegram does not accept authorization links. (https://user:pass@example.com/file.jpg) and local addresses (http://localhost/) The bot server must have access to the URL.

Error: Empty CURLFile or file not found. Check the file_exists() before sending. If the file is generated in memory (e.g. via imagejpeg()), write to a temporary file via tempnam or use the flow php://memory with wrapper CURLFile.

Error: “File is too big”. Check the filesize and limits of the method. If you send by file_id, limits do not apply — resending the same file is unlimited.

Bottom line: when is the approach

For directories and static media — downloaded once, saved file_id to the database, send by ID. For dynamic documents (invoices, contracts) — generate, send via CURLFile. For content from the CDN — we pass the URL, save server resources.

Proper work with files in the Telegram bot reduces response time, saves traffic and reduces the load on the Bot API within the limits of 20–60 messages per second. For complex file integrations, consider a хостинг ботов with a fast drive and sufficient outbound channel.

New articles on Telegram

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