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.

Sources read on 3 Nov 2026. Each fact about a third-party product links to its source (S1, S2...), listed at the end with a short quote.

The short version

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.

Bar chart of support tickets per week, sample data: 41, 38, 52, 47, 35 and 29 over six weeks.
labelvalue
W141
W238
W352
W447
W535
W629

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

  1. 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.
  2. In your spreadsheet, make a tab called Data with the headers label and value.
  3. Open Read rows. Pick your Google Sheets credential (the file names it Google Sheets account), your spreadsheet and the Data tab.
  4. Open Send photo to Telegram. Pick your Telegram credential (Telegram account) and set the Chat ID.
  5. In Build chart URL, change TITLE to what the chart is about.
  6. 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

  1. Every Monday, a Schedule Trigger. Change the day and hour to suit.
  2. 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 value to a number.
  3. Build chart URL, a Code node. It builds the bar chart from label and value, URL-encodes it, and writes imageUrl, a caption (the last label and value), the Slack blocks and a slackError that is empty unless the URL is too long for Slack. It throws, naming the row, when a value cell is empty or not a number, so a half-filled sheet stops the run instead of charting a blank as 0.
  4. 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.
  5. 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

What we could not confirm

Sources

All pages read on 3 Nov 2026. Quotes are exact and short. Web pages change, so check the date.

  1. 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.

  2. 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.

  3. 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".

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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."

  9. 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.