◉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 Code
⟐ Claude Code/2026-05-23Advanced

A Daily Revenue Pipeline for 4 Wallpaper Apps: 8 Weeks Running App Store Connect API + AdMob With Claude Code

An 8-week record of running App Store Connect API and AdMob Reporting API through a Python pipeline for 4 iOS wallpaper apps, with Claude Code helping absorb JWT auth quirks, gzip TSV edge cases, timezone misalignment, and Slack alerting thresholds — written from an indie developer's perspective.

Claude Code264App Store Connect APIAdMob12Indie Development11Revenue AnalyticsiOS26

✦ Premium Article

A Slack message arrives at 7:00 JST every morning with yesterday's revenue, AdMob earnings, and Day 7 subscription retention for all four of my wallpaper apps, on a single line. It took about eight weeks to reach that state.

I have been building iOS apps as an indie developer since 2014, and even after my apps crossed 50 million cumulative downloads, my morning routine was still to open App Store Connect, then AdMob, then Firebase one by one and copy numbers into a Google Sheet. It is the kind of work that is dull and easy to get wrong. Forgetting to do it on a weekend means stacking three days of work on Monday morning.

To hand off this routine for good, I asked Claude Code to build me a Python pipeline that runs at 7:00 JST every morning, calls App Store Connect API and AdMob Reporting API for all four apps, joins them on a JST date axis, and writes back to a dashboard. Writing the first draft took a day. Making it boring and reliable in production took eight weeks. Below are the specific traps I hit along the way and where Claude Code actually mattered.

Why "just look at the dashboards" did not work

App Store Connect, AdMob, and Firebase all offer fine dashboards. I still built my own pipeline for three reasons.

The first reason is timezone mismatch. App Store Connect's daily reports use US Pacific time (UTC-8). My AdMob account also runs in Pacific. Firebase Analytics is set to JST on some projects and Pacific on others. Comparing "revenue for May 22, 2026" across three dashboards requires constant mental correction across slightly different windows. Running four apps in parallel as an indie, doing that adjustment in your head all day is not sustainable.

The second reason is the lookback window. AdMob's UI gives you two years, but if you want the same weekly aggregate every Friday, accumulating it yourself is dramatically faster. After eight weeks of operation, I have 180 days of daily data sitting in a local SQLite database. When I run a new experiment I can ask "how does this compare to the same weekday over the past six months" on the spot.

The third reason is cross-app signal latency. When you look at four apps' revenue, eCPM, and retention separately, small signals like "wallpaper app A's AdMob CPM has been dropping since Friday" disappear into the noise. A single dashboard surfaces those. In the past eight weeks, my morning Slack digest caught a 38% Wednesday-to-Thursday eCPM drop on one rewarded ad unit, and I flipped the mediation order over the weekend instead of finding out the following Tuesday.

Overall structure: what to fetch, where to put it

The first decision I made with Claude Code was about storage and granularity. For a single indie developer with four apps, jumping straight to Cloud SQL or BigQuery felt like overkill.

[06:30 JST every morning]                       [07:00 JST every morning]
  asc_fetcher.py  ──┐                            ┌──> dashboard_sync.py
  admob_fetcher.py ─┤  ──> raw_*.csv (S3) ──>    │     (Google Sheets writeback)
  fbase_fetcher.py ─┘                            ├──> slack_digest.py
                                                 │     (day-over-day + anomalies)
                          merger.py               └──> revenue.db (SQLite)
                            │
                            └──>  daily_summary table

I put S3 in the middle on purpose so that API fetching and aggregation are fully decoupled. If an API call fails due to rate limits or an auth error, the raw CSV still exists, and the aggregation layer can be re-run as many times as needed. Telling Claude Code from the start "I want fetch, transform, and deliver split into three phases so I can isolate failures later" paid off later many times over.

SQLite was enough. Eight weeks of data sit in about 200 KB. Being able to drop into sqlite3 from the terminal whenever I want to reshape the schema is genuinely useful for an evolving setup.

✦

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 ES256 JWT auth flow for App Store Connect API that avoids the DER vs raw signature trap, plus retry strategy for empty Sales Reports (200 status with header-only TSV)
✦Concrete AdMob Reporting API v1beta report spec with currency JPY, language ja, and timeZone Asia/Tokyo, plus a SQLite schema that joins ASC and AdMob on a single JST date axis
✦Six battle-tested alerting rules from 8 weeks of operation, including a 14.3% AdMob eCPM recovery I caught only because the morning Slack digest surfaced a Wednesday-to-Thursday drop
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 Code2026-10-12
After Moving to SPM, My App Died the Moment It Launched — Reading One dyld Line with Claude Code
Right after moving from CocoaPods to Swift Package Manager, the build passed but the app died on launch. Here is how I traced one dyld line to its cause, the fix, and how to verify it.
⟐ Claude Code2026-09-10
One plugin.json turns a shelf of skills into something you can switch off as a group
When I sorted my skills by what work they belong to, the set I open almost daily turned out to be 5 percent of the total. Here is how I dropped a .claude-plugin/plugin.json into each group so I could switch a whole shelf off, plus the two loading rules I tripped over on the way.
⟐ Claude Code2026-09-04
The One File I Keep Out of Claude Code's Reach: project.pbxproj
With Xcode project files, the breakage that still opens costs far more than the breakage that refuses to open. Here is what I measured before moving every edit behind a script, and where the line sits today.