◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude.ai
◉ Claude.ai/2026-03-21Advanced

Building Dashboards with Claude Artifacts — Why I Aggregate Before Pasting, and the Setup That Stuck

Claude artifacts render as React components. Working from that fact, here is how I stopped pasting raw CSVs and settled on a setup that separates preprocessing, data contracts, and refresh paths.

Claude52ArtifactsVisualizationsDashboardsData PreprocessingIndie Development11

✦ Premium Article

At the end of each month I open AdMob, App Store Connect, and Firebase in turn and export the CSVs. Running several wallpaper apps as an indie developer, I would rather decide where the next update goes by looking at numbers than by going on instinct.

For a long time I pasted those CSVs straight into Claude. A few seconds later, a plausible line chart appeared. It felt good.

What tripped me up was noticing that the weekly peak in the chart sat one day away from the peak in my own spreadsheet.

The cause turned out to be almost disappointingly simple. The data I pasted was never read all the way through.

An artifact renders as a React component. Whatever data you hand over has to get transcribed into an array literal inside that code at some point. Pasting 4,312 rows of CSV is, in effect, asking for 4,312 objects to be typed out one by one. That is not what happens.

What follows is the setup I arrived at once I accepted that constraint — the path from raw exports to numbers I can actually make decisions with, along with the code I run today.

The CSV You Pasted Did Not Arrive Intact

I started by confirming what was happening, with numbers.

The 90-day daily report exported from AdMob was 4,312 rows and 178 KB. I pasted it, opened the source of the generated artifact, and counted the elements in the data array. There were 117.

Nothing announced this. There was no note saying "I sampled representative values" — just a line chart, drawn without comment. The weekly peak sat a day off because, after the thinning, an adjacent day happened to look like the high point.

I do not think of this as a defect. Output tokens are finite, and transcribing 4,312 rows would consume most of a response on its own. Fitting the data to what can reasonably be written is a sensible call.

The problem was that I never asked for that call to be made.

How to Check

When you suspect the same thing, opening the artifact's code is the fastest route.

  1. Open the artifact's code view
  2. Find the data array declaration (the equivalent of const data = [...])
  3. Count the elements and compare against your source row count
  4. If they diverge, do not use that chart for decisions

A gap of three rows is rounding. A gap of four thousand is a different dataset. Since I started making this check, I have stopped second-guessing my charts.

Do the Aggregation Yourself — 178 KB Down to 3.1 KB

The approach I settled on was to stop handing the aggregation to Claude at all.

Of those 4,312 daily rows, the only thing that drives a decision is the weekly trend per app. So I fold the data into that shape before it goes anywhere. Here is the script I actually use. It reads the AdMob daily export and emits weekly JSON.

# aggregate_admob.py — fold an AdMob daily CSV into a weekly summary JSON
# usage: python3 aggregate_admob.py admob_daily.csv > weekly.json
import sys
import json
import pandas as pd
 
REVENUE = "Estimated earnings (USD)"
IMPRESSIONS = "Impressions"
 
def main(path: str) -> None:
    df = pd.read_csv(path)
    df["Date"] = pd.to_datetime(df["Date"])
 
    # Pin weeks to a Monday start. Skip this and your totals land
    # one day off from the console's own weeks (mine did).
    df["week"] = df["Date"].dt.to_period("W-SUN").dt.start_time
 
    grouped = (
        df.groupby(["App", "week"])
        .agg(revenue=(REVENUE, "sum"), impressions=(IMPRESSIONS, "sum"))
        .reset_index()
    )
 
    # eCPM is total revenue / total impressions * 1000.
    # Averaging daily eCPM instead inflates the number, because
    # low-impression days carry the same weight as busy ones.
    grouped["ecpm"] = (grouped["revenue"] / grouped["impressions"] * 1000).round(2)
    grouped["revenue"] = grouped["revenue"].round(2)
    grouped["week"] = grouped["week"].dt.strftime("%Y-%m-%d")
 
    records = grouped.to_dict(orient="records")
    print(json.dumps(records, ensure_ascii=False, separators=(",", ":")))
 
if __name__ == "__main__":
    main(sys.argv[1])

Here is what I measured.

MetricPasting the raw CSVPasting the weekly fold
Payload178 KB / 4,312 rows3.1 KB / 26 rows
Input tokens (approx.)~47,000~900
Elements in the artifact117 (thinned)26 (complete)
Agreement with the numbersWeekly peak off by one dayMatches the spreadsheet
Cost of regeneratingRe-paste everything each timeSwap the JSON

That is roughly a 98% token reduction, but the tokens were never the point. What mattered was that every row now arrives, and that rebuilding got cheap.

The W-SUN line is the bug itself, written down. Pandas defaults its weekly periods to a Sunday close, which lands a day away from how the ad console groups its weeks. Pinning it made the original one-day gap disappear.

If you would rather keep the aggregation on Claude's side, the setup in handing monthly revenue CSV aggregation to the Claude API Code Execution tool is close in spirit. When you do not want Python sitting on your own machine, that is the more natural road.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A working pandas preprocessing script that takes a 178 KB raw CSV down to 3.1 KB — a measured ~98% token reduction
✦A cron + static JSON setup that delivers a daily-refreshing dashboard despite artifacts having no external fetch and no localStorage
✦A 4-step data contract that stopped keys and axes from drifting on every regeneration
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◉ Claude.ai2026-10-16
I Stopped Asking Claude to 'Cite Sources' — Three Ways I Ask Instead
Three ways I check Claude's answers before they go into a published text, instead of asking for sources: leave room to say 'I don't know', pull verbatim quotes first, and have each claim re-checked against the source after drafting. Plus what still needs a human.
◉ Claude.ai2026-10-15
Let Claude Draft the Quote's Wording, Keep the Numbers in Your Own File — A Small Source of Truth and a Check Script
How a freelancer can have Claude polish the wording of quotes and invoices while the amounts stay in a JSON file and get verified by a short Python script.
◉ Claude.ai2026-10-09
I Kept Rewriting My First Request to Claude Until I Handed It One Page About My Work
If Claude's answers keep missing the mark, the cause may not be your wording. It may be that you never explained your work. Here is how a non-engineer can write a one-page work description today and fold it into the first request.