Create a Chart from BigQuery and Post it to Slack
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
- Open a new Google Sheet
- Go to Data → Data connectors → Connect to BigQuery
- Select your GCP project and write or paste your query
- 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 DataFramepandas+matplotlib(orplotly) — render the chartslack_sdk— upload the image viafiles_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.
- Connect your GCP project (service account or OAuth)
- Write your query or select a saved view
- Chartcastr renders the chart from the query results
- 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 + Chartcastr | Python + Cloud Run | Chartcastr Direct | |
|---|---|---|---|
| Setup time | ~10 min | 2–4 hours | ~5 min |
| Coding required | None | Yes | None |
| Custom chart styling | Limited (Sheets charts) | Full control | Moderate |
| AI summary | Yes (via Chartcastr) | DIY | Yes |
| Maintenance burden | Low | High | Low |
| Best for | Non-engineers, fast setup | Engineering teams, custom charts | Anyone 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
- Create a Chart from a Data Source and Embed it in Slack — the broader guide covering Google Sheets and CSV sources
- Create a Chart from Google Sheets and Post it to Slack
- Best Tool to Load Google Sheets to BigQuery (2025/2026 Guide)
- Why You Shouldn't Screenshot BigQuery Charts
- Top 10 Ways to Send Automated Data Pulses to Slack
- Slack vs. Dashboards: Why the Best Data Teams Push, Not Pull