Pricing Academy Blog

Claude Code for funnel data analysis

What Claude Code can do with your funnel data through the ef CLI, then one full analysis replayed prompt by prompt: the commands it ran, the tables it built and the three times it was wrong.

Summarize with:
Illustration: the monitor mascot, a terminal prompt on its screen, holds a magnifying glass up to a four-step funnel next to a bar chart
The short answer

Claude Code for data analysis works when the data sits behind commands it can run. Bind a folder to your brand with the ElasticFunnels CLI and it reads sales by funnel, every step of a funnel, per-buyer upsell takes, affiliates and single visits, then joins them into tables and checks one number against another.

It is fast and it is often right. It can also be confidently wrong, so make it prove every claim from orders before you act on it. Below is a real analysis, replayed prompt by prompt, including the three times it was wrong.

Most funnel reports get read the same way. Someone opens the dashboard, sees a conversion rate that looks bad, and changes a page. Half the time the page was fine and the number was measuring something else.

I spent nine years as a CTO building funnel systems, and the analysis part never got faster: export, join, pivot, argue about which number is right. This post is about handing that part to Claude Code. The walkthrough is one real session on a live supplement brand. The brand here is our demo brand, Northwind Supplements, and every name, id and number is changed, but the questions, the commands, the ratios and the mistakes are the ones that happened.

What Claude Code can do with your funnel data

Claude Code is Anthropic's AI agent for the terminal: it reads files, runs commands and writes code. On its own it knows nothing about your funnel. With the ef CLI in the folder, it can run the same analytics the dashboard reads, scoped to one brand by that folder's key.

Illustration: four cards in a cycle, a chat bubble, a terminal prompt, a table and a lightbulb, joined by clockwise arrows
You ask in plain English, it runs ef commands, builds a table and tells you what it means. Then you ask the next question, or challenge the answer.

What it can read and work out, with the command behind each:

QuestionWhat it runs
Which funnel, page, product, affiliate, country, device or UTM makes the moneyef stats by <field>
Headline numbers for any scope: revenue, AOV split into main and upsell, refunds, profitef stats --funnel / --aff / --page
How buyers move through a funnel, step by stepef funnels product-flow + ef stats by page --funnel + ef products list
What each package's buyers bought next (per buyer, not per session)ef orders buyers --funnel
What one visitor actually did, page by pageef sessions show
Whether a split test has a resultef stats split (the app's significance check, never its own)

With those it can build a funnel's step table, measure take rate per buyer, profile an affiliate, reconcile two numbers that disagree, spot traffic that isn't people, and write a brief a developer can act on. When you decide something, it can act on it: end a split test on the variant you chose (ef splits winner), or change a page-event rule. Reading is read-only by design. ef stats has no write path at all.

The rules it follows

ef init installs four analysis skills into the project: ef-stats, ef-funnel-analysis, ef-upsell-diagnosis and ef-funnel-performance (plus ef-page-events for changes). They are the guardrails, and every one of them exists because an analysis went wrong without it:

  • Every number comes with its range and timezone. The CLI counts days in your computer's timezone unless the project says otherwise, and your computer is not your market.
  • Unavailable is not zero. A metric the brand doesn't track is reported as "not tracked", never as 0.
  • The comparison is the previous period of the same length, not last year. A rise in a cost metric is bad news.
  • --limit truncates, and says so. "Most traffic is X" needs the row count first. The blank row is direct or untagged traffic, not a mystery source.
  • Checkout pages are not funnel steps. The checkout is chosen when the buy link is clicked, from the active merchant for that domain (which you can override), so a checkout missing from the funnel graph is normal.
  • Buyers are not sessions. Pages after checkout undercount sessions badly, so upsell and routing claims are checked against orders.
  • It never calls a split test early, and never runs its own significance test. It reads the app's result and says when the call is yours.

What it can't do, or shouldn't

  • See other brands. The folder's key opens one brand. There is no account-wide roll-up.
  • Know why you ran a test. The server stores numbers, not intent. Write the hypothesis in split-tests.md and it reads it.
  • Be right every time. It reasons well and sometimes reasons past the data. You'll see it happen three times below. Challenge anything that would change money.

Setting up Claude Code for data analysis

You need an ElasticFunnels brand (the CLI and every report it reads are on every plan), Node.js 18 or newer, and Claude Code signed in to your Claude account. Then, once:

$ npm i -g @elasticfunnels/cli
$ mkdir northwind && cd northwind
$ ef init                                      # browser sign-in, pulls the brand, installs the skills
$ ef config set analyticsTz America/New_York   # the timezone your brand reports in
$ claude

ef init writes a CLAUDE.md with the commands and rules, installs the skills into .claude/skills, and adds a session hook that keeps the folder current. In a folder you bound earlier, ef claude refreshes the guidance and skills. If ef orders --help errors, your CLI is older than these commands: run ef update --check.

Prefer to follow along one step at a time, with a clip per step? The Academy tutorial analyse your funnel data with Claude Code covers the same setup and the first three questions.

A real funnel analysis with Claude Code, replayed

Northwind sells one product, Vitality, a daily supplement, in 2, 3 and 6-bottle packages, mostly through affiliates. Four of them matter here: Maya, who runs Meta ads and brings the most revenue; Dev, who sends cold mobile Google Ads traffic to a VSL (video sales letter); the house email list; and Carlos and Lucía, who bid on search. Each step below is what the owner typed, what Claude Code ran, a trimmed copy of the output and what it concluded.

1 · The overview

"Show me sales by funnel"

show me sales by funnel
$ ef stats by funnel_id --metrics sales,revenue,main_sales,upsell_sales,sessions,conversion_rate,aov --range 30d
by funnel_id  last 30 days (2026-08-30 → 2026-09-28, Europe/London)
FUNNEL_ID          SALES  REVENUE     MAIN_SALES  UPSELL_SALES  SESSIONS  CONVERSION_RATE  AOV
-----------------  -----  ----------  ----------  ------------  --------  ---------------  -------
Main VSL funnel    133    $21,854.35  97          16            3,950     2.46%            $225.30
Maya partner       123    $27,880.00  90          11            226       40.27%           $271.40
(unlabeled)        59     $14,580.20  38          18            43        125.58%          $231.43
Mobile VSL funnel  23     $4,430.10   14          3             487       3.08%            $247.90
Quiz test          0      $0.00       0           0             21        0.00%            -
Advertorial v2     0      $0.00       0           0             14        0.00%            -
Webinar draft      0      $0.00       0           0             9         0.00%            -
Spanish VSL        0      $0.00       0           0             6         0.00%            -
Holiday offer      0      $0.00       0           0             2         0.00%            -

338 sales and about $68.7k in the last 30 days, counted in Europe/London time.

  • Maya's funnel brings in the most, but a 40% conversion rate isn't real. Most of her buyers probably reach checkout without a tracked session on these pages. Compare it on sales and revenue, not on rate.
  • The unlabeled row is $14.6k of orders with no funnel. More sales than sessions is why it reads 125%. That's revenue no funnel gets credit for.
  • Main VSL funnel gets almost all the traffic and converts at 2.46%. The mobile VSL funnel converts a little better on an eighth of the traffic.
  • The five funnels with no sales had 52 sessions between them. They're unlaunched, not failing.

This project has no analytics timezone, so days are counted in this machine's zone. If the brand reports in US time, ef config set analyticsTz America/New_York and the day boundaries move.

The owner set the timezone. Every output from here on is New York time. Notice what the agent didn't do: it didn't rank Maya's funnel as the best converter. A number that high is a tracking question first, and it filed it as one.

2 · The step table

"The upsell CVR is too low"

let's see the main VSL funnel and how people buy. the upsell cvr is too low
$ ef funnels product-flow 412 > flow.json
$ ef stats by page --funnel 412 --metrics sessions,sales,main_sales,upsell_sales,revenue --range 30d --limit 500 --json > pages.json
$ ef products list --json > products.json
$ jq … flow.json | column -t    # the step-table join from the ef-funnel-analysis skill
CLASS   PAGE  NAME             SESS  SALES  S/S    NODE    AFTER   OFFERS
main    2311  VSL              2104  45     2.1%   q3m8vd  -       VIT_2B=$138 VIT_3B=$159 VIT_6B=$264
main    2318  Article          980   27     2.8%   w5t1ka  -       VIT_2B=$138 VIT_3B=$159 VIT_6B=$264
main    2320  Packages         420   13     3.1%   q3m8vd  -       VIT_2B=$138 VIT_3B=$159 VIT_6B=$264
upsell  2402  Upsell 1         116   6      5.2%   u1r4nx  q3m8vd  VIT_6B_UP=$204
upsell  2403  Downsell         58    7      12.1%  d8k2wb  q3m8vd  VIT_3B_DS=$102
upsell  2405  Upsell 1 (alt)   3     0      0%     u1a7pc  w5t1ka  VIT_6B_UP2=$150
upsell  2410  Upsell 2         54    2      3.7%   o2v6hs  u1r4nx  OMG_1B=$69 OMG_3B=$99
upsell  2415  Upsell 3         49    1      2%     g3n5yd  o2v6hs  GLW_4B=$204
$ ef stats --funnel 412 --metrics upsell_take_pct,main_revenue,upsell_revenue --range 30d
last 30 days (2026-08-30 → 2026-09-28, America/New_York)
METRIC           VALUE       CHANGE   VS
---------------  ----------  -------  ------------------
upsell_take_pct  16.49%      -6.40%   vs Jul 31 - Aug 29
main_revenue     $19,310.00  +4.11%   vs Jul 31 - Aug 29
upsell_revenue   $2,340.00   -9.85%   vs Jul 31 - Aug 29

About 1 in 6 buyers (16.5%) buys anything after the first order. In the order a buyer sees it:

StepOfferSessionsSalesPer session
Front endVitality 2 / 3 / 6 bottles, $138 / $159 / $2643,504852.4%
Upsell 16 more bottles, $20411665.2%
Downsell3 bottles, $10258712.1%
Upsell 2Omega Plus, $69–$995423.7%
Upsell 3Glow Collagen 4 bottles, $2044912.0%
  • Price is the objection, not the product. The downsell is the same product at half the price and converts about 2.3 times better per session. People want more Vitality, just cheaper.
  • Upsell 1 offers 6 more bottles to everyone, including buyers who just paid $264 for 6. I'd send 6-bottle buyers to a different product first.
  • Article buyers barely see an upsell. The Article page made 27 sales, but the upsell page that follows it had 3 sessions.
  • Upsells 2 and 3 are nearly dead: 3 sales from 103 sessions. Glow Collagen costs as much as upsell 1, for a product nobody came for.

Sessions after checkout overcount reloads, so the per-session rates understate take per buyer. The ranking between steps still holds.

Two of those four bullets are wrong. Hold on to them; the agent finds out in step 7. The other two held up: the half-price downsell beating the upsell is the most useful single fact in the whole session.

Illustration: five pills stacked like a funnel, a cart, an up arrow, a down arrow and two plus signs, with price tags beside the upsell and downsell
The step table puts every post-purchase page in buyer order: front end, upsell 1, downsell, upsell 2. The upsell and its downsell sell the same product at two prices, which is what exposed the price objection.

The funnel also had a split test on upsell 1. The agent read it the way the skill says to:

$ ef stats split 3107 --range 30d
Upsell 1 story test (#3107, 2026-08-30 → 2026-09-28, America/New_York)
VARIANT                  SESSIONS  CONVERSION_RATE  SALES
-----------------------  --------  ---------------  -----
Control (control)        81        7.41%            6
Story version            70        12.86%           9

p-value  0.2915   power 18.7%
No winner yet — each arm needs ~520 sessions before a call can be made.

The story version converts at almost twice the control's rate, but the app hasn't marked a winner (p-value 0.29). Each arm needs about 520 sessions and gets about 70 a month, so that's roughly 7 months away. Waiting won't produce an answer at this traffic. Whether to call it anyway is your decision, not the statistics'. There's no entry for this test in split-tests.md, so I don't know what the story version was meant to prove.

3 · Acting on a decision

"Set the story version as the winner"

The owner made the business call: the story version wins. This is the first time in the session the agent changes anything, and only because it was told to.

$ ef splits variants 3107
Upsell 1 story test (#3107, active)
VARIANT        NODE CODE
-------------  ---------
Control        k2x8qd
Story version  r7m4tw
$ ef splits winner 3107 "Story version" --yes
Upsell 1 story test (#3107) → winner Story version r7m4tw
! The server has not named a winner (p-value 0.4803, ~520 sessions per arm needed). This is a manual call.
✓ #3107 finalized — all traffic now goes to "Story version".
The graph changed on the server — run "ef pages events pull upsell-1" before editing it.
Record the outcome in elasticfunnels/split-tests.md.

Two details worth copying. The CLI prints the app's significance result before it acts, so an early call is a deliberate one, and it refuses to run off a terminal without --yes. The p-value it quotes (0.48) covers the whole test, not the last 30 days, which is why it differs from the 0.29 above. Then the agent pulled the page's graph to confirm the winner had reached what the runtime serves, and wrote the decision into the project:

## #3107 — Upsell 1 story test (page upsell-1, #2402)
- 2026-09-28: Story version (r7m4tw) set as winner. Manual call by the owner.
- Last 30 days (America/New_York): Control 81 sessions, 7.41%, 6 sales ·
  Story version 70 sessions, 12.86%, 9 sales. App: no winner, p 0.29
  (0.48 over the whole test), ~520 sessions per arm needed.
4 · Keeping the method

"Make this a skill so it's easier next time"

A Claude Code skill is a Markdown file of instructions the agent loads when a task matches it. The owner asked for the analysis above to become one, so the next session would start from the method instead of rediscovering it. That session is where the CLI's analysis skills came from: ef-funnel-analysis (the step table, including the jq join above), ef-upsell-diagnosis (the questions in the order that costs most) and later ef-funnel-performance (the whole playbook). ef init and ef claude install them, so your agent starts with them too.

This is the step I'd tell anyone to steal. Every correction later in this post went back into a skill the same day, so the agent doesn't make the same mistake twice.

5 · Two numbers that disagree

"CVR is crazy low. Can we tell why?"

The owner pasted a screenshot of a dashboard widget for the mobile VSL funnel: 33,000 visitors, 23 sales, 0.07% conversion. Step 1 said 3.08%.

[screenshot] also see this. cvr is crazy low. can we tell why
$ ef stats --funnel 587 --metrics sessions,sales,revenue,conversion_rate --range 30d
last 30 days (2026-08-30 → 2026-09-28, America/New_York)
METRIC           VALUE      CHANGE   VS
---------------  ---------  -------  ------------------
sessions         487        -21.30%  vs Jul 31 - Aug 29
sales            23         -41.03%  vs Jul 31 - Aug 29
revenue          $4,430.10  -38.62%  vs Jul 31 - Aug 29
conversion_rate  3.08%      -18.52%  vs Jul 31 - Aug 29
$ ef sessions list --funnel 587 --range 7d --limit 12
last 7 days (2026-09-22 → 2026-09-28, America/New_York)
TIME                 SESSION   EVENTS  DEVICE   COUNTRY  HOSTING  BOT  USER AGENT                           PATH
-------------------  --------  ------  -------  -------  -------  ---  -----------------------------------  ------
2026-09-28 09:19:22  Qm4…f2Lk  0       -        US       yes      no   Mozilla/5.0 (compatible; UptimeCra…  /watch
2026-09-28 09:19:21  Rt8…a0Wc  0       -        US       yes      no   Mozilla/5.0 (compatible; UptimeCra…  /watch
2026-09-28 09:19:20  Lp2…x7Hd  0       -        US       yes      no   Mozilla/5.0 (compatible; UptimeCra…  /watch
…
2026-09-28 09:04:14  Vb6…k1Qe  0       -        US       yes      no   Mozilla/5.0 (compatible; UptimeCra…  /watch
2026-09-28 08:57:40  Hs3…p9Zn  14      mobile   US       no       no   Mozilla/5.0 (iPhone; CPU iPhone OS…  /watch
12 of 7,683 page load(s). "ef sessions show <session>" for one visit's timeline.

The funnel's conversion rate isn't 0.07%. The widget divided by a different number. On sessions that loaded a page, it converts at 3.08%, which is better than the main VSL funnel's 2.46%.

Widgetef stats
Traffic33,000 "visitors"487 sessions
Sales2323
Conversion0.07%3.08%

The two count different things. That widget's "visitors" counted click records: every hit on a tracking link, whether or not a page ever loaded. ef stats sessions count visits with a page view. The page-load list shows what fills the gap: rows with zero events, no device, and the same crawler user agent, in bursts one second apart. In a sample of 200 recent rows, 191 were that crawler. They never load the page, so they never become sessions. Nobody is being lost here.

Illustration: a big cloud of small link circles and a short stack of page cards on a tipped balance scale, with the mascot pointing at the page stack
Tracking-link hits on one side, sessions with a page view on the other. A rate is only as honest as what it divides by.

The lesson isn't about that widget. It's that two numbers with the same label can count different things, and an agent is very good at reconciling them if you ask. "Why do these two disagree?" is one of the most valuable prompts you can type.

6 · Is this traffic real?

Reading the junk

The agent traced one of the crawler hits:

$ ef sessions show Qm4Tn8Wc2xLa9Rv5Hd1Kp7Ze3Yb6f2Lk --full-urls
Session     Qm4Tn8Wc2xLa9Rv5Hd1Kp7Ze3Yb6f2Lk
Started     2026-09-28 09:19:22 (America/New_York)
Landing     https://northwind-example.com/watch?aff_id=2210&subid={gclid}&subid2={acct}
Referrer    -
Affiliate   2210
Funnel      587
Device      -
Country     United States
User agent  Mozilla/5.0 (compatible; UptimeCrawler/2.4)
Flags       bot: no  hosting: YES  vpn: no  proxy: no  tor: no
Page loads  1
Path        -

Timeline
TIME  EVENT  PAGE  URL  NOTE
----  -----  ----  ---  ----
0 event(s).

Affiliate 2210 is Dev. Something opens his links about 1,056 times a day, and the pattern says what:

  • Perfectly regular. The same count every day for 30 days, no weekday or time-of-day shape. Real traffic never does that.
  • Bursts. 11 hits one second apart, then 15 minutes of nothing. 11 link variants checked 96 times a day is 1,056.
  • Unfilled macros. subid={gclid} is a tracking-link template pasted somewhere as-is, never filled in by an ad click.
  • No page load, ever.

That's almost certainly an uptime monitor checking that Dev's links still work. It's harmless: you pay him per sale. But most of it comes from data-center IPs, which the clicks metric already drops, and the ~1,050 hits that don't still count as his clicks. His EPC shows $1.60 on 1,290 clicks. Per real session he earns about $7.40. Rank affiliates on revenue per session, not per click, until this is cleaned up.

The owner's guess ("I think it's his internal tool that does uptime checks") fit the data better than the agent's first one ("a crawler"). That's the right division of labour: you know your partners, the agent knows the rows.

The same step produced a mistake of its own. An affiliate with the id 40117 had sent 505 sessions to the main VSL funnel and 0 sales, surrounded by about 1,860 click rows that never loaded a page, all with browser versions two years old. The agent called it a bot farm and suggested banning it. A closer look split the two groups apart:

  • The never-loaded rows had a webmail referrer: email link scanners, which open every link in an email before the reader does. They were never in the session counts.
  • The 505 sessions with a page view had current browsers, 83% scrolled or played the video, and the page view arrived about a second after the click, like everyone else's.

So 40117 is a real email list that clicks and doesn't buy. That's an affiliate-quality problem, not a filter problem, and hiding those sessions would have hidden the problem. The fair way to show it: without 40117's traffic, the VSL page converts at 2.8%, not 2.1%. It also works the other way round. Two buyers the agent traced were flagged as data-center traffic (hosting: YES) and were real people on iCloud Private Relay, so a hosting flag alone never makes a visit a bot.

7 · The correction

"Are you sure?"

Later, summarising what to change, the agent repeated its step-2 advice: send 6-bottle buyers to a different product first. The owner pushed back.

6-bottle buyers get a different product first, not more Vitality. are you sure? should be more of the same

Good challenge. I said that without checking what each type of buyer actually took. Checking the funnel graph and the orders now.

$ ef orders buyers --funnel 412 --range 30d
Buyers — funnel #412  last 30 days (2026-08-30 → 2026-09-28, America/New_York)
FRONT END    TITLE                    LIST    MEDIAN PAID  BUYERS  TOOK ANY  TAKE %  BUMP
-----------  -----------------------  ------  -----------  ------  --------  ------  ----
VIT_6B       Vitality 6 bottles       264.00  264.00       39      10        25.6%   9 
VIT_3B       Vitality 3 bottles       159.00  159.00       31      5         16.1%   6 
VIT_2B       Vitality 2 bottles       138.00  138.00       15      0         0.0%    3
VIT_6B_SALE  Vitality 6 (sale)        528.00  84.00        12      0         0.0%    2
TOTAL                                                      97      15        15.5%

Takes by offer
FRONT END    OFFER      TITLE                     BUYERS  % OF GROUP  BOUGHT ON
-----------  ---------  ------------------------  ------  ----------  ---------
VIT_6B       VIT_6B_UP  6 more bottles            5       12.8%       upsell-1
VIT_6B       VIT_3B_DS  3 bottles (downsell)      4       10.3%       downsell
VIT_6B       OMG_3B     Omega Plus 3 bottles      2       5.1%        upsell-2
VIT_3B       VIT_6B_UP  6 more bottles            1       3.2%        upsell-1
VIT_3B       VIT_3B_DS  3 bottles (downsell)      3       9.7%        downsell
VIT_3B       GLW_4B     Glow Collagen 4 bottles   1       3.2%        upsell-3
133 purchase row(s) · bump/bonus codes: SHIP_PROTECT · take window 1440 min.

You're right, and I was wrong. Every buyer's first upsell is already 6 more bottles, whatever they bought, and it works best on the buyers who bought 6:

First purchaseBuyersTook more Vitality
6 bottles, full price399 (23.1%), the highest
3 bottles314 (12.9%)
2 bottles150
6 bottles on the sale page, paid ~$84120

Buyers who bought the biggest package are the committed ones. "More of the same" isn't the problem. The problems are buyers who buy small and get the biggest offer, and sale buyers who either never see an upsell or see one that costs 2.4 times what they just paid.

This table counts buyers who took more Vitality. The 16.5% in step 2 counts every upsell sale, including the second product.

Illustration: three stacks of capsules of different heights, six, three and one, with a check mark only above the tallest stack
Grouped by what they bought first, the biggest-package buyers took "more of the same" most often. Only orders grouped by buyer could show that; page sessions can't.

Then it ran one more cut, because the same logic threatened its other step-2 claim, the Article buyers who "barely see an upsell":

$ ef orders buyers --funnel 412 --range 30d --by page
FIRST PAGE           PACKAGES                    BUYERS  TOOK ANY  TAKE %  BUMP  MEDIAN PAID
-------------------  --------------------------  ------  --------  ------  ----  -----------
watch (2311)         VIT_6B, VIT_3B, VIT_2B      45      8         17.8%   10    264.00
story (2318)         VIT_6B, VIT_3B, VIT_2B      27      5         18.5%   5     159.00
packages (2320)      VIT_6B, VIT_3B, VIT_2B      13      2         15.4%   3     159.00
weekend-sale (2344)  VIT_6B_SALE                 12      0         0.0%    2     84.00
TOTAL                                            97      15        15.5%

A second correction: there's no Article leak. Its buyers take upsells at the same rate as everyone else. The compiled funnel flow shows them going to the $150 alternative page; at runtime they reach the main upsell 1. The 3 sessions on that page were real, and they weren't where Article buyers went. The real leak is the sale page: 12 buyers, 0 upsells. I've corrected both points in the skills so the next analysis checks orders before it claims a leak.

This is the moment that made me trust the tool more, not less. The agent was wrong twice in one table, and both times the fix was the same: stop reasoning from page sessions, count buyers from orders. It retracted in plain words and changed its own instructions. A human analyst who does that is rare.

8 · Following one visitor

Session traces

Before calling the sale page a leak, the agent traced its buyers, and one of Maya's buyers to explain step 1's impossible 40%:

$ ef sessions show Ws7Kd2Lm9Qa4Xe1Rb8Tn5Hc3Vy6p0Gj
Session     Ws7Kd2Lm9Qa4Xe1Rb8Tn5Hc3Vy6p0Gj
Started     2026-09-21 20:14:07 (America/New_York)
Landing     /weekend-sale on northwind-example.com
Referrer    https://mail.google.com/
Affiliate   7702
Funnel      412
Device      mobile / iOS / Mobile Safari
Country     United States / Texas / Austin
User agent  Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 …
Flags      bot: no  hosting: no  vpn: no  proxy: no  tor: no
Page loads  2
Path        /weekend-sale → checkout → /members

Timeline
TIME      EVENT      PAGE  URL                  NOTE
--------  ---------  ----  -------------------  -----------
20:14:07  page-view  2344  /weekend-sale
20:14:15  buy-link   2344  /weekend-sale        VIT_6B_SALE
20:15:02  page-view  2390  /members
3 event(s).
$ ef sessions show Nf3Bq8Zt1Kc6Wm4Ys9Dh2Lx7Ra5e0Uv
…
Affiliate   5120
Device      mobile / iOS / Instagram
Path        buy /packages

Timeline
TIME      EVENT     PAGE  URL        NOTE
--------  --------  ----  ---------  ------------------------------------------------
14:02:11  buy-link  2520  /packages  TRACKING GAP: buy-link with no earlier page-view
1 event(s) · 1 tracking gap(s).
  • Sale buyers go from checkout straight to the members area. The sale page isn't wired into the funnel's upsell path, so they never see an offer. That may be deliberate for the house list. If it isn't, they need an upsell priced for someone who just paid $84, not $204.
  • Maya's buyers click buy on a page that never recorded its view. They arrive in Instagram's in-app browser, and the page view isn't tracked there. That's why her funnel shows 40%: the buyers are real, the sessions are missing. Until it's fixed, her conversion rate and step table can't be used.
9 · The second funnel

"What about the mobile VSL funnel?"

whats the landing page on the mobile VSL funnel? … what about upsells on it?
$ ef stats by page --funnel 587 --metrics sessions,sales,conversion_rate,revenue --range 30d --limit 4
by page  last 30 days (2026-08-30 → 2026-09-28, America/New_York)
PAGE             SESSIONS  SALES  CONVERSION_RATE  REVENUE
---------------  --------  -----  ---------------  ---------
VSL              298       10     3.36%            $2,580.40
Article          74        2      2.70%            $402.00
Packages         5         0      0.00%            $0.00
Long letter      0         2      -                $418.00
4 of 11 rows — raise --limit to see more.
$ ef funnels pull k8v2rn && jq -r '…node table…' elasticfunnels/funnels/k8v2rn.flow.json
6   purchase                                output_1->7
7   router                                  output_1->8,9,10,11,12
8   product_check_product  vit_1            output_1->20
9   product_check_product  vit_2            output_1->21
10  product_check_product  vit_3            output_1->22
11  product_check_product  vit_6            output_1->23
12  product_check_all                       output_1->23
22  page                   up-3-more-84     output_1->30 output_2->14
23  page                   up-6-more-150    output_1->30 output_2->14
14  page                   omega-3          output_1->30 output_2->18
18  split_test                              output_1->41 output_2->42 output_3->43 output_4->44
…
$ ef orders buyers --funnel 587 --range 30d
FRONT END  TITLE               LIST    MEDIAN PAID  BUYERS  TOOK ANY  TAKE %  BUMP
---------  ------------------  ------  -----------  ------  --------  ------  ----
VIT_6B     Vitality 6 bottles  264.00  264.00       6       3         50.0%   3
VIT_3B     Vitality 3 bottles  159.00  159.00       4       0         0.0%    2
VIT_2B     Vitality 2 bottles  138.00  138.00       4       0         0.0%    1
TOTAL                                               14      3         21.4%

The landing page is the VSL, the same page the main funnel uses as a front end: about 60% of this funnel's traffic, most of it Dev's.

Upsell 1 is picked by the product bought, but the checks list the old product codes (vit_3). The VSL sells VIT_3B, which matches none of them, so every VSL buyer falls through to the default: 6 more bottles for $150. A 3-bottle buyer never sees the 3-more-for-$84 offer built for them.

  • Keep the $150 six-pack for 6-bottle buyers: 3 of 6 took it, the same pattern as the main funnel.
  • Add the current codes to the product checks, so smaller buyers get the offer sized for them.
  • There's no same-product downsell. Declining sends buyers to Omega Plus, which sold nothing. On the main funnel, the half-price downsell is the best-converting step.
  • Everything after upsell 1 made nothing, and a 4-way split on about 14 buyers a month can never finish. I'd cut it to upsell 1, a Vitality downsell, one second product, members area.

14 buyers is a signal, not a verdict. The code mismatch is certain; the size of the win isn't.

10 · Affiliate economics

"Dev's AOV? Main and upsell separately"

$ ef stats --aff 2210 --funnel 587 --range 30d --metrics aov,main_order_value,upsell_order_value,sales,revenue,refund_pct
last 30 days (2026-08-30 → 2026-09-28, America/New_York)
METRIC              VALUE      CHANGE   VS
------------------  ---------  -------  ------------------
aov                 $291.60    +8.12%   vs Jul 31 - Aug 29
main_order_value    $226.10    +3.02%   vs Jul 31 - Aug 29
upsell_order_value  $65.50     +30.22%  vs Jul 31 - Aug 29
sales               13         -38.10%  vs Jul 31 - Aug 29
revenue             $2,068.40  -33.94%  vs Jul 31 - Aug 29
refund_pct          13.80%     +2.10%   vs Jul 31 - Aug 29

Dev's AOV is $291.60: $226.10 from the main order and $65.50 in upsells per buyer, against a brand average of $258.40 ($214.30 + $44.10). Upsell order value is upsell revenue spread over his buyers, not the price of an upsell. These are the app's figures; its divisor isn't a whole number of orders, so I'm quoting them rather than recomputing. Sales don't add up to main plus upsell because 3 are shipping-protection bumps.

Then the owner asked the question that turned into the best table of the session: how do his affiliates differ? The agent profiled two of them with the same commands, scoped by --aff:

$ ef stats by device --aff 7702 --funnel 412 --range 30d --metrics sessions,sales,conversion_rate,average_session_duration,aov
$ ef stats by device --aff 2210 --funnel 587 --range 30d --metrics sessions,sales,conversion_rate,average_session_duration,aov
$ ef orders buyers --funnel 412 --aff 7702 --range 30d
Illustration: two separate paths, a short one from an envelope card straight to a cart, and a long winding one from a phone video card to a cart and then an extra plus card
Pre-sold email readers buy in seconds; cold video viewers take minutes and buy bigger. One funnel average fits neither of them.
House email list (7702)Dev (2210)
TrafficInternal email, mobileCold mobile Google Ads to the VSL
LandingWeekend sale page, 6 bottles at ~$84VSL
Time on page~8 seconds~4.5 minutes
Conversion14%2.5%
AOV$92$291.60
Upsells0 of 12: never shown one6-bottle buyers take the $150 six-pack
What their traffic needsA price-matched upsell after the sale pageKeep the six-pack upsell, add a Vitality downsell, tune the VSL for phones

The funnel average hid both. The list's 14% made the funnel look healthy, and Dev's upsell success was invisible inside a 16.5% take rate. Different traffic needs different funnels. To act on it: a page-event rule on aff_id that loads a different upsell for one affiliate, or a dedicated funnel for a big one, then a split test on that affiliate's traffic only.

11 · Where buyers came from

"Per affiliate, but also traffic source: Meta, Google, YouTube"

Northwind's affiliates don't pass UTMs, so no report could answer this directly. Claude Code did it the slow way: it pulled every buyer's session id from ef orders list --funnel … --all --json, opened each visit with ef sessions show --full-urls (one call per session, so a few minutes), and classified the first visit from its landing URL and user agent with its own rules:

  • fbclid in the URL, or the Facebook or Instagram in-app browser: Meta (see what fbclid is)
  • gclid in the URL, or a Google click ID forwarded in an affiliate's sub-ID: Google Ads (what gclid is)
  • A webmail referrer or mail-app user agent: email
  • An order with no web visit: phone, through the call center
Source (my classification)BuyersRevenueMain affiliates
Meta, in-app browser99$28.4kMaya
Phone / call center6 (54 orders)$13.9kCall-center partners
Google Ads32$7.7kCarlos, Lucía, Dev
Affiliate presell pages~28~$7.0kSeveral
Affiliate link, no signal15$3.5kTwo small affiliates
Email15$2.2kHouse list
Direct~8~$1.8k-
  • Maya's traffic is Meta, almost all through Instagram's in-app browser, with no click ID and no referrer. That fits the tracking gap in step 8, and it fits her refund rate: impulse buyers refund more.
  • About a fifth of revenue is phone orders. They were most of the unlabeled row in step 1.
  • Carlos and Lucía run Google search ads: buyers who searched for it. That's why they convert several times better than cold traffic.
  • Dev runs Google Ads too. His links forward the click ID in subid2, which also explains the {gclid} template his monitor pings.
  • A compliance flag: a few affiliates send traffic from domains that imitate the brand name (misspellings, "-usa.shop" style). Worth checking against your affiliate terms.

Read the header of that table carefully: it's the agent's classification from session data, built for this question, not a report the app printed. It's a good example of what an agent adds. When the report you want doesn't exist, it can build it from the raw visits, slowly, and tell you exactly which rules it used.

12 · The handover

"Write this up for a developer"

The last deliverable was a brief in the project, research/main-vsl-dev-brief.md: issues ranked by money, each with the evidence, links, sample ids and how to tell it's fixed. The top of it:

# Main VSL funnel: developer brief (last 30 days, America/New_York)

| # | Issue                                            | Severity | Status   |
|---|--------------------------------------------------|----------|----------|
| 1 | Sale-page buyers skip every upsell               | High     | Open     |
| 2 | No page views tracked in Instagram's in-app       | High     | Open     |
|   | browser (partner funnel)                          |          |          |
| 3 | Mobile VSL funnel: product checks use old codes  | Medium   | Open     |
| 4 | Upsell 1 priced 2.4x above sale-page buyers      | Medium   | Decision |
| 5 | Affiliate 40117: real clicks, 0 sales            | Low      | Decision |

## 1. Sale-page buyers skip every upsell
What we see: 12 buyers from /weekend-sale, 0 took an upsell (ef orders buyers --by page).
Sample sessions: https://app.elasticfunnels.io/1042/clicks/session?id=Ws7Kd2Lm9Qa4Xe1Rb8Tn5Hc3Vy6p0Gj
Check: the sale page's buy links and where checkout returns buyers.
Done when: a test purchase on /weekend-sale lands on an upsell page, and sale buyers show takes.

## Reproduce
ef orders buyers --funnel 412 --range 30d --by page
ef sessions show Ws7Kd2Lm9Qa4Xe1Rb8Tn5Hc3Vy6p0Gj

No customer data in it: order codes and session ids only. A developer can reproduce every number with the commands at the bottom, which is the difference between a brief and an opinion.

What this analysis shows about Claude Code

In one session, a marketer who typed maybe fifteen short prompts got a funnel-by-funnel overview, a step table, a split-test decision carried out, a reconciliation of two numbers that disagreed by a factor of forty, a traffic audit, affiliate profiles, a source breakdown nobody had, and a developer brief. By hand that's days of exports.

It was also wrong three times: the "different product" upsell, the Article leak and the "bot farm". Every one was a claim built on page sessions and pattern-matching instead of orders and engagement, and every one fell over the moment someone asked "are you sure?" and the agent went to the orders. That's the real workflow. The agent does the reading at machine speed, and you do the doubting.

If you want to build and test pages the same way, Claude Code for marketing runs a landing-page split test from brief to result. Reading a test's numbers with an agent is covered in AI A/B testing, and ElasticFunnels analytics is where all of these numbers come from.

My take
  • Never act on an upsell or routing finding that came from page sessions. Ask for the per-buyer table from orders first; it's one command.
  • When a conversion rate looks absurd, high or low, assume it's measuring something else until two numbers agree.
  • Don't let the agent ban an affiliate or filter traffic out of reporting on a pattern alone. Zero sales from engaged people is a conversation with the affiliate, not a bot rule.
  • Every time it gets something wrong, make it fix the skill, not just the answer. That's how the next session starts smarter.

None of this replaces knowing your business. The owner knew the uptime monitor was probably Dev's, knew the sale page was for the house list, and knew that big buyers want more of the same. What changed is how fast those hunches got checked against every row. Give the agent the data and the rules, keep the decisions, and argue with it. It argues back well.

FAQ

Claude Code for data analysis FAQ

Is Claude Code good for data analysis?

It is good at the part that takes a person hours: pulling numbers from several commands, joining them into one table and checking one number against another. It is only as good as the data it can read and the rules it follows, so give it a CLI with real data (for funnels, the ef CLI) and make it prove claims from orders before you act on them.

How do you use Claude Code as a data analyst?

Put the data behind commands it can run, in a folder it works in. For an ElasticFunnels brand that is ef init in an empty folder, then plain-English questions: sales by funnel, how people buy in one funnel, which affiliate is worth it. It runs ef stats, ef orders and ef sessions, and answers with the range and timezone it used.

Can Claude analyze a dataset from my funnel?

Yes. It reads breakdowns by page, product, funnel, affiliate, country, device, UTM and day from ef stats, per-buyer upsell takes from ef orders buyers, and single visits from ef sessions show. Every command also prints JSON, which it can join and compute with.

Can Claude Code change my funnel while it analyses it?

ef stats, ef orders and ef sessions are read-only. Changes are separate commands: ef splits winner ends a split test, and page-event and funnel edits go through ef pages events and ef funnels. Claude Code asks before it runs a command unless you have allowed it, so nothing changes without your yes.

Does Claude Code see my customers' personal data?

Whatever a command prints becomes part of the conversation Claude Code sends to Anthropic. ef orders leaves out customer emails, names, phones and addresses unless you pass --include-pii, and ef orders buyers only prints totals, so an analysis never needs them.

How do you analyze a sales funnel?

Find which funnel makes the money, then list every step in the order a buyer meets it (front end, upsell 1, downsell, upsell 2) with the offer, price, sessions and sales at each. Check the weak step against orders grouped by buyer before you change it, because page sessions after checkout undercount.

What is funnel analysis?

Funnel analysis measures how many people move from one step of a funnel to the next and where they drop out. In a direct-response funnel the steps are the landing page, the checkout, and each upsell and downsell, and the useful numbers are conversion rate, take rate per step and revenue per step.

Why does my dashboard conversion rate differ from what Claude Code reports?

Usually because the two numbers divide by different things. In the analysis in this post, a widget's visitors counted every tracking-link hit, including a crawler that never loaded a page, while ef stats counts sessions with a page view. Ask the agent to reconcile the two before you trust either.

Can Claude Code tell bots from real visitors?

It can find the patterns in session data: hits that never load a page, perfectly regular timing, unfilled ad macros like {gclid} in the URL, outdated browser versions. It should also check engagement before calling anything a bot, because real people who don't buy look like junk at first glance.

Is Claude Code better than ChatGPT for analyzing funnel data?

The difference that matters here is that Claude Code runs commands in your folder, so it pulls the numbers itself instead of working from a pasted export. Codex works the same way with the ef CLI. A chat window only sees what you paste into it.

What does it cost to analyse funnel data with Claude Code?

Claude Code runs on your Claude subscription or API account from Anthropic. The ef CLI, the analytics it reads and the skills it installs are included on every ElasticFunnels plan.

Ask your funnel a question

Bind a folder with ef init and ask Claude Code which funnel makes the money. Start a 14-day free trial.