Telegram SDK (MT4/MT5) — Developer Guide (Send Messages + Photos + Threading)

Telegram SDK (MT4/MT5) — Developer Guide (Send Messages + Photos + Threading)

5 January 2026, 17:40
The Hung Ngo
0
58

This article is a step-by-step guide for developers who bought Telegram SDK (compiled library) and want to integrate Telegram notifications into their own MQL4/MQL5 code. It covers both versions: MT4 and MT5.

SDK update note: Telegram SDK v1.10 improves chat targeting by normalizing the chat_id you pass to the SDK. You can now safely pass numeric IDs (-100...), @channelusername, or a public link like https://t.me/channelusername (auto-converted to @channelusername). No function signatures were changed.

Products

Not a coder? Use the ready-to-run "Send To Telegram" utilities

If you do not write code and you want a solution you can just attach to a chart and configure via inputs, use these products instead (they are full-featured, highly customizable, and ready to run):

1) Who this SDK is for (important)

  • Developers only: You must be able to write/compile an EA or Script and use #import.
  • This is a compiled library ( .ex4 / .ex5 ). You call its functions from your own program.
  • No chart UI, no inputs panel, no "drag & drop to use" behavior (that’s what the Utilities are for).

Limitations you must know

  • WebRequest is required. If WebRequest is blocked or not whitelisted, sending will fail.
  • Strategy Tester: WebRequest is typically restricted in the tester, so treat this as a live/demo chart feature, not a backtest feature.
  • Custom Indicators: WebRequest usage is restricted in indicators on many builds — use an EA/Script/Service for Telegram sending.

2) After purchase: where the files are & how to install

Important note about #import paths (MQL4/MQL5)

In both MQL4 and MQL5, #import searches relative to the Libraries folder:

  • MT4: <Data Folder>\MQL4\Libraries\
  • MT5: <Data Folder>\MQL5\Libraries\

Therefore, to use this SDK as an imported library, you must place the .ex4 / .ex5 file into Libraries and then import it by filename.

Step 2.1 — Open your terminal data folder

  1. In MetaTrader: File → Open Data Folder
  2. Go to the MQL4 or MQL5 folder depending on your platform.

Step 2.2 — Download from Market (inside terminal)

  1. Open the Market tab:
    • MT4: View → Terminal → MarketPurchased
    • MT5: View → Toolbox → MarketPurchased
  2. Find the product and click Download/Install.

Step 2.3 — Where the Market puts the downloaded file (actual location)

After installation, this product is placed under the Scripts\Market folder inside the terminal Data Folder:

  • MT4: <Data Folder>\MQL4\Scripts\Market\Telegram SDK.ex4
  • MT5: <Data Folder>\MQL5\Scripts\Market\Telegram SDK.ex5

Step 2.4 — Required step: copy the SDK file into Libraries and import

Because #import resolves libraries from the Libraries folder, you must copy the compiled file from Scripts\Market to Libraries:

  • MT4: copy Telegram SDK.ex4 from MQL4\Scripts\Market\ to MQL4\Libraries\
  • MT5: copy Telegram SDK.ex5 from MQL5\Scripts\Market\ to MQL5\Libraries\

Then import by filename:

#import "Telegram SDK.ex4"
   // declarations...
#import
#import "Telegram SDK.ex5"
   // declarations...
#import

Tip: After copying, restart MetaEditor (and/or the terminal) if MetaEditor does not detect the library immediately.

Tip: MetaEditor can generate an import/include stub automatically from an exported library (Tools → Generate Include File). This helps avoid prototype mistakes.


3) Telegram prerequisites (Token + Target chat)

Step 3.1 — Create a bot token

  1. In Telegram, search for @BotFather
  2. Create a bot and copy the bot token (looks like 123456789:AA....)

Step 3.2 — Understand what you must pass as chat_id

Telegram Bot API methods require a chat_id (target). In practice, you have these common target types:

Target What to use as chat_id Must-know rule
Private user (DM) Numeric user chat id (usually positive) User must press Start / send /start to your bot at least once. Bots cannot DM users first.
Group / Supergroup Numeric chat id (often -100...) Bot must be added to the group (and allowed to send messages).
Public channel @channelusername (or numeric -100...) To post to a channel, add bot as Administrator (so it can post messages).
Private channel/group (no @username) Numeric chat id (-100...) Public username targeting will not work. Use numeric id.

Step 3.3 — New in v1.10: chat_id normalization (quality-of-life)

In Telegram SDK v1.10, the SDK automatically normalizes chat_id inside TG_Init() and send calls:

  • Numeric IDs remain unchanged: "123456789" or "-1001234567890".
  • @channelusername stays unchanged.
  • Public links are converted: "https://t.me/channelusername" → "@channelusername".
  • Important: private invite links like "t.me/+xxxx" or "t.me/joinchat/xxxx" are NOT public usernames. Use numeric chat_id for private chats.

4) MetaTrader settings (WebRequest whitelist)

You must allow WebRequest for Telegram API in terminal options, otherwise calls fail.

  1. Go to: Tools → Options → Expert Advisors
  2. Enable: Allow WebRequest for listed URL
  3. Add this URL:
https://api.telegram.org

5) Import stub (copy/paste)

This is the full import stub for both MT4 and MT5. If you prefer, MetaEditor can generate it automatically (Tools → Generate Include File).

MT4 Import Stub

#import "Telegram SDK.ex4"
   int    TG_Init(string token, string chat_id);
   void   TG_SetTimeoutMs(int ms);
   void   TG_SetRetries(int n);
   void   TG_SetDebug(bool on);
   string TG_Version();

   // Base send (manual reply id)
   int TG_SendMessage(string token, string chat_id, string text,
                      string parse_mode, bool disable_preview, bool disable_notification,
                      int reply_to_message_id);
   int TG_SendMessageDefault(string text, string parse_mode, bool disable_preview, bool disable_notification,
                             int reply_to_message_id);

   // Threading (ROOT/REPLY flow)
   int TG_SendMessageThread(string token, string chat_id, string thread_key,
                            string text, string parse_mode,
                            bool disable_preview, bool disable_notification,
                            bool saveAsRoot, bool replyToRoot);
   int TG_SendMessageThreadDefault(string thread_key,
                                   string text, string parse_mode,
                                   bool disable_preview, bool disable_notification,
                                   bool saveAsRoot, bool replyToRoot);

   // Legacy int key (MT4 ticket)
   int TG_SendMessageThreadInt(string token, string chat_id, int key,
                               string text, string parse_mode,
                               bool disable_preview, bool disable_notification,
                               bool saveAsRoot, bool replyToRoot);
   int TG_SendMessageThreadIntDefault(int key,
                                      string text, string parse_mode,
                                      bool disable_preview, bool disable_notification,
                                      bool saveAsRoot, bool replyToRoot);

   // Photo base
   int TG_SendPhotoFile(string token, string chat_id, string file_path,
                        string caption, string parse_mode,
                        bool disable_notification, int reply_to_message_id);

   // Photo default
   int TG_SendPhotoFileDefault(string file_path,
                               string caption, string parse_mode,
                               bool disable_notification, int reply_to_message_id);

   // Photo threading
   int TG_SendPhotoFileThread(string token, string chat_id, string thread_key,
                              string file_path,
                              string caption, string parse_mode,
                              bool disable_notification,
                              bool saveAsRoot, bool replyToRoot);
   int TG_SendPhotoFileThreadDefault(string thread_key,
                                     string file_path,
                                     string caption, string parse_mode,
                                     bool disable_notification,
                                     bool saveAsRoot, bool replyToRoot);

   // Photo threading legacy int key
   int TG_SendPhotoFileThreadInt(string token, string chat_id, int key,
                                 string file_path,
                                 string caption, string parse_mode,
                                 bool disable_notification,
                                 bool saveAsRoot, bool replyToRoot);
   int TG_SendPhotoFileThreadIntDefault(int key,
                                        string file_path,
                                        string caption, string parse_mode,
                                        bool disable_notification,
                                        bool saveAsRoot, bool replyToRoot);

   // Map (optional, power-user)
   bool TG_SaveRootMessageId(string key, int message_id);
   int  TG_GetRootMessageId(string key);

   // Validate
   bool TG_ValidateToken(string token);
   bool TG_ValidateChatID(string token, string chat_id);

   // Last error
   int    TG_GetLastHttpCode();
   int    TG_GetLastErrorCode();
   string TG_GetLastErrorMessage();
   string TG_GetLastResponseRaw();
#import

MT5 Import Stub

#import "Telegram SDK.ex5"
   int    TG_Init(string token, string chat_id);
   void   TG_SetTimeoutMs(int ms);
   void   TG_SetRetries(int n);
   void   TG_SetDebug(bool on);
   string TG_Version();

   // Base send (manual reply id)
   int TG_SendMessage(string token, string chat_id, string text,
                      string parse_mode, bool disable_preview, bool disable_notification,
                      int reply_to_message_id);
   int TG_SendMessageDefault(string text, string parse_mode, bool disable_preview, bool disable_notification,
                             int reply_to_message_id);

   // Threading (ROOT/REPLY flow)
   int TG_SendMessageThread(string token, string chat_id, string thread_key,
                            string text, string parse_mode,
                            bool disable_preview, bool disable_notification,
                            bool saveAsRoot, bool replyToRoot);
   int TG_SendMessageThreadDefault(string thread_key,
                                   string text, string parse_mode,
                                   bool disable_preview, bool disable_notification,
                                   bool saveAsRoot, bool replyToRoot);

   // Legacy MT5 ticket key (ulong)
   int TG_SendMessageThreadULong(string token, string chat_id, ulong key,
                                 string text, string parse_mode,
                                 bool disable_preview, bool disable_notification,
                                 bool saveAsRoot, bool replyToRoot);
   int TG_SendMessageThreadULongDefault(ulong key,
                                        string text, string parse_mode,
                                        bool disable_preview, bool disable_notification,
                                        bool saveAsRoot, bool replyToRoot);

   // Photo base
   int TG_SendPhotoFile(string token, string chat_id, string file_path,
                        string caption, string parse_mode,
                        bool disable_notification, int reply_to_message_id);

   // Photo default
   int TG_SendPhotoFileDefault(string file_path,
                               string caption, string parse_mode,
                               bool disable_notification, int reply_to_message_id);

   // Photo threading
   int TG_SendPhotoFileThread(string token, string chat_id, string thread_key,
                              string file_path,
                              string caption, string parse_mode,
                              bool disable_notification,
                              bool saveAsRoot, bool replyToRoot);
   int TG_SendPhotoFileThreadDefault(string thread_key,
                                     string file_path,
                                     string caption, string parse_mode,
                                     bool disable_notification,
                                     bool saveAsRoot, bool replyToRoot);

   // Photo threading legacy ulong key
   int TG_SendPhotoFileThreadULong(string token, string chat_id, ulong key,
                                   string file_path,
                                   string caption, string parse_mode,
                                   bool disable_notification,
                                   bool saveAsRoot, bool replyToRoot);
   int TG_SendPhotoFileThreadULongDefault(ulong key,
                                          string file_path,
                                          string caption, string parse_mode,
                                          bool disable_notification,
                                          bool saveAsRoot, bool replyToRoot);

   // Map (optional, power-user)
   bool TG_SaveRootMessageId(string key, int message_id);
   int  TG_GetRootMessageId(string key);

   // Validate
   bool TG_ValidateToken(string token);
   bool TG_ValidateChatID(string token, string chat_id);

   // Last error
   int    TG_GetLastHttpCode();
   int    TG_GetLastErrorCode();
   string TG_GetLastErrorMessage();
   string TG_GetLastResponseRaw();
#import

6) Quick Start (minimal working example)

This minimal EA sends 1 message on startup. Notes:

  • The SDK supports |n as a newline placeholder.
  • For v1.10: chat_id can be "-100..." or "@channelusername" or "https://t.me/channelusername".

MT4 Minimal EA

#property strict

#import "Telegram SDK.ex4"
   int  TG_Init(string token, string chat_id);
   void TG_SetDebug(bool on);
   string TG_Version();

   int  TG_SendMessageDefault(string text, string parse_mode, bool disable_preview, bool disable_notification,
                              int reply_to_message_id);

   int    TG_GetLastHttpCode();
   int    TG_GetLastErrorCode();
   string TG_GetLastErrorMessage();
#import

int OnInit()
{
   TG_SetDebug(true);

   // Put your real token/chat_id here:
   // Examples for chat_id:
   // "-1001234567890"   (group/channel numeric ID)
   // "@MyChannel"       (public channel username)
   // "https://t.me/MyChannel" (v1.10 auto-normalized to @MyChannel)
   TG_Init("123456789:AA....YOUR_TOKEN....", "-1001234567890");

   Print("Telegram SDK version: ", TG_Version());

   int mid = TG_SendMessageDefault("Hello from MT4|n<b>Telegram SDK</b>", "HTML", true, false, 0);

   Print("mid=", mid,
         " http=", TG_GetLastHttpCode(),
         " err=",  TG_GetLastErrorCode(),
         " msg=",  TG_GetLastErrorMessage());

   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {}
void OnTick() {}

MT5 Minimal EA

#property strict

#import "Telegram SDK.ex5"
   int  TG_Init(string token, string chat_id);
   void TG_SetDebug(bool on);
   string TG_Version();

   int  TG_SendMessageDefault(string text, string parse_mode, bool disable_preview, bool disable_notification,
                              int reply_to_message_id);

   int    TG_GetLastHttpCode();
   int    TG_GetLastErrorCode();
   string TG_GetLastErrorMessage();
#import

int OnInit()
{
   TG_SetDebug(true);

   // Put your real token/chat_id here:
   TG_Init("123456789:AA....YOUR_TOKEN....", "-1001234567890");

   Print("Telegram SDK version: ", TG_Version());

   int mid = TG_SendMessageDefault("Hello from MT5|n<b>Telegram SDK</b>", "HTML", true, false, 0);

   Print("mid=", mid,
         " http=", TG_GetLastHttpCode(),
         " err=",  TG_GetLastErrorCode(),
         " msg=",  TG_GetLastErrorMessage());

   return(INIT_SUCCEEDED);
}

7) SDK settings (recommended before real usage)

These functions control network behavior and logging. Call them once (usually in OnInit).

  • TG_SetTimeoutMs(ms): HTTP timeout in milliseconds (example: 10000).
  • TG_SetRetries(n): retry count for short network outages (example: 2 or 3).
  • TG_SetDebug(true/false): prints detailed logs (turn OFF in production if you want a clean Journal).
  • TG_Version(): returns SDK version string (useful to confirm you’re on v1.10).

8) Text messages (Base API)

8.1 TG_Init + Default calls

  • Call TG_Init(token, chat_id) once (e.g., in OnInit()).
  • Then use ...Default() methods so you don’t pass token/chat_id every time.
  • Return value: on success you get a message_id (positive integer). On failure you get 0.

8.2 parse_mode

  • Use "HTML" if you want formatting like <b>bold</b> , <i>italic</i> , etc.
  • Use "MarkdownV2" if you prefer MarkdownV2 syntax.
  • Use "" (empty) for plain text.

8.3 disable_preview / disable_notification

  • disable_preview=true avoids link previews (cleaner channels).
  • disable_notification=true sends silently (no notification sound).

8.4 Replying to a specific message (manual reply_to_message_id)

You can reply to an existing message by passing reply_to_message_id (message_id of a message in the same chat).

// Reply to message_id=123 (same chat)
int reply_mid = TG_SendMessageDefault("This is a reply", "HTML", true, false, 123);

9) Threading (ROOT/REPLY flow)

Threading helps you keep a clean timeline per trade/order. You send one ROOT message, then later updates are sent as replies under that ROOT.

9.1 The core idea

  • thread_key identifies the thread (example: ticket id, order id, custom string like "EURUSD#A1").
  • saveAsRoot = store the returned message_id as the ROOT for that key.
  • replyToRoot = automatically reply to the stored ROOT message_id.

Important: ROOT mapping is kept in memory. If you restart the terminal/EA, you must send ROOT again (or store/reload mapping yourself).

9.2 Minimal ROOT/REPLY demo — MT4 (int key)

#property strict

#import "Telegram SDK.ex4"
   int  TG_Init(string token, string chat_id);
   void TG_SetDebug(bool on);

   int TG_SendMessageThreadIntDefault(int key,
                                      string text, string parse_mode,
                                      bool disable_preview, bool disable_notification,
                                      bool saveAsRoot, bool replyToRoot);

   int    TG_GetLastHttpCode();
   int    TG_GetLastErrorCode();
   string TG_GetLastErrorMessage();
#import

int OnInit()
{
   TG_SetDebug(true);
   TG_Init("123456789:AA....YOUR_TOKEN....", "-1001234567890");

   int ticket = 10001;

   // 1) ROOT message
   int root_mid = TG_SendMessageThreadIntDefault(ticket,
                                                 "Order opened (ROOT)|nTicket=" + IntegerToString(ticket),
                                                 "HTML", true, false,
                                                 true,  false);

   // 2) REPLY message (goes under ROOT)
   int reply_mid = TG_SendMessageThreadIntDefault(ticket,
                                                  "Update (REPLY)|nSL moved...",
                                                  "HTML", true, false,
                                                  false, true);

   Print("root_mid=", root_mid, " reply_mid=", reply_mid,
         " http=", TG_GetLastHttpCode(),
         " err=",  TG_GetLastErrorCode(),
         " msg=",  TG_GetLastErrorMessage());

   return(INIT_SUCCEEDED);
}

9.3 Minimal ROOT/REPLY demo — MT5 (ulong key)

#property strict

#import "Telegram SDK.ex5"
   int  TG_Init(string token, string chat_id);
   void TG_SetDebug(bool on);

   int TG_SendMessageThreadULongDefault(ulong key,
                                        string text, string parse_mode,
                                        bool disable_preview, bool disable_notification,
                                        bool saveAsRoot, bool replyToRoot);

   int    TG_GetLastHttpCode();
   int    TG_GetLastErrorCode();
   string TG_GetLastErrorMessage();
#import

int OnInit()
{
   TG_SetDebug(true);
   TG_Init("123456789:AA....YOUR_TOKEN....", "-1001234567890");

   ulong ticket = 10001;

   int root_mid = TG_SendMessageThreadULongDefault(ticket,
                                                   "MT5 ROOT|nTicket=" + (string)ticket,
                                                   "HTML", true, false,
                                                   true, false);

   int reply_mid = TG_SendMessageThreadULongDefault(ticket,
                                                    "MT5 REPLY|nTP changed...",
                                                    "HTML", true, false,
                                                    false, true);

   Print("root_mid=", root_mid, " reply_mid=", reply_mid,
         " http=", TG_GetLastHttpCode(),
         " err=",  TG_GetLastErrorCode(),
         " msg=",  TG_GetLastErrorMessage());

   return(INIT_SUCCEEDED);
}

10) Send photos (sendPhoto)

10.1 Where to put image files

The SDK reads image files using the terminal sandbox. The simplest rule:

  • Put your image into:
    • MT4: <Data Folder>\MQL4\Files\
    • MT5: <Data Folder>\MQL5\Files\
  • Then pass file_path as just the filename (example: "shot.png") or a subfolder path inside Files.

10.2 Photo examples

Send photo (default)

// caption supports parse_mode too (e.g. "HTML")
int mid = TG_SendPhotoFileDefault("shot.png",
                                 "Chart screenshot|n<b>XAUUSD</b>",
                                 "HTML",
                                 false,
                                 0);

Send photo threaded (save ROOT + reply later)

// ROOT photo
int root_photo = TG_SendPhotoFileThreadDefault("trade#10001",
                                              "shot.png",
                                              "ROOT screenshot",
                                              "",
                                              false,
                                              true,   // saveAsRoot
                                              false); // replyToRoot

11) Validation helpers (recommended during setup)

  • TG_ValidateToken(token): calls Telegram getMe and returns true/false (good for quick token check).
  • TG_ValidateChatID(token, chat_id): sends a silent test message and returns true/false (good for verifying chat permissions).

Note: If chat validation returns “chat not found”, it is usually wrong chat_id or the bot has no access (not in group/channel, not admin in channel, or the user never started the bot in private DM).


12) Error handling & debugging (must-read)

12.1 Always check these after a send

  • TG_GetLastHttpCode() — HTTP response code (200 = OK)
  • TG_GetLastErrorCode() — Telegram error code or internal code
  • TG_GetLastErrorMessage() — human-readable message
  • TG_GetLastResponseRaw() — raw JSON response (useful for troubleshooting)

12.2 Common failure causes

  • WebRequest not allowed: You forgot to whitelist https://api.telegram.org in Options.
  • Wrong token: HTTP 401/403 or “Unauthorized”.
  • Wrong chat_id / no access: HTTP 400 “chat not found” or “Forbidden”.
  • Rate limits (429): Too many messages too fast. Reduce sending frequency and keep retries reasonable.
  • Photo send errors: file not found, empty bytes, or wrong path — place files in MQL4/Files or MQL5/Files.

12.3 Performance tip

Do not send a message on every tick. Instead, trigger only on events (new trade, close, SL/TP change, etc.), or implement a simple throttling/queue.


13) Recommended settings

  • TG_SetTimeoutMs(10000) for a stable network environment.
  • TG_SetRetries(2) or 3 to survive short outages, but don’t spam.
  • TG_SetDebug(true) while integrating; turn OFF in production if you want a clean Journal.

14) Final notes + please support the product ❤️

If Telegram SDK saved you development time and worked well for your project, please consider leaving a 5-star review on the Market page. It helps a lot with product ranking and future updates.

And again — if you want a ready-to-run version for non-coders: