Guide for developers
Google Sheets chart to Telegram with n8n
Google Sheets has no API that exports its charts as an image S1, so a workflow cannot copy the chart from the sheet. It can read the rows and draw the chart itself: the rows become a ChartPNG URL, and Telegram'ssendPhoto takes a URL S3. No binary node, no Drive upload.
The short version
- The Sheets API describes a chart as a specification, and we found no method that returns it as an image S2.
- Telegram downloads a photo from an HTTP URL you pass S3, and the n8n node has a Photo field for it S5.
- The template reads a Data tab (columns
labelandvalue), builds one chart URL and sends it. An optional Slack node, off by default, sends the same URL as an image block S8. - Out of scope: reading the charts you already drew in Sheets, and bots that answer commands.
What you get
Six rows of made-up data in, one photo out. The chart is drawn from the rows, so it does not look like a chart you built in Sheets; it shows the same numbers.
| label | value |
|---|---|
| W1 | 41 |
| W2 | 38 |
| W3 | 52 |
| W4 | 47 |
| W5 | 35 |
| W6 | 29 |
The Code node turns those rows into this config, which is the part you change:
{
"type": "bar",
"data": {
"labels": [
"W1",
"W2",
"W3",
"W4",
"W5",
"W6"
],
"datasets": [
{
"label": "Support tickets per week",
"data": [
41,
38,
52,
47,
35,
29
]
}
]
},
"options": {
"plugins": {
"legend": {
"display": false
},
"title": {
"display": true,
"text": "Support tickets per week"
},
"chartpng": {
"source": "Sample data"
}
}
}
}The URL is 556 characters long. The template adds w=800&h=450&dpr=2, and its footer says "Source: Google Sheets" where this example says "Sample data". This URL is unsigned, so the image keeps the small watermark. A signed URL removes it: see Docs, Signed URLs.
Why a URL and not a file
Telegram takes a photo three ways: a file_id, an HTTP URL it downloads itself, or a multipart upload S4. With the image as binary data, the node's Input Binary Field has to be a field name, and one n8n issue shows an expression there failingS7. The usual advice for a binary file is to host it somewhere that gives a URL S6. A ChartPNG URL is already that URL, so the workflow skips the hosting step.
The same holds for Slack. A Block Kit image block needs a publicly hosted image URL S8, and uploading a file means the two-step files API that replaced files.upload S9. See Charts in Slack from n8n and AI agents.
Import and set up the workflow
- Download google-sheets-chart-to-telegram.json, or use Import from URL in n8n with
https://chartpng.dev/templates/n8n/google-sheets-chart-to-telegram.json. The n8n guide has the import steps. - In your spreadsheet, make a tab called Data with the headers
labelandvalue. - Open Read rows. Pick your Google Sheets credential (the file names it
Google Sheets account), your spreadsheet and the Data tab. - Open Send photo to Telegram. Pick your Telegram credential (
Telegram account) and set the Chat ID. - In Build chart URL, change
TITLEto what the chart is about. - Run Execute workflow. The photo should land in the chat. Then click Publish, or switch the workflow to Active on n8n 1.x, so the Monday 9:00 schedule runs.
The workflow, node by node
- Every Monday, a Schedule Trigger. Change the day and hour to suit.
- Read rows, the Google Sheets node, operation Get Row(s). It is the same node the Google Sheets rows to a chart URL template uses to read the Data tab. Sheets may return a value as a number or as text, so the next node converts
valueto a number. - Build chart URL, a Code node. It builds the bar chart from
labelandvalue, URL-encodes it, and writesimageUrl, acaption(the last label and value), the Slackblocksand aslackErrorthat is empty unless the URL is too long for Slack. It throws, naming the row, when avaluecell is empty or not a number, so a half-filled sheet stops the run instead of charting a blank as 0. - Check the render, an HTTP Request node that opens the URL once. A bad config returns an error here, so Telegram never gets a broken link. The workflow does not use the file this node downloads; Telegram fetches the photo from the URL on its own.
- Send photo to Telegram, the Telegram node. These are the fields, using the names from the node source S5:
Resource: Message
Operation: Send Photo
Chat ID: your chat id or @channelusername
Binary File: off
Photo: {{ $('Build chart URL').first().json.imageUrl }}
Caption: {{ $('Build chart URL').first().json.caption }} (Additional Fields)Binary File stays off: with it on, the node reads a binary field instead of the Photo URL S5. The caption can hold up to 1024 characters S3.
The optional Slack branch
Post to Slack is in the workflow but disabled. To use it, right-click the node and enable it, pick your Slack credential (Slack account) and set the channel. It posts one image block with the same URL and the caption asalt_text. Slack accepts an image_url of up to 3000 characters and an alt_text of up to 2000S8. When the URL is longer than 3000, the Code node fills slackError and the Slack node stops with that message instead of posting; the Telegram node does not read it, so Telegram still sends. Fewer rows fix it. The node also sets the caption as the Notification Text, and turns off "Include Link to Workflow" so n8n adds no "Automated with this n8n workflow" line.
If Telegram does not show the photo
- The URL must be public. Telegram's servers download it S3, so open the URL in a private window. A ChartPNG URL needs no key to be fetched; signing only removes the watermark. A URL on your own machine, behind a login or on a private network is not reachable from Telegram.
- Size by URL: 5 MB. Telegram caps a photo it downloads from a URL at 5 MB S4. The example above is 37 KB (measured on 3 Nov 2026), and a bar chart stays far below the cap.
- Dimensions. Width plus height must not exceed 10000 and the ratio of one to the other must be at most 20 S3. ChartPNG accepts sides from 50 to 2000 pixels and a dpr of 1 or 2, so width plus height tops out at 8000. The ratio is the one to watch:
w=2000&h=50is 40. - URL length. Telegram states no limit S4, but a very long URL is a risk in any proxy between the two. Keep the chart short: fewer rows, shorter labels. This is our advice, not a Telegram rule.
- An error before the photo. If Check the render fails, the config is the problem, not Telegram. If Build chart URL fails, a
valuecell is empty or not a number. - Chat ID. A channel can be given as
@channelusername, and the Telegram Trigger node can supply a chat's ID S5. - Binary File is on. Turn it off. The Photo field is used only when it is off, and with it on the node reads a binary field instead S5.
What we could not confirm
- Two Reddit threads (r/Slack and r/n8n) could not be opened from here, so we quote nothing from them.
- Whether a real Telegram bot shows the ChartPNG image when the workflow runs. We checked the image URL (HTTP 200, image/png) and the node parameters against the n8n source, but did not send to a Telegram chat.
- The longest photo URL Telegram accepts. S4 gives no limit. The advice to keep the URL short is ours, not Telegram's.
- The error text Telegram returns for a URL it cannot use. We did not trigger one, so the troubleshooting names causes, not messages.
- That Google has no other way to get a Sheets chart as an image, such as a published chart link or an export of the whole sheet. S2 is two reference pages; S1 is a competitor. We did not read all of Google's documentation.
Related
- n8n workflow templates and all guides.
- Charts in Google Sheets: a chart inside a cell with the
IMAGEfunction. - Charts in Slack from n8n and AI agents: the image block and its limits.
Sources
All pages read on 3 Nov 2026. Quotes are exact and short. Web pages change, so check the date.
- S1ChartCastr: ChartCastr vs n8nhttps://chartcastr.com/compare/chartcastr-vs-n8n
Quote: "Google Sheets has no API to export a chart as a PNG or image."
Read 3 Nov 2026. A competitor's comparison page, so a claim by a third party. S2 is the check against Google's own reference.
- S2Google Sheets API: spreadsheets.sheets and chartshttps://developers.google.com/workspace/sheets/api/reference/rest/v4/spreadsheets/charts
Quote: "A chart embedded in a sheet."
Read 3 Nov 2026. The reference describes a chart as a specification (EmbeddedChart, ChartSpec: "The specifications of a chart."). On this page and on spreadsheets.sheets we found no method that returns a chart as an image. That is absence on two pages, not a statement from Google.
- S3Telegram Bot API: sendPhotohttps://core.telegram.org/bots/api#sendphoto
Quote: "pass an HTTP URL as a String for Telegram to get a photo from the Internet"
Read 3 Nov 2026. The same entry: "The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20." The caption is "0-1024 characters after entities parsing".
- S4Telegram Bot API: Sending fileshttps://core.telegram.org/bots/api#sending-files
Quote: "5 MB max size for photos and 20 MB max for other types of content."
Read 3 Nov 2026. It is the line under "Provide Telegram with an HTTP URL for the file to be sent. Telegram will download and send the file." The same page gives 10 MB for photos you upload yourself. It states no limit on the length of the URL.
- S5n8n docs: Telegram message operations, Send Photohttps://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.telegram/message-operations/
Quote: "an HTTP URL for Telegram to get a file from the internet"
Read 3 Nov 2026. The Photo field is used when Binary File is off. "To send a binary file from the node itself, turn this option on." The Caption field is "max of 1024 characters". The node source (Telegram.node.ts, versions 1, 1.1, 1.2) names the fields binaryData and file.
- S6n8n community: Help to send Telegram photohttps://community.n8n.io/t/help-to-send-telegram-photo/3459
Quote: "upload it to a service (like Google Drive, Dropbox etc)"
Read 3 Nov 2026. An answer to someone holding a binary file: the node needs a file_id or a URL, so host the file first. With a chart URL there is no file to host.
- S7n8n GitHub issue 22955: Telegram "Send a photo message" fails to send binary datahttps://github.com/n8n-io/n8n/issues/22955
Quote: "expected and not a bug"
Read 3 Nov 2026. Closed on 9 Dec 2025, label working-as-expected. An expression that returned a binary object failed because the Input Binary Field expects a string. It shows one way the binary route goes wrong.
- S8Slack: Image blockhttps://docs.slack.dev/reference/block-kit/blocks/image-block
Quote: "The URL for a publicly hosted image."
Read 3 Nov 2026. image_url holds up to 3000 characters and alt_text up to 2000. "You must provide either an image_url or slack_file."
- S9Slack changelog: files.uploadhttps://docs.slack.dev/changelog/2024-04-a-better-way-to-upload-files-is-here-to-stay
Quote: "The original web API method for uploading files to Slack, files.upload, is being sunset"
Read 3 Nov 2026. Apps are told to move to two methods instead, files.getUploadURLExternal and files.completeUploadExternal (named on the page, not quoted here). The page shows two sunset dates (11 March 2025 and 12 November 2025), so we give none.