Category: Analytics

  • Analytics event deduplication: when double-counting inflates conversions

    Analytics event deduplication: when double-counting inflates conversions

    Analytics event deduplication: when double-counting inflates conversions
    Photo: Nemo bis via Wikimedia Commons (CC BY-SA 3.0)

    You’re tracking conversions across multiple tools—Google Analytics, your email platform, maybe a CRM—and the numbers don’t match. One dashboard says you had 47 signups yesterday. Another says 52. A third claims 61.

    The culprit is often event deduplication, or more precisely, the lack of it. When the same user action triggers multiple tracking events without a shared identifier to merge them, you end up counting the same conversion two, three, or more times.

    This isn’t just a reporting nuisance. Inflated conversion counts make your funnel look healthier than it is, lead you to overspend on acquisition channels that aren’t actually performing, and break attribution when you’re trying to figure out what’s working.

    How duplicate events happen

    The most common scenario: you fire a tracking pixel on page load and via a JavaScript event listener. A user completes a signup form. The page reloads to a thank-you URL. Your analytics script fires once on form submission, once on the new page load, and if you’ve got server-side tracking hooked into your backend, possibly a third time when the database write completes.

    Each event looks legitimate in isolation. None of your dashboards know the others exist. You see three conversions. Reality: one person signed up.

    Another pattern: tracking the same conversion in both client-side and server-side systems without a deduplication key. Google Analytics records the event when the browser sends it. Your email platform records it when the API call creates the subscriber. Your payment processor records it when the webhook fires. Same user, same action, three separate counts.

    Single-page apps make this worse. If your React or Vue app doesn’t manage history state carefully, navigation events can fire duplicate pageviews. Add a tag manager that reinitializes on every route change, and you’re stacking events every time someone clicks a link.

    What actually deduplicates events

    Most analytics platforms offer deduplication, but it’s not automatic. Google Analytics 4 uses a combination of client ID, session ID, and timestamp to merge events that fire within a narrow window—usually a few seconds. If your duplicate fires outside that window, or if the client ID changes between events (common in cross-device scenarios), GA4 treats them as separate.

    Facebook Pixel and other ad-platform tracking use event IDs. If you pass the same eventID parameter with both a browser pixel and a server-side Conversion API call, the platform deduplicates them. But you have to implement it. The default setup doesn’t generate or pass event IDs automatically.

    Email platforms like MailerLite and Beehiiv deduplicate by email address when you’re tracking list growth, but if you’re sending conversion events via API and tracking form submissions via their embed code, you’ll still double-count unless you add a check in your own code to prevent firing both.

    How to catch and fix it

    Start by auditing where your conversions get tracked. Open your browser’s network inspector, complete a test conversion, and watch how many tracking requests fire. Look for:

    • Multiple requests to the same analytics endpoint within a few seconds
    • Duplicate event names with identical or near-identical timestamps
    • The same conversion being sent to different platforms without a shared transaction or session ID

    If you’re running server-side tracking, check your logs. Grep for the event name and see if the same user ID or session appears multiple times for a single action.

    Once you’ve identified duplicates, the fix depends on your stack. If you’re firing events both on form submit and page load, pick one and remove the other. If you’re using both client-side and server-side tracking, implement event IDs or transaction hashes so platforms can merge them. If you’re tracking across multiple tools, designate one as the source of truth for each conversion type and use it to reconcile the others.

    For Google Analytics, enable User-ID tracking and pass a consistent identifier across sessions and devices. For Facebook, generate a unique event ID server-side, store it in the user’s session, and pass it with both pixel and API calls. For email platforms, check whether a subscriber already exists before firing a secondary conversion event.

    When deduplication breaks

    Deduplication fails silently when identifiers don’t match. A user clears cookies between events. A session expires. A server-side call uses a hashed email while the client-side call uses a raw one. A mobile app and a web browser use different device IDs.

    Cross-domain tracking makes this worse. If your checkout lives on a different domain than your marketing site, and you haven’t configured cross-domain measurement correctly, every conversion looks like a new session with a new client ID. No deduplication happens because the platform doesn’t recognize them as the same user.

    The best defense: log your conversion events somewhere you control—a database table, a Google Sheet via API, a Slack channel—and compare counts across platforms weekly. When your internal log shows 50 conversions and your analytics dashboard shows 73, you know you have a deduplication problem.

    One Two Three Send covers the tools and workflows solo operators actually use. If you want sharp, specific takes on analytics, email platforms, and everything else in the online-business stack, subscribe below.

    Heads up — some links in this article are affiliate links. If you sign up through them, we may earn a small commission at no extra cost to you. We only recommend tools we use ourselves.

  • Analytics event naming: why inconsistency kills your funnel data

    Analytics event naming: why inconsistency kills your funnel data

    Analytics event naming: why inconsistency kills your funnel data
    Photo by 1981 Digital on Unsplash

    You set up event tracking six months ago. It worked. You added Google Analytics 4, maybe Plausible or Fathom, wired up a few conversions, and moved on.

    Now you’re trying to build a funnel report and half your events don’t match. button_click in one tool, Button Click in another, btn_clicked in your CRM. None of them talk to each other. Your attribution is broken before you even start analyzing.

    Event naming isn’t glamorous, but it’s the difference between actionable data and a dashboard full of question marks.

    Why event names diverge

    Most operators start tracking events one tool at a time. You add GA4, fire a sign_up event. Two months later, you install a heatmap tool and track signup_button. Your email platform logs Signup with a capital S. Your payment processor calls it account_created.

    Each name made sense in isolation. But now you’re trying to connect the dots across platforms, and nothing lines up.

    The problem gets worse when multiple people touch your tracking stack. A contractor adds events with camelCase. You prefer snake_case. Your VA uses sentence case because that’s what the UI suggested. Three months later, you have 47 events and no idea which ones measure the same thing.

    Renaming events after the fact is painful. GA4 lets you modify event names via the interface, but historical data stays unchanged. Most analytics tools don’t offer retroactive renaming at all. You either live with the mess or start over.

    What a naming convention actually needs

    A useful event naming system has three jobs: it prevents duplicates, it groups related events, and it survives handoffs to other people.

    Start with a consistent case format. Snake_case (button_click) works well because it’s readable, doesn’t break in spreadsheets, and most analytics platforms handle it without fuss. Avoid spaces, capital letters, and special characters unless your tool explicitly requires them.

    Use a namespace prefix for event categories. If you track multiple funnels—say, newsletter signups and course purchases—prefix each event with its domain: newsletter_signup_started, course_checkout_completed. This keeps related events grouped in alphabetical lists and makes filtering easier.

    Be specific about the action and the object. click is too vague. button_click is better. header_cta_click is best. When you’re looking at a list of 50 events three months from now, you want to know exactly what fired without opening the implementation code.

    Write it down. A shared Google Doc, a Notion page, a comment block in your tag manager—anywhere your future self and collaborators can check before adding a new event. Include the event name, where it fires, what it measures, and when it was added. This takes two minutes per event and saves hours of detective work later.

    How to audit what you have

    Pull a full event list from each tool you use. GA4 lets you export all events from the Events report. Plausible shows them under Goals. Your CRM probably has an API endpoint or a CSV export.

    Dump everything into a spreadsheet. Look for duplicates with different casing, pluralization, or verb tenses. form_submit and form_submitted probably measure the same thing. So do page_view and pageview.

    Group events by funnel stage or user journey. Which events represent awareness? Consideration? Conversion? If an event doesn’t clearly belong to a stage, it’s either redundant or poorly named.

    Decide which names to keep. Prioritize the ones already in your most critical reports. If you’ve been running a GA4 funnel for six months with checkout_started, don’t rename it to cart_checkout_begin just for consistency. Rename the outliers instead.

    For tools that allow it, create event mappings or modify event parameters to normalize naming without losing historical data. GA4’s “modify event” feature lets you rename events going forward while keeping old data intact. Not ideal, but better than a full reset.

    For tools that don’t support renaming, add a documented transition period. Fire both the old and new event names for 30 days, then retire the old one. This gives your dashboards time to adjust without dropping data.

    When to enforce the system

    New events should follow the convention from day one. Before you add anything, check the doc. If the name isn’t there, add it. If it conflicts with an existing event, revise it before you push the code live.

    Set up a monthly review. Scan your event list for anything that doesn’t match the pattern. If you find a rogue event, trace it back to the source and fix it. The longer you wait, the harder it gets.

    If you work with contractors or team members, include event naming rules in your onboarding docs. A two-paragraph explainer and a link to your event registry will prevent most mistakes.

    This isn’t about perfectionism. It’s about making sure the data you collect six months from now is still useful. Consistent event naming doesn’t make your funnels convert better, but it makes it possible to know why they don’t.

    Want more systems like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and workflows that actually matter for online operators.

  • Analytics dashboard refresh rates: when ‘real-time’ is 15 minutes old

    Analytics dashboard refresh rates: when ‘real-time’ is 15 minutes old

    Analytics dashboard refresh rates: when 'real-time' is 15 minutes old
    Photo: Saleemkce via Wikimedia Commons (CC BY-SA 4.0)

    You refresh your analytics dashboard, see a spike in traffic, and make a decision. Twenty minutes later, the numbers change. The spike wasn’t real—it was a delayed batch update finally catching up.

    Most analytics platforms advertise “real-time” data, but the definition varies wildly. Some update every few seconds. Others label data “real-time” when it’s actually on a 10- or 15-minute delay. If you’re running paid campaigns, testing content, or troubleshooting a technical issue, that lag can cost you.

    Here’s what the refresh intervals actually look like across the tools solo operators use most.

    Google Analytics 4: real-time vs. standard reports

    GA4 splits its interface into two modes with completely different refresh schedules.

    The Realtime report updates every few seconds and shows activity from the last 30 minutes. It’s genuinely live. You can watch users land on pages, see referral sources, and track active sessions as they happen.

    Every other report in GA4—traffic acquisition, landing pages, conversions—runs on processed data that lags 24 to 48 hours. The interface doesn’t always make this obvious. You can select “today” as a date range, but the data you’re seeing might be incomplete or entirely missing if Google hasn’t finished processing it yet.

    This matters when you’re trying to measure same-day campaign performance. If you launch a newsletter at 9 a.m. and check your landing page traffic at noon, GA4’s standard reports might show zero visits even if 500 people clicked through. The Realtime report will show them; the rest of the dashboard won’t.

    Plausible, Fathom, and privacy-first tools

    Privacy-focused analytics platforms tend to update faster because they process less data and don’t rely on Google’s infrastructure.

    Plausible refreshes every 60 seconds. You’ll see new pageviews, referrers, and goals within a minute of them happening. There’s no separate “real-time” mode—the entire dashboard operates on the same refresh cycle.

    Fathom Analytics works similarly, with updates every 30 to 60 seconds depending on server load. Both tools are built for solo operators who want to check traffic without waiting for batch processing.

    The tradeoff: these platforms don’t offer the same segmentation depth as GA4. You get fast data, but fewer filtering options.

    Paid ad platforms: Facebook, Google Ads, LinkedIn

    Ad dashboards have their own refresh logic, and it’s slower than most operators expect.

    Facebook Ads Manager updates every 15 minutes for active campaigns. If you’re testing ad creative or adjusting budgets, you won’t see the impact of your changes until at least one refresh cycle passes. Conversion data—especially for events tracked via the Pixel—can lag an additional hour as Facebook attributes clicks and validates events.

    Google Ads refreshes every 3 hours for most metrics. Impressions and clicks appear faster, but conversion data tied to GA4 goals or imported offline events can take 6 to 24 hours to populate.

    LinkedIn Campaign Manager updates every 24 hours. You can’t optimise LinkedIn ads intraday—the platform doesn’t surface enough data to make that possible.

    If you’re split-testing ad creative, this lag forces you to wait longer than you’d expect before making decisions. A campaign that looks like it’s underperforming at 10 a.m. might show strong results by 2 p.m. once delayed conversions populate.

    What this means for decision-making

    The biggest mistake is treating delayed data as if it were complete. If you check your analytics dashboard and see low traffic or weak conversions, ask whether the platform has actually finished processing the time window you’re looking at.

    For same-day decisions—like killing an underperforming ad or tweaking a landing page—use tools with sub-60-second refresh rates. Plausible, Fathom, or GA4’s Realtime report are your best options.

    For campaign analysis and attribution, wait at least 48 hours before drawing conclusions. Conversion tracking, especially across multiple touchpoints, needs time to reconcile.

    And if you’re running a high-stakes launch—a product drop, a webinar, a sponsorship deal—set up multiple dashboards. Cross-reference GA4 Realtime with your email platform’s click tracking and your payment processor’s revenue feed. No single dashboard will give you the full picture on the same refresh cycle.

    Want more breakdowns like this? Subscribe to One Two Three Send—we cover the tools, tactics, and operational details solo operators actually need.

  • Google Analytics 4 session timeout: what 30 minutes actually measures

    Google Analytics 4 session timeout: what 30 minutes actually measures

    Google Analytics 4 session timeout: what 30 minutes actually measures
    Photo: Saleemkce via Wikimedia Commons (CC BY-SA 4.0)

    Google Analytics 4 ends a session after 30 minutes of inactivity by default. Most operators know that number, but fewer understand what the timer actually tracks—or how changing it affects every engagement metric in your dashboard.

    If you’re running a content site, newsletter archive, or documentation hub where readers spend time away from the tab, the standard timeout can fragment what should count as a single visit. Here’s how the mechanism works and when to adjust it.

    How the 30-minute timer starts and resets

    GA4 starts a session the moment someone lands on your site and fires the first pageview or event. The 30-minute countdown begins immediately. Every subsequent interaction—pageview, scroll, click event, video play—resets the timer back to zero.

    If someone reads an article for twelve minutes, opens a new tab to check email for eight minutes, then returns to click a link, GA4 sees continuous activity. The session persists because the gap never hit thirty minutes.

    But if that reader leaves the tab open, walks away for thirty-one minutes, then comes back and scrolls, GA4 registers a new session. Same browser, same tab, different session. That split changes your session count, pages-per-session average, and engagement rate.

    The timer is client-side and local to the browser. It doesn’t sync across devices. If someone starts reading on mobile during a commute and picks up the same article on desktop an hour later, those are always separate sessions—even if they’re logged in.

    When the default timeout distorts your metrics

    Thirty minutes works well for ecommerce and SaaS dashboards where sessions map to discrete tasks: browse products, compare plans, check out. Gaps longer than half an hour usually signal a different intent or context.

    Content sites see different behavior. A reader might open five tabs from your homepage, read each article for eight minutes over the course of an hour, and trigger five separate sessions because the gaps between tab switches exceeded the threshold.

    Documentation sites and tutorial hubs get hit hardest. Readers toggle between your guide and their own project. A thirty-minute threshold treats a single problem-solving session as three or four visits, deflating engagement time and inflating bounce rate.

    If your average GA4 session duration is under two minutes but you publish 10-minute reads, the timeout setting is likely splitting real reading sessions into fragments.

    Adjusting the session timeout in GA4

    You can change the session timeout in GA4’s data stream settings. Navigate to Admin → Data Streams → [your stream] → Configure tag settings → Adjust session timeout. The range is 1 to 120 minutes for inactivity timeout.

    Extending it to 60 or 90 minutes makes sense if your content encourages multi-tasking or requires readers to step away and return. Shortening it to 15 minutes can help if you want tighter attribution windows for fast-moving campaigns or live events.

    Changing the timeout doesn’t backfill historical data. GA4 applies the new setting only to sessions that start after you save the change. If you’re comparing month-over-month engagement metrics and you adjusted the timeout mid-period, your averages will be skewed.

    One non-obvious side effect: increasing session timeout also extends the window for attributing conversions to the original traffic source. If someone arrives from organic search, reads for twenty minutes, leaves for forty, then returns and subscribes, a 30-minute timeout attributes that conversion to direct traffic. A 60-minute timeout keeps the organic attribution intact.

    When not to change it

    If you’re running paid campaigns or affiliate tests and need to compare performance across tools, keep GA4’s timeout at the default. Most ad platforms and attribution tools assume a 30-minute session window. Diverging from that standard makes cross-platform reporting harder to reconcile.

    Similarly, if you’re part of a media network or benchmark group that shares analytics data, non-standard timeout settings make your engagement metrics incomparable. A 90-minute timeout will always show higher pages-per-session than a peer using 30 minutes, even if actual behavior is identical.

    And if your content is truly short-form—tweet threads, quick-hit news briefs, recipe cards—a longer timeout just adds noise. Sessions that stretch across an hour when your median read time is ninety seconds don’t reflect real engagement; they reflect open tabs.

    The default exists for a reason: it works for most sites most of the time. But if your engagement metrics don’t align with how readers actually use your content, the session timeout is one of the first settings worth testing.

    Want more analytics breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and settings that shape your business metrics.

  • Analytics custom dashboards: when pre-built templates mislead you

    Analytics custom dashboards: when pre-built templates mislead you

    Analytics custom dashboards: when pre-built templates mislead you
    Photo by Stephen Phillips – Hostreviews.co.uk on Unsplash

    Most analytics platforms ship with attractive templates. You connect your data source, the dashboard populates itself, and you get a grid of charts that look professional enough to screenshot for a board deck.

    The problem: those templates measure what the vendor thinks matters, not what actually drives your business. And because they look polished, operators assume they’re looking at the right metrics—until revenue stalls and the dashboard still shows green.

    What pre-built dashboards optimise for

    Analytics vendors design default templates to serve the widest possible audience. That means they prioritise:

    • Vanity metrics that trend upward. Page views, session counts, total subscribers. These make new users feel good and reduce churn during trial periods.
    • Metrics that showcase platform features. If the tool sells attribution modelling, the dashboard will surface multi-touch funnels—even if you’re a solo operator with one traffic source.
    • Industry averages that may not apply. E-commerce dashboards assume you care about cart abandonment rate. But if you sell a $2,000 course with a multi-week consideration cycle, cart behaviour is noise.

    The result: you spend every Monday morning reviewing a dashboard that tells you visits are up 12% but can’t explain why revenue per subscriber dropped.

    The metrics that actually matter depend on your model

    If you run a sponsored newsletter, your core dashboard should answer:

    • What’s my seven-day open rate by acquisition source?
    • Which posts drove the most sponsor link clicks?
    • What’s my subscriber-to-sponsor-ready ratio? (Sponsors care about engaged readers, not total list size.)

    If you sell a productised service with monthly retainers, you need:

    • Monthly recurring revenue vs. one-time project income.
    • Client lifetime value by acquisition channel.
    • Churn rate and leading indicators (missed payments, support ticket volume).

    If you monetise with affiliates and ad networks, track:

    • Revenue per thousand visitors by content category.
    • Affiliate conversion rate by product and placement.
    • Traffic source profitability after paid acquisition cost.

    None of these maps cleanly onto a Google Analytics 4 or Plausible default template. You have to build it yourself.

    When to abandon the template and start from scratch

    Here’s the test: open your current dashboard and ask, “If this metric moved 20% in either direction, would I change what I do this week?”

    If the answer is no, delete the widget.

    Then list the three business questions you asked yourself in the last 30 days. Examples:

    • “Why did last week’s post convert worse than the week before?”
    • “Which traffic source sends readers who actually buy?”
    • “Is my welcome sequence still working, or has open rate decayed?”

    Build one dashboard widget per question. If your analytics platform can’t answer it, you’re either tracking the wrong events or using the wrong tool.

    What good custom dashboards look like

    Effective operator dashboards are not beautiful. They’re a small grid—often just four to six widgets—that update weekly and drive a specific decision.

    A working example from a course creator I consulted for:

    • Widget 1: Revenue this month vs. same month last year.
    • Widget 2: Email-to-purchase conversion rate for the last four launches.
    • Widget 3: Refund rate by cohort (tracks product-market fit over time).
    • Widget 4: Traffic source breakdown for purchase-page visitors (not all visitors).

    That’s it. Four numbers. Every Monday, she knows whether to focus on traffic, conversion, or retention. The default Stripe dashboard showed gross volume and successful charges—impressive numbers that didn’t clarify what to do next.

    One dashboard, one decision

    If you’re still using a pre-built template, block an hour this week to rebuild from scratch. Start with one business question. Add only the metrics that answer it. If you can’t connect a widget to a decision you’ll make in the next 30 days, leave it out.

    The goal isn’t a dashboard that impresses a investor. It’s a dashboard that tells you what to do on Tuesday.

    What’s the one metric you check every week that actually changes how you work? Hit reply and tell me—I’m collecting examples for a deeper dive on operator-specific analytics setups.

  • Most online operators track revenue wrong—it compounds

    Most online operators track revenue wrong—it compounds

    Most online operators track revenue wrong—it compounds
    Photo by KOBU Agency on Unsplash

    Revenue tracking sounds straightforward: money comes in, you record it, you know what you made. But most solo operators and small teams get it wrong in ways that compound over time—distorting decision-making, complicating taxes, and hiding which parts of the business actually work.

    The error isn’t usually a miscounted invoice. It’s structural: tracking cash received instead of revenue earned, mixing gross and net figures, or failing to reconcile platform payouts with actual sales. These mistakes don’t stay isolated. They cascade into poor pricing decisions, inaccurate runway projections, and tax filings that require expensive amendments.

    Cash accounting hides what’s actually happening

    Most operators track revenue when money hits their bank account. That’s cash accounting, and it works fine until you start dealing with delayed payouts, refunds issued weeks later, or affiliate networks that pay 60 days in arrears.

    When you record Stripe revenue on the day of payout instead of the day of sale, your August numbers include sales from July and miss the last week of August entirely. Add in a refund from June that processes in August, and your month-over-month comparison becomes meaningless.

    Accrual accounting—recording revenue when the sale happens, regardless of when cash moves—fixes this. You track the sale on August 3, even if Stripe pays you on August 10. Refunds get recorded against the original sale month. Affiliate commissions get logged when earned, not when paid.

    This isn’t about compliance. It’s about knowing whether August was actually better than July, or whether you’re looking at a payout-timing illusion.

    Gross revenue vs. net revenue: the numbers diverge fast

    Stripe takes 2.9% plus 30 cents per transaction. Gumroad takes 10%. Affiliate networks take 20–30%. If you’re recording gross sales as revenue but paying expenses from net proceeds, your P&L is structurally wrong.

    Here’s what happens: you see $10,000 in sales, set aside 25% for taxes ($2,500), then realize you only received $8,500 after platform fees. Now you’re $1,500 short on your tax estimate, and that gap compounds every month.

    The fix is simple but requires discipline: decide whether you’re tracking gross or net, then apply it consistently across every revenue source. Most operators should track gross revenue and record platform fees as a cost of goods sold or merchant fee expense. That way, you can compare effective take-rates across Stripe, Gumroad, and direct PayPal invoices on equal footing.

    If you’re using a spreadsheet, add columns for gross, fees, and net. If you’re using accounting software, create separate accounts for platform fees and map them correctly during import.

    Platform dashboards lie by omission

    Stripe’s dashboard shows gross volume. Beehiiv‘s dashboard shows net revenue after their cut. ConvertKit shows gross subscription value but doesn’t subtract payment processing fees unless you export the full transaction CSV.

    If you’re pulling numbers from multiple dashboards and adding them together, you’re mixing gross and net without realizing it. The total is wrong, and worse, it’s wrong in a way that drifts further from reality as you add more revenue streams.

    The only fix is a single source of truth: a spreadsheet, a proper accounting tool like QuickBooks or Xero, or at minimum a dedicated revenue tracker like Baremetrics or ProfitWell. Import or manually enter every transaction with the same structure: date, gross, fees, net, source, product. Reconcile monthly against bank deposits.

    This sounds tedious, but it takes 20 minutes a month and prevents the six-hour reconciliation nightmare in January when you’re trying to close the year.

    Why this compounds

    Revenue tracking errors don’t just distort historical reports—they corrupt forward-looking decisions. If you think a product made $3,000 last quarter but it actually netted $2,100 after fees and refunds, you might double down on it instead of testing alternatives. If you believe your business grew 15% month-over-month when the real figure is 8%, you might overspend on hiring or tools.

    Tax filings compound the problem further. If your revenue tracking doesn’t match your 1099-K forms from payment processors, you’ll either overpay taxes or trigger an IRS inquiry. Both cost money and time you don’t have.

    The longer you wait to fix this, the harder it gets. Reconciling six months of transactions across three platforms is miserable. Reconciling 18 months is a billing event for your accountant.

    Start this week: pick one revenue source, export the last 90 days of transactions, and compare the total to what you’ve recorded. If the numbers don’t match within $50, your tracking is broken. Fix that one source, then add the next.

    Revenue tracking isn’t exciting. But it’s the foundation for every other decision you make. Get it right once, and it stays right. Get it wrong, and the error grows every month until you’re forced to stop and rebuild from scratch.

    Want more operator-focused breakdowns like this? Subscribe to One Two Three Send—one article daily, no fluff.

    Heads up — some links in this article are affiliate links. If you sign up through them, we may earn a small commission at no extra cost to you. We only recommend tools we use ourselves.

  • Google Search Console filter logic: combining AND vs. OR operators

    Google Search Console filter logic: combining AND vs. OR operators

    Google Search Console filter logic: combining AND vs. OR operators
    Photo: John Poyser via Wikimedia Commons (CC BY-SA 2.0)

    Google Search Console lets you stack filters to narrow down query, page, country, and device data. But the way it combines multiple conditions isn’t intuitive—and if you’re used to spreadsheet filter logic or SQL, you’ll get surprising results the first time you try to isolate a segment.

    The interface offers two combination modes: “Filter by AND” and “Filter by OR.” The labels sound clear. In practice, they behave differently depending on whether you’re filtering within the same dimension or across different ones.

    Same dimension: OR is the only option that works

    If you want to see data for two specific queries—say, “WordPress caching” and “WordPress CDN”—you’d think you could add both as separate query filters and choose AND to show rows matching both. You can’t. When you add multiple filters to the same dimension (queries, pages, countries, devices), Search Console forces OR logic even if you select AND.

    Why? A single search impression can’t satisfy two different query strings simultaneously. A click either came from “WordPress caching” or “WordPress CDN,” never both. So AND would return zero rows. Google’s interface quietly overrides your selection and treats same-dimension filters as OR.

    This trips up operators who want to exclude certain queries while viewing others. You can’t add a “Query contains ‘caching’” filter and a “Query does not contain ‘plugin’” filter and expect AND logic to show caching queries that aren’t about plugins. Both filters apply to the query dimension, so Search Console ORs them—you’ll see every caching query plus every query that doesn’t contain “plugin,” which is almost your entire dataset.

    Different dimensions: AND and OR work as labeled

    When you filter across different dimensions—one filter on queries, another on pages, a third on country—the AND/OR toggle behaves as expected.

    Example: You want to see how the query “email deliverability” performs on your /guides/postmark-setup page in the United States. Add three filters: query exactly matches “email deliverability,” page exactly matches your URL, country exactly matches USA. Set the combination mode to AND. You’ll get rows that satisfy all three conditions.

    Switch to OR, and you’ll see every impression that matches any of the three: every “email deliverability” query across all pages and countries, every impression on that Postmark guide regardless of query, and every US impression regardless of query or page. The dataset explodes.

    For cross-dimension filtering, AND is almost always what you want. OR is useful when you’re trying to compare performance across segments—like traffic from the US or the UK combined, filtered to a specific landing page.

    The non-obvious workaround: use regular expressions for exclusions

    If you need to exclude certain query patterns while keeping others, don’t stack multiple query filters. Use a single filter with a regular expression that combines inclusion and exclusion logic in one pattern.

    Say you want queries containing “WordPress” but not “theme” or “plugin.” Instead of three separate filters, use one: set the filter type to “Custom (regex)” and enter wordpress(?!.*(theme|plugin)). The negative lookahead excludes rows matching your unwanted terms without triggering Search Console’s forced OR behavior.

    Regular expressions aren’t exposed prominently in the filter dropdown—select “Custom (regex)” under the query filter type menu. The interface doesn’t validate your regex in real time, so test patterns in a regex tester before applying them to avoid silent failures.

    When filter combinations cost you accurate attribution

    Stacking too many filters—especially across queries and pages—can shrink your sample size below Search Console’s anonymization threshold. Google suppresses rows when impression counts fall under ~10 to protect user privacy. If your AND filter combination is too narrow, you’ll see “(not set)” or missing rows, even though the traffic exists.

    This happens most often when you filter by long-tail queries (low volume) AND specific pages (also low volume) AND a narrow date range. Broaden one dimension—expand the date range to 28 days, or widen the query filter to “contains” instead of “exactly matches”—and the data reappears.

    If you’re debugging attribution or trying to confirm whether a specific query drives traffic to a specific page, export the full dataset as a CSV and filter locally in a spreadsheet. The export includes rows that fall below the UI threshold, though Google still suppresses some for privacy.

    Want to catch more tool-behavior nuances like this? Subscribe to One Two Three Send for weekly breakdowns of how online-business software actually works—no fluff, just the mechanics that matter.

  • Attribution window settings: why 7-day click matters more than 30

    Attribution window settings: why 7-day click matters more than 30

    Attribution window settings: why 7-day click matters more than 30
    Photo by Catarina Carvalho on Unsplash

    Attribution windows determine how long after someone clicks a link you still get credit for their conversion. Most platforms default to 30 days for clicks, 1 day for views. That sounds generous—until you realize it’s hiding the signal you actually need.

    For content-driven online businesses, the gap between click and conversion tells you more than the conversion itself. A 30-day window lumps together immediate intent and vague awareness. A 7-day window isolates the traffic sources that drive action, not just browsing.

    What attribution windows actually control

    An attribution window is the lookback period a platform uses to assign credit. If someone clicks your affiliate link on Monday and buys on Wednesday, you get the commission—as long as Wednesday falls within the window.

    Google Analytics 4 defaults to 30-day click, 1-day view. Meta Ads uses 7-day click, 1-day view. Amazon Associates gives you 24 hours for most products, 90 days for a few categories. Stripe’s attribution tracking (if you’re using UTM parameters) doesn’t enforce a window at all—it’s first-touch forever unless you configure otherwise.

    The mismatch creates confusion. Your analytics might show 200 conversions attributed to a blog post, while your payment processor shows 80. The difference isn’t missing data—it’s window drift.

    Why shorter windows surface better decisions

    A 30-day attribution window inflates the value of top-of-funnel content. Someone reads your SEO guide in July, bookmarks it, forgets about it, then subscribes in August after seeing a LinkedIn post. The guide gets credit. That’s not wrong, but it’s not actionable.

    When you tighten to 7 days, you see which content drives near-term intent. A tutorial that converts within a week is fundamentally different from an evergreen pillar post that nudges people over months. Both have value, but if you’re deciding what to write next, the 7-day data tells you what creates momentum.

    I tested this on a niche WordPress hosting comparison site. At 30 days, my top post by attributed revenue was a 4,000-word “ultimate guide.” At 7 days, it was a 600-word troubleshooting article about a specific plugin conflict. The guide brought awareness. The troubleshooting post brought buyers. I wrote six more troubleshooting posts. Revenue per publish hour tripled.

    Where to adjust attribution settings

    Google Analytics 4: Admin → Data display → Attribution settings. Change “Reporting attribution model” from default (usually data-driven, 30-day click) to any custom window. You can set click and view windows independently. I run 7-day click, 1-day view for most clients.

    Meta Ads Manager: doesn’t let you change the window in reporting after the fact, but you can toggle between 1-day and 7-day views in the attribution dropdown above your campaigns table. The data’s stored for both; you’re just filtering the view.

    Affiliate dashboards (Amazon, Impact, CJ): you can’t change the window—it’s set by the merchant. But you can export raw click and conversion timestamps, then calculate your own 7-day attribution in a spreadsheet. I do this monthly. It shows which content is worth updating versus which is just coasting on old backlinks.

    Stripe or other payment processors: if you’re passing UTM parameters into metadata fields, you control attribution logic in your own reporting layer. Most operators don’t bother. If you’re doing $5k+/month in subscriptions, it’s worth the two hours to set up a Zapier flow or custom script that logs source + timestamp, then pivots by window in a Google Sheet.

    When 30 days still makes sense

    Brand-new sites benefit from longer windows early on—you don’t have enough conversions to segment cleanly, and you want to reward any content that contributes. Once you’re above ~50 conversions per month, tighten to 7 days for operational decisions, but keep a 30-day dashboard around for investor updates or year-end reviews.

    High-ticket products or B2B services genuinely have longer consideration cycles. If you’re selling $2,000 courses or SaaS annual plans, a 30-day (or even 90-day) window reflects reality. But even then, I’d run both: 7-day to find high-intent content, 30-day to credit awareness plays.

    Content syndication or guest posts often show delayed conversions—someone discovers you on Medium, then subscribes two weeks later via your site. A 30-day window captures that. But if most of your traffic is owned (SEO, email, direct), shorter windows cut through the noise faster.

    One thing to try this week: Pull your top 10 attributed traffic sources in Google Analytics at 30-day click attribution. Then switch to 7-day and compare. Anything that drops out of the top 10 is awareness, not conversion. Anything that stays is working. Double down there.

    Want more analytics breakdowns like this? Subscribe to One Two Three Send—we dig into the tooling decisions solo operators actually face, one focused article at a time.

  • ConvertKit broadcast analytics: open rate vs. link click timing lag

    ConvertKit broadcast analytics: open rate vs. link click timing lag

    ConvertKit broadcast analytics: open rate vs. link click timing lag
    Photo by Kit (formerly ConvertKit) on Unsplash

    ConvertKit’s broadcast analytics dashboard updates in real time—sort of. Opens appear within minutes of sending. Link clicks take longer. Sometimes hours longer. If you’ve ever sent a broadcast, refreshed obsessively, and wondered why you’re seeing 400 opens but only 12 clicks an hour later, you’re not imagining things. The delay is real, and it’s not a bug.

    Why opens report faster than clicks

    Email opens are tracked with a tiny invisible image embedded in every message. When a recipient’s email client loads that image, ConvertKit logs an open. Most modern email clients—Gmail, Apple Mail, Outlook—preload images as soon as the email arrives in the inbox, even before the recipient actually opens it. That means ConvertKit sees the open event almost immediately, whether or not anyone’s reading.

    Link clicks are different. They require the recipient to actually click a URL in your email. ConvertKit wraps every link with a tracking redirect, so when someone clicks, the request hits ConvertKit’s servers first, gets logged, then redirects to your destination. That’s a real human action, not a preloaded asset. It takes time—and it only happens if someone’s genuinely engaged.

    The lag isn’t just about recipient behavior. ConvertKit processes click data in batches. Opens are logged instantly because they’re high-volume and low-cost to write. Clicks trigger additional database writes—subscriber activity logs, segment recalculations, automation triggers—so they’re processed in queues. Depending on send volume and server load, that queue can take 15 minutes to two hours to fully flush.

    When the lag matters (and when it doesn’t)

    If you’re sending time-sensitive content—a flash sale, a webinar reminder, a product launch—you need to know whether people are clicking, not just opening. But checking analytics five minutes after send is premature. The first wave of opens will arrive fast. Clicks won’t stabilize for at least 30 minutes, often longer.

    Here’s the timing pattern I’ve seen across dozens of broadcasts to lists between 2,000 and 50,000 subscribers: opens plateau around 60–90 minutes post-send. Clicks plateau around 90–120 minutes. If you’re making a decision—resend to non-openers, adjust your landing page, kill an underperforming link—wait at least two hours. Earlier than that, you’re reading incomplete data.

    One edge case: if you’re using ConvertKit’s link triggers to start an automation (e.g., someone clicks “Download the guide” and gets tagged or moved to a sequence), those triggers fire in real time. The click gets logged for automation purposes immediately, but the analytics dashboard number lags behind. So your automation might run before the dashboard reflects the click. That’s intentional—ConvertKit prioritizes subscriber experience over reporting speed.

    How to read early-stage broadcast data correctly

    Don’t calculate click-through rate in the first hour. The denominator (opens) inflates faster than the numerator (clicks), so your CTR will look artificially low. If you see 8% CTR at the 30-minute mark, it’ll likely settle closer to 12–15% by hour three. I’ve watched this pattern repeat across hundreds of sends.

    Instead, track absolute click volume early on. If you’re expecting 200 clicks based on past performance and you’re seeing 40 after 30 minutes, you’re probably on track. If you’re seeing 4, something’s wrong—your subject line didn’t match your content, your link isn’t visible, or your CTA is buried.

    One non-obvious tactic: compare your current broadcast’s early click volume to a similar past broadcast at the same elapsed time. ConvertKit doesn’t surface this view natively, so keep a simple spreadsheet: broadcast name, list size, clicks at 30 min, clicks at 60 min, final clicks at 24 hours. After five or six sends, you’ll have a reliable benchmark. If today’s 30-minute number is significantly lower than your average, you can troubleshoot before the send is fully delivered.

    When stale data becomes a problem

    The lag compounds if you’re running paid traffic to a landing page mentioned in your broadcast. You send the email, check ConvertKit 20 minutes later, see weak click numbers, panic, and spin up a Facebook ad to the same page. Then the ConvertKit clicks catch up an hour later, your ad spend overlaps with organic email traffic, and you can’t tell which source drove conversions. If you’re mixing email and paid on the same day, give email at least 90 minutes to report fully before you activate paid.

    ConvertKit’s reporting delay is also why A/B subject line tests sometimes feel inconclusive. The platform splits your list, sends both variants, and declares a winner based on open rate after a set window (usually 4 hours). But if clicks are your real goal, the winner might not be the variant with the highest open rate—it’s the one with the best click-through. You won’t know that until hours after ConvertKit has already sent the winning variant to the remainder of your list.

    If click-through matters more than open rate for your business, skip ConvertKit’s built-in A/B test. Manually split your list into two segments, send both variants as separate broadcasts, and wait 3–4 hours to compare click data. It’s more work, but the data’s accurate.

    Got a ConvertKit analytics question we should cover? Reply to this email—we read every one and use reader questions to shape future articles. If you found this useful, forward it to another operator who’s probably refreshing their dashboard right now.

  • Google Analytics 4 custom event parameters: the 25-limit nobody explains

    Google Analytics 4 custom event parameters: the 25-limit nobody explains

    Google Analytics 4 custom event parameters: the 25-limit nobody explains
    Photo: Ajiro Shinpei via Wikimedia Commons (CC BY-SA 4.0)

    Google Analytics 4 gives you almost unlimited flexibility to track custom events. You can fire anything: lead_form_submit, coupon_applied, video_watched. But there’s a hard constraint most solo operators don’t discover until it’s too late: GA4 only indexes 25 custom event parameters per property.

    After that, new parameters still get logged in the raw event stream—but they won’t appear in standard reports, Explore, or Looker Studio. You can’t dimension or filter by them. They’re effectively invisible unless you’re pulling BigQuery exports, which most small operators aren’t.

    This isn’t a bug. It’s a design decision Google made to keep the product performant. But it catches people by surprise because GA4’s interface doesn’t warn you when you’re approaching the limit, and old Universal Analytics didn’t have this restriction in the same way.

    How the 25-parameter limit actually works

    When you send a custom event to GA4—say, newsletter_signup with parameters like source, landing_page, referrer, and email_domain—those parameters need to be manually registered as custom dimensions in the GA4 admin panel before they show up in reports.

    GA4 gives you:

    • 25 custom dimensions (event-scoped)
    • 25 custom dimensions (user-scoped)
    • 50 custom metrics (numeric values)

    Event-scoped dimensions are what most operators burn through first. These are things like button_label, video_title, product_category—anything that describes a single interaction.

    Once you hit 25 event-scoped dimensions, you’re done. You can’t add more without archiving an existing one. And archiving doesn’t free up the slot—it just stops collection. Historical data stays, but the dimension becomes read-only.

    What breaks when you hit the ceiling

    Let’s say you’re tracking newsletter signups across six different lead magnets. You’ve been sending lead_magnet_name as a parameter for months. Then you launch a new sponsored post tracking setup and add five more parameters: sponsor_name, placement_type, cta_variant, reader_segment, and content_topic.

    You go to GA4 Explore to build a report. The new parameters don’t show up in the dimension picker. You check the raw event in DebugView—it’s firing correctly. The data is being sent. But it’s not indexed, so it’s not queryable.

    Here’s what you lose:

    • You can’t segment audiences by that parameter
    • You can’t build Explore reports around it
    • You can’t use it in Looker Studio dashboards
    • You can’t create conversion funnels that filter by it

    The only workaround is BigQuery, which requires a GA4 360 subscription (starting at $50,000/year) or a manual export setup most indie operators won’t bother with.

    How to plan your parameter budget

    The fix isn’t technical—it’s editorial. You need to treat custom dimensions like a finite resource and plan what you track before you start sending events.

    Start by auditing what you’re already using. Go to Admin > Data display > Custom definitions in GA4. You’ll see a list of every registered dimension and metric. Count them. If you’re above 20, you’re in the danger zone.

    Then ask: Which of these dimensions do I actually query? Most operators register parameters “just in case” and never look at them again. Archive anything you haven’t used in a report in the last 90 days.

    For new tracking, consolidate where you can. Instead of separate parameters for lead_magnet_name, lead_magnet_category, and lead_magnet_format, use a single lead_magnet_id and map it to a lookup table in your reporting layer. Instead of tracking button_color, button_size, and button_position separately, combine them into one button_variant string like blue_large_sidebar.

    This isn’t elegant, but it works. And it keeps you under the limit.

    The non-obvious tip: namespace your parameters early

    If you’re starting fresh or still have slots available, prefix your custom parameters by category. Use form_name, form_step, form_source instead of generic names like name, step, source. It makes your dimension list easier to scan, reduces the chance of accidental overwrites, and helps you spot redundant tracking before you register a new dimension.

    And when you do hit the limit? Don’t panic and start archiving things randomly. Export your current Explore reports first, note which dimensions they depend on, and only archive parameters that aren’t load-bearing.

    Want more breakdowns like this? Reply with the analytics edge case that’s been tripping you up—we’ll cover it in a future issue.