Create a Chart from BigQuery and Post it to Slack

7 min read

How to turn BigQuery query results into chart images and deliver them to Slack automatically — using Connected Sheets, Python, or Chartcastr.

Create a Chart from BigQuery and Post it to Slack

BigQuery is excellent at storing and crunching data. Slack is where your team works. Getting a chart from one to the other is a two-step problem: first render the query results into an image, then get that image into the right Slack channel at the right time.

This guide covers every way to do it.


Why BigQuery Can't Post Charts Natively

BigQuery is a query engine. It returns tabular data — rows and columns — not images. There is no native "export to Slack" button, no built-in chart renderer, and no scheduler that posts visuals.

This is different from a tool like Google Sheets, which has a chart renderer built in. With BigQuery, you always need an intermediate step:

BigQuery query results → [render to image] → Slack

That rendering step is what varies between approaches. And whatever renders the chart ultimately uploads a static PNG image to Slack — because that's all Slack supports. There are no live dashboard embeds, no interactive charts, no iframes. Just images.


Approach 1: BigQuery → Connected Sheets → Chartcastr (No Code)

The easiest path for non-engineers.

Step 1: Connect BigQuery to Google Sheets

  1. Open a new Google Sheet
  2. Go to Data → Data connectors → Connect to BigQuery
  3. Select your GCP project and write or paste your query
  4. Sheets pulls the results in as a connected range you can build charts from
-- Example: weekly revenue by product category
SELECT
  DATE_TRUNC(order_date, WEEK) AS week,
  category,
  SUM(revenue_usd) AS revenue
FROM `your-project.sales.orders`
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 12 WEEK)
GROUP BY 1, 2
ORDER BY 1, 3 DESC

Step 2: Build your chart in Sheets

Highlight the connected data range and insert a chart. Sheets renders it as a standard Google Sheets chart — bar, line, pie, whatever fits your data.

Step 3: Connect to Chartcastr

Connect the Sheet to Chartcastr, select the chart or data range, pick a Slack channel, and set a schedule. Chartcastr renders the chart fresh on each run and posts it with an AI summary.

The Connected Sheet auto-refreshes from BigQuery when Chartcastr reads it, so your Slack post always reflects the latest data.

For more on the BigQuery + Sheets setup, see Best Tool to Load Google Sheets to BigQuery (2025/2026 Guide).


Approach 2: Python + Cloud Scheduler (Full Control)

For engineers who want pixel-perfect charts or need to go direct without a Sheets intermediary.

The stack

  • google-cloud-bigquery — query BQ and return a DataFrame
  • pandas + matplotlib (or plotly) — render the chart
  • slack_sdk — upload the image via files_upload_v2
  • Google Cloud Scheduler — trigger the script on a cron schedule
  • Cloud Run or Cloud Functions — serverless host for the script

Full example

import io
import os
from datetime import datetime

import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import pandas as pd
from google.cloud import bigquery
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

SLACK_TOKEN = os.environ['SLACK_BOT_TOKEN']
SLACK_CHANNEL = os.environ['SLACK_CHANNEL_ID']  # e.g. C01234ABCDE


def query_bigquery() -> pd.DataFrame:
    client = bigquery.Client()
    query = """
        SELECT
          DATE_TRUNC(order_date, WEEK) AS week,
          SUM(revenue_usd) AS revenue
        FROM `your-project.sales.orders`
        WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 12 WEEK)
        GROUP BY 1
        ORDER BY 1
    """
    return client.query(query).to_dataframe()


def render_chart(df: pd.DataFrame) -> bytes:
    fig, ax = plt.subplots(figsize=(12, 6))
    ax.bar(df['week'].astype(str), df['revenue'], color='#4A90D9')
    ax.set_title('Weekly Revenue — Last 12 Weeks', fontsize=14, fontweight='bold')
    ax.set_xlabel('Week starting')
    ax.set_ylabel('Revenue (USD)')
    ax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f'${x:,.0f}'))
    ax.tick_params(axis='x', rotation=45)
    plt.tight_layout()

    buf = io.BytesIO()
    fig.savefig(buf, format='png', dpi=150)
    buf.seek(0)
    plt.close(fig)
    return buf.getvalue()


def post_to_slack(image_bytes: bytes, title: str) -> None:
    client = WebClient(token=SLACK_TOKEN)
    try:
        client.files_upload_v2(
            channel=SLACK_CHANNEL,
            file=image_bytes,
            filename='weekly-revenue.png',
            title=title,
            initial_comment=f'*{title}* — {datetime.today().strftime("%B %d, %Y")}'
        )
    except SlackApiError as e:
        print(f'Slack error: {e.response["error"]}')
        raise


def main():
    df = query_bigquery()
    image_bytes = render_chart(df)
    post_to_slack(image_bytes, 'Weekly Revenue Report')


if __name__ == '__main__':
    main()

Deploy to Cloud Run + Cloud Scheduler

# Build and push container
gcloud builds submit --tag gcr.io/YOUR_PROJECT/bq-slack-chart

# Deploy as Cloud Run job
gcloud run jobs create bq-slack-chart \
  --image gcr.io/YOUR_PROJECT/bq-slack-chart \
  --region us-central1 \
  --set-env-vars SLACK_BOT_TOKEN=xoxb-...,SLACK_CHANNEL_ID=C01234...

# Schedule weekly on Monday at 9am UTC
gcloud scheduler jobs create http bq-slack-chart-weekly \
  --schedule "0 9 * * 1" \
  --uri "https://us-central1-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/YOUR_PROJECT/jobs/bq-slack-chart:run" \
  --message-body '{}' \
  --oauth-service-account-email YOUR_SA@YOUR_PROJECT.iam.gserviceaccount.com

What you're responsible for maintaining

  • Slack bot token rotation (bots tokens don't expire, but workspace admins can revoke)
  • BQ schema changes that break your query
  • Python dependency updates (matplotlib, slack_sdk, etc.)
  • Cloud Run/Scheduler permissions and IAM roles
  • Error alerting (add a try/catch that pings a Slack channel or sends email on failure)

This is the right choice when you need custom chart styling, multi-panel layouts, or transformations that don't fit in a Sheets chart.


Approach 3: Chartcastr with BigQuery Direct Source

Chartcastr supports BigQuery as a first-class source — no Sheets intermediary needed.

  1. Connect your GCP project (service account or OAuth)
  2. Write your query or select a saved view
  3. Chartcastr renders the chart from the query results
  4. Set the Slack channel and schedule

This combines the directness of the Python approach with the maintenance-free reliability of a managed service. It also adds the AI summary on every post — the query results are analysed and a plain-English explanation of what changed is included automatically.


Comparison

Connected Sheets + ChartcastrPython + Cloud RunChartcastr Direct
Setup time~10 min2–4 hours~5 min
Coding requiredNoneYesNone
Custom chart stylingLimited (Sheets charts)Full controlModerate
AI summaryYes (via Chartcastr)DIYYes
Maintenance burdenLowHighLow
Best forNon-engineers, fast setupEngineering teams, custom chartsAnyone wanting direct BQ integration

A Note on "Embedding" vs "Posting"

If you searched for "embed BigQuery chart in Slack" — the answer is that you can't embed a live, interactive chart in Slack at all. Slack's message format does not support iframes, HTML, or dynamic widgets of any kind.

Every approach above produces a static image (PNG) that appears inline in the Slack channel. That's the correct model. The chart is rendered at post time from the latest data, so it's always current — it just isn't interactive once it's in Slack.

If your team needs to drill down after seeing the chart, the right pattern is a Slack thread: people ask questions, and a bot (like the one Chartcastr provides) answers them against the live data.


Related Posts

Frequently Asked Questions

Was this post helpful?

Google SheetsSlackAI Summaries

Turn your data into automated team updates.

Connect a data source, create charts, and deliver AI-powered insights to Slack or email — in minutes.

No card required. Setup in 2 minutes.

Chartcastr