Home / Blog / The newsletter writer's guide to AU economic data automation
2026-05-20 · Harry Vass
The newsletter writer's guide to AU economic data automation
A working stack, Slack digest, tweet thread, Notion sync, Discord bot, for AU newsletter writers who want live cash-rate, CPI, and unemployment numbers in their drafts without copy-pasting from rba.gov.au.
A friend who writes a weekly AU macro newsletter (~6k Substack readers, mostly retail-investor adjacent) told me his Tuesday morning ritual:
> Open rba.gov.au in one tab. Open abs.gov.au in another. Open the ABS CPI media release PDF. Copy-paste the cash rate, copy-paste headline CPI, copy-paste trimmed mean, sanity-check the unemployment rate against my last issue, then write 600 words. The data-gathering part alone is 20-30 minutes.
That's 20-30 minutes per week to find numbers that haven't changed in any creative way since 1992. The numbers are public, they're free, they're CC-BY licensed for exactly this kind of reuse. There's no reason for them to live in a browser tab.
This post is the stack I helped him build. Four pieces, Slack digest, tweet thread, Notion sync, Discord bot, all wired to one free API key. Total setup: 45 minutes. Total ongoing time: zero.
<!-- IMG: newsletter-automation-stack-diagram.png -->
The stack
[ABS / RBA / WGEA / AEMO / AIHW ... ]
↓
ausdata.io
↓
┌────────┬─────────┬─────────┬─────────┐
↓ ↓ ↓ ↓ ↓
Slack X/Twitter Notion Discord Substack
digest thread page bot draft
One API key. Four destinations. The data is cited and licensed the same way in every channel, source, source_url, attribution, retrieved_at in every response.
Piece 1, the Slack digest (Monday 7am AEST)
A scheduled Python script that posts one Slack message every Monday morning so the writer wakes up to the week's macro numbers already curated.
import os, requests
from datetime import datetime
API_KEY = os.environ["AUSDATA_API_KEY"]
SLACK_WEBHOOK = os.environ["SLACK_WEBHOOK_URL"]
def get(path):
r = requests.get(
f"https://api.ausdata.io{path}",
headers={"X-API-Key": API_KEY}, timeout=30,
)
r.raise_for_status()
return r.json()
d = get("/v1/economic-dashboard")["data"]
real = get("/v1/real-rate-regime")["data"]
msg = (
f"*AU macro digest, {datetime.now():%a %d %b %Y}*\n"
f"> Cash rate: *{d['cash_rate_pct']}%* (real: {real['real_cash_rate_pct']:+.2f}%)\n"
f"> CPI annual: *{d['cpi_annual_pct']}%* | Unemployment: *{d['unemployment_rate_pct']}%*\n"
f"> Real wages YoY: *{d['real_wages_yoy_pct']:+.1f}%*\n"
f"_Source: ausdata.io, RBA + ABS, CC-BY 4.0_"
)
requests.post(SLACK_WEBHOOK, json={"text": msg})
Run it on a GitHub Actions cron ('0 21 * * 0' is Sunday 21:00 UTC = Monday 07:00 AEST). Zero hosting cost. Five lines of YAML in .github/workflows/digest.yml:
on:
schedule: [{cron: '0 21 * * 0'}]
jobs:
digest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install requests
- run: python digest.py
env:
AUSDATA_API_KEY: ${{ secrets.AUSDATA_API_KEY }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
That's the full weekly numbers pipeline. He uses the Slack message as his draft skeleton.
Piece 2, the tweet thread (auto-draft, manual send)
For release days. The writer doesn't want a bot auto-tweeting in his voice, but he does want the numbers pre-formatted so he can paste and edit:
def cpi_thread():
d = get("/v1/data/abs/CPI?limit=2")["data"]
latest, prior = d[0], d[1]
yoy_change = latest["value"] - prior["value"]
return [
f"AU CPI for {latest['period']}: {latest['value']}% annual.",
f"That's {abs(yoy_change):.1f} pp {'up' if yoy_change > 0 else 'down'} on last quarter.",
f"Trimmed mean: {latest.get('trimmed_mean_pct', 'n/a')}%.",
f"Source: ABS, retrieved {latest['period']}. Full series via ausdata.io.",
]
for t in cpi_thread():
print(t)
print("---")
He pastes the output into TweetDeck on release day. The factual core is done; he adds the editorial voice.
Piece 3, the Notion sync (read-only mirror of the dashboard)
For the writer's research dashboard. A Notion page he opens daily that always shows the latest numbers, without him having to refresh anything.
The Notion API doesn't natively support API-driven blocks, so the pattern is: a cron job updates a single Notion database row every 15 minutes via the Notion API.
import os, requests
from notion_client import Client
notion = Client(auth=os.environ["NOTION_TOKEN"])
DB_ID = os.environ["NOTION_DB_ID"]
d = get("/v1/economic-dashboard")["data"]
notion.pages.update(
page_id=os.environ["DASHBOARD_ROW_ID"],
properties={
"Cash rate": {"number": d["cash_rate_pct"]},
"CPI annual": {"number": d["cpi_annual_pct"]},
"Unemployment":{"number": d["unemployment_rate_pct"]},
"Updated at": {"date": {"start": d["retrieved_at"]}},
},
)
Notion treats the row as live editable data, the writer's dashboard page renders it as a callout block. See piece 4 of the next post in this series for the full Notion recipe.
Piece 4, the Discord bot (for paid-subscriber channel)
His paid subscribers get a private Discord channel. The bot exposes !cpi, !cashrate, !dashboard commands so subscribers can pull live numbers themselves without bothering him. Full code is in the Discord-bot post, 30 lines, deploys to Fly.io for $0/month.
What this isn't
This stack isn't:
- A replacement for the writer's editorial judgement. The numbers are the easy part; "what does it mean?" is what subscribers pay for.
- A way to schedule Substack drafts directly, Substack's API is anaemic. You still paste the draft yourself.
- A research tool for sector-level deep dives. The macro dashboard is for top-of-newsletter; subjects like state-level unemployment, housing finance, or industry-level pay gap need separate calls (
/v1/data/abs/...,/v1/gender-pay-context, etc.). - A way to monetise public data behind a paywall, the CC-BY licence requires attribution and forbids you implying endorsement. Cite ausdata.io OR the underlying ABS/RBA source. Both work.
Why a paid API for free data
Two reasons the writer paid the $29 Analyst tier within two weeks of trying the free tier:
First, the free tier is 500 calls/month. A Monday digest + a daily Notion sync alone is ~150 calls/month. Add an active Discord channel and a CPI-release-day tweet thread and you're at 800-1500. Analyst tier is 10k.
Second, the stale fallback matters more than people think. The ABS website goes down maybe 4-6 times a year. The RBA's F-table HTML gets refactored maybe 2-3 times a year. When that happens, ausdata.io serves the cached value with stale: true, stale_reason: "ABS 503 at 14:32 AEST". The writer's Monday digest doesn't break. That alone is worth the $29.
Pricing
- Free: 500 calls/month, fine for a Slack digest + occasional Notion refresh.
- Analyst: $29/mo, 10k calls, covers the full four-piece stack with headroom.
- Pro: $99/mo, 100k calls + webhooks, when you want auto-pushed alerts on ABS / RBA releases instead of polling.
Get a free key at ausdata.io. No card, no trial expiry.