Robinhood · Data Analytics

Marketing Channel Performance

The work

What they wrote about it

Robinhood Marketing Channel Performance - H1 2025

Analyze six months of paid marketing against free account signups and Robinhood Gold subscriptions, then recommend an H2 2025 budget. This is an educational analysis of the supplied challenge data.

Recommendation: prioritize brand search and retargeting, which return $1.91 and $1.34 in first-90-day revenue per dollar of ad spend. Their combined proposed allocation is about 61%. Reduce generic search and prospecting video, and keep display paused unless a redesigned, capped experiment is approved. ROAS measures revenue efficiency; it does not include operating costs or prove profitability.

Reproduce the analysis

Requires Python 3.11+.

python -m pip install -r requirements.txt
python main.py
python -m unittest discover -s tests -v

Run these commands from the repository folder. Paths resolve relative to the project, and running the script recreates the result tables, nine charts, and this README. No credentials or external data services are required.

Data validation and methodology

  • Datasets/ad_spend.csv: 1,046 raw records; 7 repeated date/channel/campaign records removed, leaving 1,039. Raw spend is $125,806.46; cleaned spend is $124,886.22. All metrics use cleaned data.
  • Datasets/signups.csv: 18,840 users with unique IDs. Both files cover January 1 through June 30, 2025.
  • Normalize channel names by stripping whitespace, lowercasing, and replacing spaces with underscores. Match the channel/campaign pairs, rather than campaign names alone, before joining.
  • The six paid campaigns match across files. Reject unmatched pairs, duplicate user IDs, invalid dates, missing or negative ad metrics/revenue, and payments before signup.
  • Empty campaigns belong to organic/referral users. Empty payment dates indicate users who have not paid; missing campaigns are represented as none for aggregation.
  • A paying subscriber has a nonempty subscription_start_date. This counts observed payments, not specifically conversions within 90 days. ROAS uses the supplied first-90-day revenue field.
  • Aggregate spend and outcomes independently, then join on normalized channel and campaign. Channel ratios use summed numerators and denominators, not averages of campaign ratios.
MetricFormula
CTRclicks / impressions
CPCspend / clicks
CPA (cost per signup)spend / signups
CPP (cost per paying subscriber)spend / paying subscribers
Signup-to-paying conversionpaying subscribers / signups
90-day ROASfirst-90-day revenue / spend

Undefined ratios with zero denominators remain blank rather than being treated as zero cost. Organic/referral acquisition costs are unavailable, so their CPA and ROAS are not estimated.

Campaign performance

CampaignSpendSignupsPayingCPACPPROASConversion
brand_search$10,196.992,177465$4.68$21.931.91x21.36%
retargeting$12,987.222,465418$5.27$31.071.34x16.96%
generic_search$35,939.182,008432$17.90$83.190.47x21.51%
finance_blogs$15,392.44864162$17.82$95.020.41x18.75%
prospecting_video$33,915.736,484213$5.23$159.230.26x3.29%
display_network$16,454.662688$61.40$2,056.830.03x2.99%
CampaignImpressionsClicksCTRCPC
brand_search267,76312,0894.51%$0.84
retargeting1,084,88017,2451.59%$0.75
generic_search898,18822,1532.47%$1.62
finance_blogs706,6117,7521.10%$1.99
prospecting_video5,662,30068,0851.20%$0.50
display_network6,605,0317,8440.12%$2.10

Paid campaigns generated 14,266 signups, 1,698 paying subscribers, and $69,443.91 in 90-day revenue on $124,886.22 spend: approximately 0.56x aggregate ROAS. Only brand search and retargeting have 90-day revenue above their ad spend.

Full metrics: campaign table, ad metrics.

ChannelSpendSignupsPayingCPACPPROASConversion
paid_search$46,136.174,185897$11.02$51.430.79x21.43%
paid_social$46,902.958,949631$5.24$74.330.56x7.05%
affiliate$15,392.44864162$17.82$95.020.41x18.75%
display$16,454.662688$61.40$2,056.830.03x2.99%

Search leads at the channel level, but its aggregate ROAS remains below 1 because generic search consumes much more budget than brand search. Social similarly combines strong retargeting with low-converting prospecting video. Allocate within channels at campaign level to avoid hiding these differences.

The complete paid-channel table also includes impressions, clicks, CTR, CPC, and revenue.

SourceSignupsPayingConversion90-day revenue/user
referral1,06321219.94%$8.91
paid14,2661,69811.90%$4.87
organic3,5113459.83%$4.12

Referral converts at about 1.68 times the combined paid rate and generates the highest revenue per signup. It outperforms aggregate paid traffic, but paid search converts slightly better than referral. Explore referral incentives separately; no referral costs are supplied, so these results do not establish that referrals are free or have better ROI.

Detailed channel comparison | Source comparison

Six-month performance

  • Brand search: monthly CPA ranges from $4.36 to $5.00; monthly ROAS ranges from 1.74x to 2.02x. It remains above 1x in every month.
  • Retargeting: monthly ROAS remains above 1x, but falls from 1.41x in January to 1.20x in June. June CPA rises to $5.69 from $4.97 in May; monitor before increasing spend.
  • Generic search: CPA rises from $17.50 in January to $18.88 in June, with a $19.83 peak in May. ROAS is below 1x every month.
  • Prospecting video: CPA improves from $5.44 to $4.99, while CPP improves from $226.51 to $148.14. June ROAS is still only 0.29x, so cheaper signups alone do not justify scaling.
  • Finance blogs: monthly ROAS ranges from 0.35x to 0.50x, below ad-spend recovery throughout the period.
  • Display: the last recorded ad date is May 14, 2025; there are no June ad records. Its May spend reflects a partial month. The data show cessation, but do not establish why it stopped. March and May have no paying subscribers, so CPP is undefined.

Monthly outcomes are grouped by signup month. Monthly revenue is each cohort's supplied 90-day revenue, not cash collected during that calendar month. The monthly table is anchored on observed spend; absent campaign months are listed separately in the activity table.

Monthly CPA, CPP, conversion and ROAS | Campaign activity and last ad dates

Which metric should guide the decision?

Use 90-day ROAS as the primary metric, supported by CPP and signup-to-paying conversion. ROAS compares attributed revenue with spend; CPP identifies how efficiently each campaign attracts paying users. CTR and CPC diagnose ad engagement and auction cost, but do not measure downstream value.

CampaignCPA rankCPP rankROAS rankCTR rankCPC rank
brand_search11113
retargeting32232
generic_search53324
finance_blogs44455
prospecting_video25541
display_network66666

The cheapest campaign is brand search for both CPA and CPP. The grading checklist's suggestion that the winner must differ is not supported by these data. The useful distinction is that the overall ranking changes: prospecting video is second by CPA but fifth by CPP, while retargeting moves from third by CPA to second by CPP. Prospecting video converts only 3.29% of signups, compared with retargeting's 16.96%. Optimizing CPA alone would favor low-cost signups without considering subscriber quality.

H2 2025 budget recommendation

Assume the next six-month budget equals cleaned H1 spend: $124,886.22. Reserve 5% per campaign, then distribute the remaining 70% in proportion to historical campaign ROAS:

allocation_i = 5% + 70% * (campaign_ROAS_i / sum_of_campaign_ROAS)

This is a transparent starting allocation, not a forecast or an optimized response model. The 5% reserve is a deliberate exploration policy, not evidence that every campaign deserves continued spending. Percentages total 100% before rounding, and every allocation meets the minimum.

CampaignH1 actualH2 allocationH2 budget
brand_search8.17%35.22%$43,983.20
retargeting10.40%26.22%$32,742.04
generic_search28.78%12.46%$15,560.80
finance_blogs12.33%11.49%$14,354.93
prospecting_video27.16%9.09%$11,352.46
display_network13.18%5.52%$6,892.80

Aggregated channel split:

ChannelH2 allocationH2 budget
affiliate11.49%$14,354.93
display5.52%$6,892.80
paid_search47.68%$59,544.00
paid_social35.31%$44,094.50
  • Brand search: increase its share, but release funds gradually and test incrementality because branded demand is finite.
  • Retargeting: scale with audience-size and frequency limits; monitor the softer June performance.
  • Generic search: reduce broad spending and test tighter keyword intent, bids, and landing pages.
  • Finance blogs: negotiate rates and test partner-level quality before expanding.
  • Prospecting video: reduce volume buying; test creative and audiences against paying conversion and ROAS.
  • Display: keep the existing campaign paused. Its allocation is a capped reserve for a redesigned test, released only with a measurable hypothesis and stop rule; otherwise hold it unspent pending the next review.

The table allocates available funds; conditional reserves may remain unspent. Referral experiments require a separately costed proposal rather than assuming zero-cost acquisition.

Exact campaign budget | Exact channel budget

Risks and controls

RiskImplication and response
Attribution and incrementalityCredited signups are not necessarily caused by the ads. Brand search may capture organic demand, and upper-funnel ads may assist other channels. Use holdouts before committing the full increase.
SaturationHistorical average ROAS will not necessarily hold at higher spend. Increase budgets in stages; inspect marginal CPA, CPP, and ROAS.
Retargeting dependencyRetargeting needs an eligible audience, potentially supplied by prospecting. Preserve controlled acquisition tests and monitor audience size/frequency.
Revenue window and maturity90-day revenue understates lifetime value. Confirm all cohorts have complete observation windows; compare matured cohorts before acting on recent performance.
Revenue versus profitROAS excludes product, servicing, incentive, and other costs. Add contribution margins and a business-specific hurdle before calling a campaign profitable.
Seasonality and concentrationH1 market conditions may differ in H2, and about 61% is allocated to two campaigns. Review allocations monthly and keep exploration funds available.
Small samplesDisplay has only eight paying users; its results are noisy. Any restart should be a capped experiment with predefined stop rules.

Review spend and acquisition metrics weekly and mature-cohort revenue monthly. As a proposed operational guardrail, investigate if brand-search CPA exceeds 130% of its H1 baseline (about $6.09). Pause further increases until CPP and mature-cohort ROAS support them. Do not automatically transfer funds to a historically weak campaign.

Repository contents

main.py                  Reproducible analysis entry point
src/config.py            Paths and plotting style
src/data_loader.py       Name alignment, validation and deduplication
src/metrics.py           Campaign and paid-channel metrics; rankings
src/comparison.py        Paid, organic and referral comparisons
src/trends.py            Monthly metrics and campaign activity
src/visualizations.py    Nine chart functions
src/report.py            README generation from calculated results
src/eda.py               Optional deeper exploration; not part of default run
Datasets/                Two supplied CSV inputs
outputs/tables/          Campaign/channel metrics, comparisons, trends and budgets
outputs/figures/         Nine PNG figures
tests/                  Pipeline regression checks

Submission

The scripts, input data, result tables, charts, and written recommendation form the local deliverable. Publish this folder to a public GitHub repository, verify its URL works when signed out, and submit that URL in the challenge's Solutions tab. Public repository availability is a separate submission step; it cannot be established from these local files.

The brief this answers

Compare six months of ad spend against signups and paying customers, then recommend where the next budget should go.

Try it yourself

Other answers to this brief

The same problem, solved by somebody else.

Discussion

Nothing here yet. Ask how they approached it, or tell them what you would have done differently.