MuftiPortfolio← All projects
Case study · Internal system

Ads Reporting Automation

Daily, hands-off reporting for 40+ Meta ad accounts across several Business Portfolios, synced two-way with the team’s Google Sheets and a web dashboard.

Meta Graph / Marketing APIGoogle Apps ScriptGoogle SheetsPHPMySQL
RoleFull Stack Developer (built and maintain)
Scope40+ Meta (CPAS) ad accounts, 2 brands
CadenceAuto-pull 01:00 & 08:00, validation every morning
UsersAds team, owner
Ads dashboard — per-account cards and funnel breakdown (figures blurred).
Ads dashboard — per-account cards and funnel breakdown (figures blurred).

01Problem

The ads team copied spend, purchases and revenue from dozens of ad accounts into monthly sheets by hand. It was slow, numbers were often missing or wrong for recent days, and every new month meant rebuilding files and formulas.

The goal: numbers that match Ads Manager without anyone touching them, while keeping the team’s existing sheets and formulas.

02Architecture

Meta Graph APISpend at campaign level, conversions at account level
Apps ScriptPer-portfolio tokens, 3-day re-sync, monthly files
Google SheetsTeam’s monthly files & rollup formulas
Web dashboardLocal copy of data, edits sync back to sheets

Each ad account is read with the System User token of the portfolio that owns it, so no account depends on a single token.

03My contribution

04Technical challenges

Spend not matching Ads Manager

Account-level spend included campaigns the team does not count, while conversions were double counted at campaign level.

Spend summed at campaign level for active/paused campaigns only, conversions read once at account level with the same attribution window as Ads Manager.

Accounts in different portfolios

Some accounts returned permission errors (#200) because they were read with the wrong token.

Mapped every account to its owning portfolio’s System User token and made the live fetch use the same mapping as the scheduled sync.

API rate limits

Pulling every account at once hit Meta’s request limit (code 17).

Queued requests with back-off and retries; temporary errors are retried instead of being shown as failures.

Editing formulas owned by another team

The rollup sheet’s formulas belong to the ads team and must not be rewritten.

Insert-only change that mirrors the last account’s pattern, verified so the old formula is unchanged apart from the insertion, with automatic backup.

05Results

06Evidence

Internal company system. Source code is private; screenshots are blurred to hide business figures and personal data. Happy to walk through the code and architecture in an interview.