Author: onetwothreeadmin

  • Most analytics platforms count the wrong conversion timestamp

    Most analytics platforms count the wrong conversion timestamp

    Most analytics platforms count the wrong conversion timestamp
    Photo by Stephen Dawson on Unsplash

    Open any analytics dashboard and you’ll see conversion timestamps down to the second. Clean numbers. Precise attribution. The only problem: most platforms are measuring the wrong moment.

    Analytics tools record when the tracking pixel fired, when the webhook hit your server, or when the JavaScript event bubbled up. What they don’t capture—and can’t capture—is when the visitor actually decided to convert. That gap between decision and detection creates blind spots that skew how you read your funnel data.

    The detection lag problem

    A visitor reads your landing page at 2:14 PM. They’re convinced. But they don’t hit the button yet—they open three more tabs to compare pricing, check a review site, and read your refund policy. At 2:31 PM, they close the tabs and click through. Your analytics platform stamps the conversion at 2:31 PM and attributes it to a seventeen-minute session.

    The reality: the conversion happened in the first ninety seconds. Everything after that was confirmation bias and friction removal.

    This matters because most attribution models assign credit based on the timestamp of the tracked event, not the moment of intent. If you’re running time-based attribution windows—last-click within 7 days, for example—you’re giving credit to touchpoints that happened after the decision was already made.

    Google Analytics 4 defaults to a 30-day click window and a 1-day view window. Plausible and Fathom don’t track cross-session attribution at all. None of them distinguish between decision time and detection time, because the browser has no way to know when a visitor made up their mind.

    Where the gap shows up in your data

    Time-on-page averages get inflated. If your analytics say visitors spend an average of four minutes on your sales page before converting, you’re probably looking at two minutes of reading and two minutes of tab-switching and mental accounting. The page itself only held attention for half that window.

    Session duration becomes misleading for the same reason. A thirteen-minute session that ends in a sale might include eight minutes of the visitor doing something else entirely—answering an email, getting coffee, talking to a coworker. The conversion timestamp marks the end of that session, but the persuasive work was done much earlier.

    This shows up most clearly in email campaigns. You send a newsletter at 9:00 AM. A reader opens it at 9:14 AM, clicks through to your product page, and leaves the tab open while they finish their morning routine. At 10:02 AM, they come back to the tab and complete checkout. Your analytics platform records a 48-minute gap between click and conversion. Your email platform reports the click at 9:14 AM. Neither tool knows that the decision was made at 9:16 AM and everything else was logistics.

    How to interpret your timestamps correctly

    Start by cutting your reported time-on-page numbers in half as a mental heuristic. It’s not precise, but it’s closer to reality than taking the raw figure at face value. If your analytics say visitors spend six minutes on a landing page before converting, assume the actual engaged time was closer to three.

    For session-based conversion funnels, focus on the first touchpoint timestamp, not the last. If you’re trying to figure out what drives conversions, the moment someone entered your funnel is more predictive than the moment they finally clicked a button. This is especially true for content-driven businesses, where a visitor might read a blog post, leave, come back two days later, and subscribe. The blog post did the work; the subscription form just captured it.

    When you’re analyzing email campaign performance, measure click-to-conversion time but assume the decision window was much shorter. If your average click-to-purchase time is thirty minutes, the actual persuasion probably happened in the first five. The rest is tab management and distraction.

    For paid traffic, shrinking attribution windows make this worse. As platforms move toward same-day attribution—Meta and Google both tightened their windows in the past year—you lose visibility into conversions that were influenced early but detected late. A visitor who clicks your ad on Monday, thinks about it, and converts on Wednesday won’t show up in a one-day click window, even though the ad drove the decision.

    What you can do about it

    You can’t fix the detection lag, but you can design around it. If you’re running A/B tests on landing pages, extend your test duration to account for the gap between exposure and conversion. A visitor who sees variant B today might not convert until tomorrow, and if you stop the test too early, you’ll miss that delayed signal.

    For content attribution, track entry page separately from conversion page. If someone lands on a blog post and converts three sessions later, your analytics platform will credit the last session’s entry page—probably your homepage or a direct visit. Manually tagging your highest-intent content and tracking it as a first-touch dimension gives you a clearer picture of what actually moves people.

    If you’re using a tool like Google Analytics 4, create a custom event that fires when a visitor hits a high-intent action—scrolling to your pricing table, opening your FAQ accordion, clicking a comparison chart. Those micro-events are closer to the decision moment than the final conversion timestamp, and they give you a better signal for what’s working.

    For email-driven conversions, segment by time-to-click rather than time-to-conversion. If someone clicks your email link within five minutes of opening, that’s a strong signal regardless of when they complete checkout. Late conversions still count, but early clicks tell you more about your message’s persuasive power.

    One more thing: if you’re debugging a funnel that looks broken—high traffic, decent engagement, low conversions—check whether your analytics platform is recording conversions in a different session than the one where the decision happened. Cross-session attribution gaps are one of the most common reasons funnels appear to underperform when the real problem is measurement, not messaging.

    Want more breakdowns like this? Subscribe to One Two Three Send—we dig into the mechanics of online-business tools every week, with no fluff and no vendor spin.

  • Klaviyo’s flow filters stack backwards—here’s what fires first

    Klaviyo’s flow filters stack backwards—here’s what fires first

    Klaviyo’s visual flow builder shows filters stacked top-to-bottom, but the platform evaluates them in reverse. If you’re wondering why subscribers who shouldn’t qualify keep entering flows—or why eligible contacts get blocked—the evaluation order is probably the issue.

    Most operators assume Klaviyo reads filters like a book: top to bottom, left to right. It doesn’t. The platform processes conditional splits and trigger filters from the bottom of the stack upward, which means the last filter you add is the first one Klaviyo checks.

    How filter evaluation actually works

    When you build a flow in Klavivy, you add trigger conditions and conditional splits by stacking filters in the visual editor. A typical abandoned-cart flow might include:

    • Trigger: Started Checkout
    • Filter 1: Cart value greater than $50
    • Filter 2: Has not placed an order in the last 7 days
    • Filter 3: Email address contains “@gmail.com” (for a test segment)

    You’d expect Klaviyo to check cart value first, then recency, then email domain. Instead, it evaluates Filter 3, then Filter 2, then Filter 1. If any condition fails, the contact exits the flow without triggering downstream checks.

    This matters most when you’re using expensive API lookups, custom property checks, or time-based windows. A filter that queries your inventory API should run last in your logic chain—not first—so you’re only making the call for contacts who’ve already cleared cheaper, faster filters.

    When reversed logic breaks flows

    Reverse evaluation causes three common failure modes:

    Premature exits. If you place a narrow, restrictive filter at the bottom of the stack (visually), Klaviyo checks it first. Contacts who would have qualified under your primary conditions get blocked before those conditions are ever evaluated. You’ll see low flow-entry counts and wonder why your segmentation isn’t working.

    Wasted API calls. If your bottom filter pings an external service or checks a slow custom property, every single contact hits that check—even those who would’ve been disqualified by simpler conditions higher in the stack. At scale, this burns through rate limits and slows flow execution.

    Confusing A/B test results. If you’re testing two versions of a flow and one includes a filter at the bottom that the other lacks, the two flows aren’t just different in content—they’re evaluating contacts in a different order. Your test measures filter-stack architecture, not messaging.

    How to stack filters correctly

    Build your filter stack in reverse priority. The condition you want Klaviyo to check first should sit at the bottom of the visual stack. The condition you want checked last goes at the top.

    For an abandoned-cart flow, the correct visual order (top to bottom) would be:

    • Cart value greater than $50
    • Has not placed an order in the last 7 days
    • Email address contains “@gmail.com”

    Klaviyo will evaluate the Gmail filter first (fast, local check), then recency (medium-speed property lookup), then cart value (which may involve a Shopify API call depending on your integration setup).

    If you’re using conditional splits mid-flow, the same rule applies. Klaviyo evaluates the bottom branch condition first. If you’re splitting on “opened email in last 3 days” versus “clicked link in last 3 days,” put the click condition at the bottom so engaged contacts get prioritized before the broader open check runs.

    One non-obvious trick: use trigger filters to pre-qualify

    Instead of stacking filters inside a flow, move your fastest, most restrictive conditions into the trigger itself. Klaviyo evaluates trigger filters before the flow even starts, which means contacts who don’t qualify never enter the flow queue. This keeps your flow analytics clean and reduces server load.

    For example, if you only want to target customers with lifetime value above $200, add that as a trigger filter rather than a conditional split two steps into the flow. You’ll see accurate entry counts, and you won’t waste sends or delay timers on contacts who were never going to qualify.

    Klaviyo’s documentation doesn’t foreground the reverse-evaluation behavior—most operators learn it by accident after a flow misfires. Once you know the pattern, you can design filter stacks that execute faster, cost less, and behave predictably.

    Want more breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools that run your online business—no fluff, just the mechanics that matter.

  • Affiliate link disclosure placement: where regulators actually look

    Affiliate link disclosure placement: where regulators actually look

    Most solo operators know they need to disclose affiliate relationships. Fewer know that where you put the disclosure matters as much as whether you include one at all.

    The FTC’s Endorsement Guides don’t just require disclosure—they require it to be “clear and conspicuous.” That’s a legal standard with specific implications for placement, and getting it wrong doesn’t just risk a warning letter. It undermines reader trust and can trigger platform penalties from Google, Amazon Associates, or your payment processor.

    Here’s what actually counts as compliant placement, based on FTC enforcement actions and platform policy updates through mid-2024.

    Proximity beats boilerplate

    A disclosure at the bottom of your page, buried in a footer, or tucked into a dedicated “disclosures” page doesn’t meet the FTC standard. The agency’s position is consistent: readers must see the disclosure before they click the affiliate link or make a purchasing decision.

    That means:

    • In blog posts, the disclosure needs to appear above or directly adjacent to the first affiliate link—not at the end of the article.
    • In email newsletters, it should sit in the body copy near the link itself, not just in a footer template.
    • In social media posts, it must be in the caption or the first line—not hidden behind a “see more” fold or buried in a comment.

    The FTC has cited cases where disclosures appeared on a separate page linked via “Advertising Disclosure” in small footer text. Those don’t count. If a reader has to hunt for context, the disclosure fails.

    Language that works (and doesn’t)

    “This post contains affiliate links” is the baseline, but it’s often not enough. The FTC wants readers to understand the material connection—that you earn money if they buy.

    Effective language includes:

    • “I earn a commission if you buy through this link.”
    • “This is an affiliate link—I get paid if you purchase.”
    • “Paid partnership with [Brand]. I receive compensation for purchases.”

    What doesn’t work:

    • Ambiguous terms like “supported by” or “in partnership with” without explaining compensation.
    • Abbreviations like “aff link” or “#ad” in contexts where the audience may not know what they mean.
    • Disclosures that only appear on hover states, alt text, or link titles—those aren’t visible enough.

    The standard isn’t whether you think it’s obvious. It’s whether a reasonable reader scrolling quickly would understand the relationship before they click.

    Platform-specific requirements stack on top

    FTC compliance is the floor. Individual affiliate programs often add stricter rules.

    Amazon Associates requires you to include the phrase “As an Amazon Associate I earn from qualifying purchases” on any page with Amazon links. That’s in addition to general proximity rules—it can’t just live in your site footer.

    Google’s ad policies flag pages where affiliate disclosures aren’t “immediately visible.” If your disclosure requires scrolling past the fold, your organic rankings can take a hit or your AdSense account can get flagged for policy review.

    Payment processors like Stripe and PayPal can freeze accounts if they receive chargebacks or complaints tied to unclear affiliate relationships. It’s rare, but it happens—especially if you’re selling info products that include affiliate upsells without clear disclosure in the checkout flow.

    When disclosure must repeat

    One disclosure at the top of a 2,000-word post isn’t enough if you’re linking to five different affiliate products throughout. The FTC’s guidance is that disclosure should appear “in close proximity” to each claim or link that could mislead.

    Practical implementation:

    • If you’re reviewing multiple products and each has affiliate links, include a line like “affiliate link” or “I earn from this” next to each product mention.
    • In list-style posts (“Top 5 tools for X”), add disclosure language under each tool’s heading, not just once at the intro.
    • In email, if you mention an affiliate link in the body and again in a P.S., disclose in both places.

    It feels redundant. That’s the point. The goal is to prevent any moment where a reader clicks without understanding the relationship.

    What to do now

    Audit your three highest-traffic affiliate pages. Open each in an incognito window and scroll as a first-time reader would. Ask:

    • Can I see the disclosure before I reach the first affiliate link?
    • Is the language clear enough that I’d understand I’m clicking a paid recommendation?
    • Does the disclosure repeat near every distinct affiliate link or product claim?

    If any answer is no, revise before you publish your next affiliate post. Compliance isn’t about legal paranoia—it’s about making sure your readers know what they’re clicking, which is the foundation of long-term trust in a content business.

    One Two Three Send covers the operational details that matter when you’re running a content business solo. If this was useful, subscribe for the next one.

  • Paid ads attribution windows shrink to one day—what that breaks

    Paid ads attribution windows shrink to one day—what that breaks

    If you’re running paid ads to grow your newsletter, course, or content site, you’ve probably noticed something strange in your dashboard: conversions are lower than you remember, even when traffic and spend stay flat.

    The culprit isn’t your creative or your targeting. It’s attribution windows—and both Meta and Google have quietly tightened them over the past two years.

    As of mid-2023, Meta Ads defaults to a 1-day click attribution window for most campaign objectives. Google Ads moved to a similar model in 2022, deprecating its 30-day window for most conversions. That means if someone clicks your ad on Monday but converts on Wednesday, the platform won’t count it.

    For solo operators running lean budgets, this shift breaks the feedback loop between what you spend and what you earn.

    What attribution windows actually measure

    An attribution window defines how long a platform will credit a conversion back to an ad click or impression. If you set a 7-day click window, any purchase or sign-up that happens within seven days of someone clicking your ad gets attributed to that campaign.

    Shorter windows make your ads look less effective. Longer windows inflate performance by claiming credit for conversions that might have happened anyway.

    Until recently, the industry standard was 7 days for clicks and 1 day for views. Meta offered 28-day windows as an option. Google let you choose up to 90 days in some cases.

    Now the defaults have contracted. Meta’s 1-day click window is the starting point for Conversions, Traffic, and Engagement campaigns. Google’s data-driven attribution model leans heavily on same-day conversions, especially for campaigns tied to GA4 events.

    Why the change? Privacy regulations like iOS 14.5’s App Tracking Transparency and Europe’s GDPR made cross-device and delayed attribution harder to track. Platforms responded by shortening windows and leaning on modeled conversions—which are estimates, not direct measurements.

    What a 1-day window hides

    Most online-business funnels don’t convert in 24 hours. Someone sees your ad, clicks through, reads your welcome sequence, thinks about it, then buys three days later. A 1-day window only credits the ad if they purchased the same day they clicked.

    Here’s what gets lost:

    • Email nurture sequences. If your funnel includes a three-email welcome series, conversions will happen 48–72 hours after the click. The ad won’t get credit, even though it drove the subscriber.
    • Comparison shoppers. People researching courses, memberships, or SaaS tools often take a week to decide. A 1-day window treats that as organic, not paid.
    • Mobile-to-desktop conversions. Someone clicks your ad on their phone during lunch, then completes checkout on their laptop that evening. If it’s past midnight UTC, that’s day two—no attribution.

    The result: your dashboard shows a cost per acquisition that’s higher than reality, and campaigns that actually work look marginal or unprofitable.

    When to extend the window manually

    Both Meta and Google let you adjust attribution windows, but the settings are buried.

    In Meta Ads Manager, go to your campaign settings, scroll to Attribution Setting, and change the window to 7-day click or 7-day click + 1-day view. You can’t go longer than 7 days anymore, but that’s still better than one.

    In Google Ads, navigate to Tools → Conversions, select your conversion action, and edit the attribution model. Switch from “Data-driven” to “Time decay” or “Last click” with a 7- or 30-day window if you’re tracking conversions via Google Analytics 4 or a third-party pixel.

    Extend the window if:

    • Your average time-to-purchase is longer than 24 hours (check your CRM or Stripe data).
    • You’re running ads to a lead magnet with a backend offer—initial conversions happen fast, but paid conversions take days.
    • Your checkout process requires account creation, which adds friction and delays.

    Don’t extend it if you’re optimizing for same-day actions like webinar registrations or flash-sale purchases. In those cases, a 1-day window gives cleaner signal and avoids over-crediting ads for organic repeat traffic.

    How to measure what the platform misses

    Even if you extend the attribution window, you won’t capture everything. Platforms can’t see conversions that happen off-pixel—like someone who sees your ad, searches your brand name two days later, and buys via organic search.

    To fill the gap, track first-touch attribution in your own analytics. Add UTM parameters to every ad (e.g., utm_source=meta&utm_medium=cpc&utm_campaign=spring-launch). When someone converts, check the UTM values stored in your CRM or analytics tool to see which ad introduced them, even if the platform didn’t credit it.

    Tools like Plausible, Fathom, or Google Analytics 4 let you build custom reports showing conversions by first UTM source. Compare that to what Meta or Google report. The difference is what the 1-day window hid.

    If the gap is large—say, your CRM shows 40 conversions from Meta ads but Meta only reports 22—your actual cost per acquisition is lower than the dashboard suggests. That’s the signal to keep spending, even when the platform tells you to pause.

    One more tactic: ask new customers how they found you. A simple post-purchase question (“Where did you first hear about us?”) surfaces attribution that no pixel can track. If half your buyers say “Facebook ad,” but Meta only credits a quarter of revenue, you know the window is under-counting.

    Want more breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and tactics that actually move the needle for solo operators.

  • WordPress CDN services: when they slow your site instead of speeding it up

    WordPress CDN services: when they slow your site instead of speeding it up

    Content delivery networks sit near the top of every WordPress performance checklist. The pitch is simple: distribute static assets across edge servers, cut latency, serve files faster. For many solo operators, though, a CDN introduces more overhead than it solves—especially when you’re running a text-first site with minimal traffic.

    The problem isn’t CDNs themselves. It’s the gap between what they’re built for and how most small operators deploy them.

    When CDN cache misses cost more than direct origin requests

    A CDN works by caching copies of your assets at edge locations close to visitors. When someone requests a file, the CDN checks its cache. If the file exists (a cache hit), the CDN serves it directly. If it doesn’t (a cache miss), the CDN pulls the file from your origin server, caches it, then serves it to the visitor.

    That origin pull adds latency. For the first visitor after a cache expires, the request travels to the CDN, then to your origin, then back to the CDN, then to the visitor. That’s slower than a direct origin request—sometimes by 200–400 milliseconds.

    If your site gets 500 visitors a day and your CDN cache expires every four hours, a meaningful percentage of requests trigger origin pulls. The benefit of edge caching evaporates when cache-hit ratios stay below 70%. Sites with low traffic or frequently changing content spend more time in cache-miss penalty than they gain from edge delivery.

    Check your CDN dashboard’s cache-hit ratio. If it’s under 75%, you’re paying for infrastructure that slows more requests than it accelerates.

    HTML caching at the edge breaks dynamic WordPress features

    Most CDN providers let you cache HTML, not just static assets like images and CSS. The promise: serve entire pages from the edge without touching your origin server at all.

    The catch: WordPress is dynamic. Comment counts update. Logged-in users see personalized navigation. Cart contents change. Caching full HTML breaks all of that unless you configure cache exceptions for every dynamic element.

    Cloudflare’s default WordPress rules exclude logged-in users and query strings, but many operators enable aggressive HTML caching to chase Lighthouse scores. The result: broken checkout flows, stale post counts, and support requests from confused readers.

    If you cache HTML, you need rules that exclude:

    • Any page with cookies (wp-admin, cart, user sessions)
    • URLs with query strings (?utm_source, ?add-to-cart)
    • POST requests (forms, comments)
    • Paths where content updates frequently (/feed, /comments)

    Most small operators don’t have the traffic volume to justify the complexity. Caching assets only—images, CSS, JavaScript—delivers 80% of the performance gain with none of the breakage.

    CDN costs scale faster than traffic for small sites

    Free CDN tiers sound appealing, but bandwidth limits hit sooner than expected. Cloudflare’s free plan is genuinely unlimited for most use cases, but competitors like BunnyCDN, StackPath, and KeyCDN charge per gigabyte after you cross 100–500 GB per month.

    A single viral post with embedded images can push a small site past free-tier limits. BunnyCDN charges $0.01–0.04 per GB depending on region. If a post with 2 MB of images gets 50,000 pageviews in a weekend, that’s 100 GB of bandwidth—$1 to $4 in CDN costs. Not catastrophic, but surprising if you budgeted for free infrastructure.

    For most solo operators running text-heavy WordPress sites with under 10,000 monthly visitors, the marginal speed gain from a CDN doesn’t justify the configuration overhead or the risk of surprise bandwidth bills. A well-tuned origin server with image compression and browser caching delivers load times under two seconds—fast enough for retention and SEO.

    When a CDN actually helps

    CDNs make sense when:

    You serve large files. Video embeds, downloadable PDFs, high-resolution images, or course assets benefit from edge delivery. A 50 MB video file served from a Singapore edge node loads faster for an Australian visitor than pulling from a US origin server.

    Your traffic is geographically distributed. If your analytics show 30% of visitors from Europe, 30% from Asia, and 40% from North America, a CDN cuts cross-continent latency. If 90% of your traffic comes from one region, a well-located origin server is simpler.

    You exceed 50,000 monthly pageviews. At that scale, cache-hit ratios stabilize above 80%, and the performance gain outweighs configuration complexity.

    Before you add a CDN, run a baseline test. Use WebPageTest or GTmetrix to measure time to first byte (TTFB) and fully loaded time from multiple locations. Then enable the CDN, configure asset-only caching, and re-test. If TTFB improves by less than 150 milliseconds, the CDN isn’t doing much.

    Want more operator-to-operator breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the tools and infrastructure decisions that matter for small online businesses. No fluff, just the details that save you time and money.

  • Social platform APIs throttle read requests faster than writes

    Social platform APIs throttle read requests faster than writes

    Most solo operators automate social posting without realizing that fetching data from social platforms burns through API quotas two to five times faster than publishing content. If you’re building workflows that pull engagement metrics, follower lists, or historical posts, you’ll hit rate limits long before your posting automation breaks.

    This asymmetry isn’t accidental. Platforms want you to publish—it’s free content for them. But reading data at scale lets you build competing analytics products, scrape competitor insights, or export your audience. So they throttle reads aggressively.

    How the limits actually break down

    Twitter’s free API tier gives you 10,000 read requests per month but allows 50,000 tweets. LinkedIn’s API (available only to approved partners) caps profile lookups at 100 per day, while post publishing has no hard daily cap for most use cases. Instagram’s Graph API allows 200 read calls per hour but handles significantly more media uploads in the same window.

    The result: you can schedule a month of posts without trouble, but a single workflow that checks your last 500 tweets for engagement stats will exhaust your quota in an afternoon.

    Publer and similar social schedulers stay within limits by caching aggressively and batching requests. If you’re building custom automation with Make or Zapier, you don’t get that layer of protection by default.

    Where automation workflows break first

    The most common failure point is analytics dashboards. A Zap that pulls yesterday’s top posts from three platforms, calculates engagement rates, and logs them to a Google Sheet will chew through 90–150 API calls per run. Do that daily, and you’re at 2,700–4,500 calls per month—just from one simple report.

    Another trap: workflows that check if a post exists before publishing a duplicate. Every existence check is a read. If you’re cross-posting the same content to Twitter, LinkedIn, and Facebook, and each platform requires a lookup to confirm the post isn’t already live, you’ve tripled your read quota burn for no publishing benefit.

    Follower-sync workflows hit limits fastest. Pulling your full follower list from LinkedIn every week to update a CRM costs 100+ calls per run if you have a few thousand connections. Do it weekly, and you’re over budget in a month.

    How to route around the limits

    Cache everything you can locally. If you need engagement data for analysis, pull it once per week and store it in Airtable, Notion, or a Google Sheet. Run reports against your own data, not the live API.

    Batch your reads. Instead of checking post performance every time you publish, schedule one daily or weekly job that fetches all recent posts in a single pass. You’ll use 7–30 calls per month instead of 200+.

    Use webhooks when platforms offer them. Facebook and Instagram can push post insights to your server when events happen, eliminating the need for polling. Twitter’s webhook support is limited, but if you’re on a paid tier, it’s worth the setup cost.

    For follower syncs, only pull deltas. Most APIs let you request changes since a timestamp. If you’re tracking new followers, ask for additions since your last check instead of re-fetching the entire list.

    When to pay for higher limits

    Twitter’s Basic tier costs $100/month and raises read limits to 10,000 per month at the app level—but it’s still far below what you’d need for daily analytics across multiple accounts. The real break-even comes if you’re running client work or managing five-plus brands. At that scale, a single shared automation hitting limits blocks everyone.

    LinkedIn doesn’t sell API access directly to solo operators. You’ll need to partner with an approved vendor or use a tool like Shield Analytics, which costs $20–50/month and includes pre-built rate-limit management.

    For Instagram, the Graph API is free but requires a Facebook Business account and app review. If you’re just scheduling posts, stick with a tool that’s already approved. If you’re building custom dashboards, expect to spend a week on the approval process and another week handling edge cases when the API changes.

    Most operators don’t need higher limits—they need smarter workflows. If you’re burning through read quotas, audit your Zaps or Make scenarios for redundant lookups, and switch to a weekly batch job. You’ll stay under free-tier caps and spend your time on content, not API accounting.

    One Two Three Send covers automation, APIs, and workflow design for solo operators every week. Subscribe here to get the next deep-dive in your inbox.

    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.

  • AI image generators bill per resolution tier—here’s the cost math

    AI image generators bill per resolution tier—here’s the cost math

    If you’re using AI image generators to create featured images, social assets, or course thumbnails, you’ve probably noticed the pricing isn’t flat. Most platforms charge based on resolution tiers—and the gap between standard and high-resolution output can be 3× to 5× per image.

    For solo operators generating dozens of visuals each week, that difference adds up fast. The trick is understanding what you’re actually paying for, and when the premium tier is wasted spend.

    How resolution-based pricing works

    Most AI image platforms structure pricing around three dimensions: the number of images generated, the resolution of the output, and whether you’re using a fast or slow generation queue.

    Midjourney, for example, offers standard and high-resolution upscaling. A standard 1024×1024 image costs one credit. Upscaling to 2048×2048 or higher costs additional credits—often two to four times the base rate. DALL·E 3 via OpenAI’s API charges $0.040 per standard 1024×1024 image, $0.080 for 1024×1792, and $0.120 for 1792×1024. Stability AI’s models follow a similar pattern: higher pixel counts mean higher API costs.

    The resolution tier you pick determines not just image quality, but also processing time, server load, and—in some cases—access to advanced features like inpainting or style transfer at full fidelity.

    When high resolution matters

    If you’re creating print assets, large hero images for landing pages, or anything that will be displayed at native resolution on high-DPI screens, the premium tier makes sense. A 2048×2048 image gives you flexibility to crop, zoom, or repurpose without visible artifacting.

    But most online-business use cases don’t need that headroom. Featured images for blog posts get compressed to under 200 KB for page speed. Social media platforms downsample uploads aggressively—Instagram compresses anything over 1080px wide, and Twitter re-encodes at 1200×675 for timeline cards. If your workflow ends with a resize and export at 80% JPEG quality, you’re paying for pixels you immediately discard.

    One operator I spoke with was generating 1792×1024 images in DALL·E for newsletter headers, then running them through TinyPNG and resizing to 600px wide. She was spending $0.080 per image when $0.040 would have produced an identical final asset. Over a month of daily emails, that’s an extra $1.20—small in isolation, but it scales with volume and points to a larger pattern of over-provisioning.

    The resolution-to-use-case map

    Here’s a practical breakdown by output type:

    • Social media thumbnails, Twitter cards, Facebook link previews: 1024×1024 or 1024×576 is enough. Platforms compress aggressively.
    • Blog featured images: 1200×630 covers Open Graph and gives you crop flexibility. Standard-tier generation works fine.
    • Email headers: 600–800px wide at 72 DPI. Standard resolution, then resize in post.
    • Course or product thumbnails: If displayed under 500px, standard resolution suffices. If users click to expand or you offer print options, go higher.
    • Paid ads (Meta, Google Display): Check the platform’s recommended specs. Most want 1200×628 or 1080×1080. Standard tier handles it.
    • Print, merchandise, or high-res downloads: This is where premium tiers pay off. Aim for 2048px minimum on the short edge.

    If you’re unsure, generate one image at standard resolution, run it through your normal export pipeline, and inspect the final file. If it looks sharp at the intended display size, you don’t need to pay more.

    Batch generation and cost control

    Most platforms let you queue multiple images in a single prompt or API call. If you’re producing a set of related visuals—say, five variations of a hero image for A/B testing—generate them all at standard resolution first, pick the winner, then upscale only that one if needed.

    Some tools, like Stability AI’s API, let you set resolution as a parameter. If you’re building a workflow in Make or Zapier that triggers image generation on new blog posts, hardcode the resolution to 1024×1024 unless the content type explicitly requires more. That prevents accidental overspend when someone forgets to set the dropdown.

    For high-volume operators, consider running a monthly audit: export your image generation logs, compare resolution tier usage to final published asset specs, and flag any mismatches. If you’re consistently generating 1792px images and publishing 800px, adjust your defaults.

    When to ignore the math

    If your brand aesthetic depends on ultra-sharp, large-format visuals—or if you regularly repurpose the same asset across print, web, and social—paying for high resolution up front can save time. Generating once at 2048px and resizing down for different channels is faster than re-generating multiple times.

    But for most solo operators, the reverse is true: generate at the resolution you’ll actually use, and upgrade selectively when the output demands it. The savings are small per image, but they compound across hundreds of assets and dozens of projects.

    What’s your default resolution when you generate images? Hit reply and let me know—I’m curious whether most operators overprovision or dial it in tight.

  • Analytics UTM parameters break when platforms strip them mid-click

    Analytics UTM parameters break when platforms strip them mid-click

    You set up UTM parameters on every campaign link. You check your analytics dashboard. Half your traffic shows up as direct or referral with no campaign data attached. The parameters vanished somewhere between the user’s click and your server log.

    This isn’t a tagging mistake or a broken implementation. Social platforms, email clients, mobile apps, and third-party proxies strip, modify, or cache UTM parameters before they ever reach your site. The problem is structural, and it costs solo operators accurate attribution data every single day.

    Where UTM parameters disappear

    Instagram and TikTok in-app browsers cache the destination URL on first load, then serve that cached version on subsequent taps. If a user sees your post, taps the link, backs out, then taps again five minutes later, the second visit hits your site without UTM parameters because the app pulled the page from cache instead of following the full URL.

    LinkedIn’s link wrapper rewrites URLs when you post. A link with ?utm_source=linkedin&utm_medium=social&utm_campaign=launch gets routed through linkedin.com/safety/go, and sometimes the parameters don’t survive the redirect chain. The same happens on Facebook when the platform flags your domain for review or routes traffic through its link-shim service.

    Email clients strip query parameters they consider tracking identifiers. Apple Mail Privacy Protection prefetches links in the background, and iOS Mail strips parameters on some mailto and http links when the user has tracking protection enabled. Outlook’s Safe Links feature rewrites URLs and occasionally drops trailing parameters during rewrite.

    Link shorteners add another failure point. Bitly, TinyURL, and custom short-domain services sometimes cache the redirect destination. If you update the long URL with new UTM parameters after the short link is created, users who clicked the old short link may land on a cached version with stale or missing parameters.

    What actually survives

    UTM parameters work reliably on direct web traffic and on platforms that don’t proxy or rewrite links. Twitter/X still passes them through cleanly most of the time. Reddit does, too, unless the subreddit uses AutoModerator rules that strip query strings. Email platforms like MailerLite and Postmark preserve UTM parameters in plain-text and HTML emails as long as the link isn’t flagged by the receiving client’s anti-tracking filter.

    Search ads and display ads pass parameters without issue because the ad platform has no incentive to strip your tracking data—they want you to measure performance so you keep spending. Google Ads, Microsoft Ads, and Meta Ads all respect UTM conventions.

    Direct links posted in Slack, Discord, SMS, and WhatsApp usually work, though WhatsApp’s link preview service occasionally caches the destination and serves a parameter-free version on repeat taps.

    Workarounds that don’t scale

    Some operators try to append a cache-buster parameter like &cb=1234567890 to force platforms to treat each link as unique. This helps with caching issues but increases the risk that aggressive filters flag the URL as a tracking link and strip the entire query string. You trade one failure mode for another.

    Others embed campaign identifiers in the URL path instead of query parameters: example.com/launch-linkedin instead of example.com?utm_campaign=launch. This survives stripping but requires server-side rewrite rules and breaks when you run multiple campaigns to the same landing page. You end up creating dozens of duplicate URLs or writing complex routing logic.

    A third approach is to use first-party cookies or localStorage to persist campaign data after the first visit, then attribute conversions retroactively. This works only if the user converts on a return visit and only if your analytics tool supports cross-session attribution. Most don’t.

    What actually works

    Accept that UTM parameters will fail on 15–30% of clicks from social and email traffic. Plan your attribution model around that reality instead of trying to patch every leak.

    Use a dedicated link-management tool like Rebrandly or a self-hosted solution that logs the original UTM parameters server-side when the short link is created, then appends them to the destination URL at redirect time. This won’t fix client-side caching, but it ensures your tracking data is attached to the redirect, not just the short link.

    For high-value campaigns, create unique landing pages with campaign identifiers baked into the URL path. Reserve this for product launches, partnerships, and sponsored placements where you need accurate attribution and can afford the operational overhead.

    Track referrer headers alongside UTM parameters. When parameters are missing, your analytics tool can fall back to the HTTP referer header to identify the source platform. This won’t give you campaign-level detail, but it’s better than marking everything as direct traffic.

    Run a monthly audit of your top traffic sources in Google Analytics, Plausible, or Fathom. Filter for sessions with no UTM parameters and a known referrer domain. If you see significant traffic from l.instagram.com, lm.facebook.com, or t.co tagged as direct, you know where your parameters are breaking.

    Want to dig deeper into analytics setup and attribution models for solo operators? Reply with the tracking issue you’re trying to solve—we’ll cover it in a future issue.

  • ConvertKit custom fields bloat subscriber profiles—when to use tags instead

    ConvertKit gives you two ways to track information about subscribers: custom fields and tags. Most operators pick one by habit or intuition, then run into performance problems, automation failures, or segmentation nightmares six months later.

    The difference isn’t just semantic. Custom fields and tags work differently under the hood, cost differently at scale, and break in different ways when you push them too hard.

    What custom fields actually store

    Custom fields hold variable data—strings, numbers, dates—unique to each subscriber. Think first name, referral source, purchase count, or renewal date.

    ConvertKit stores these as key-value pairs in your subscriber record. You can reference them in emails with liquid syntax ({{ first_name }}), filter segments by their values, and update them via API or form submission.

    The gotcha: every custom field you create adds a column to every subscriber record, whether populated or not. An empty field still exists in the database. Add twenty fields and you’re carrying twenty columns per contact, even if nineteen are blank.

    This doesn’t break anything immediately. But it slows segment queries, complicates exports, and makes your data harder to audit. I’ve seen accounts with forty-plus custom fields where only six held useful information. The rest were legacy experiments or half-finished automations.

    What tags actually track

    Tags are binary flags. A subscriber either has a tag or doesn’t. You can’t store a value in a tag—just presence or absence.

    ConvertKit indexes tags separately from subscriber records. Adding a tag doesn’t expand the subscriber table. It creates a relationship record in a join table. That architecture scales better when you’re tracking dozens of attributes.

    Tags also surface better in the UI. You can see all applied tags at a glance in the subscriber list. Custom field values require opening each record or exporting a CSV. For quick visual checks—who’s in the beta cohort, who opted into coaching—tags win.

    The downside: tags can’t hold nuance. If you need to store when someone joined the beta or which tier they purchased, a tag won’t cut it. You’ll end up creating beta_joined_2026_01, beta_joined_2026_02, and so on—tag sprawl that’s worse than a single date field.

    When to use custom fields

    Reach for custom fields when you need to store or reference a specific value:

    • Personalization tokens in email copy (first name, company, city)
    • Numeric counters that increment (emails opened this month, courses completed)
    • Dates for time-based logic (trial start date, last purchase, renewal window)
    • External IDs for syncing with other tools (Stripe customer ID, WordPress user ID)

    Custom fields also make sense when you need to filter segments by ranges or partial matches. “Purchase count greater than 3” or “City contains ‘New’” requires field-based logic. Tags can’t do that.

    When to use tags

    Use tags for binary states, audience segments, and behavioral flags:

    • Lifecycle stages (subscriber, buyer, churned, reactivated)
    • Interest categories (AI tools, WordPress, monetization)
    • Engagement tiers (active, dormant, cold)
    • Cohort membership (joined Q1 2026, beta tester, workshop attendee)

    Tags scale better when you’re tracking many attributes that most subscribers won’t have. If only 8% of your list are buyers, a buyer tag is leaner than a is_buyer custom field sitting empty on 92% of records.

    Tags also play nicer with automation branching. ConvertKit’s visual automations let you split paths based on tag presence with a single click. Doing the same with custom field values requires more setup and is harder to debug when it breaks.

    The hidden cost of choosing wrong

    I’ve worked with a SaaS newsletter operator who used custom fields for everything—including audience interests. They had fields like interested_in_AI, interested_in_SEO, and interested_in_monetization, each storing “yes” or blank.

    Segmenting required filtering by six different field conditions. Automations couldn’t branch cleanly. Exports included twelve columns of “yes” and empty cells. Switching to tags cut segment load time from four seconds to under one and made the automation map readable again.

    Conversely, I’ve seen operators try to use tags for dates. They’d create trial_started_2026_06_15, then realize a week later they couldn’t query “trial started more than 7 days ago” without manually adding 365 tags. A single trial_start_date custom field solved it.

    One non-obvious tip

    You can combine both. Use a custom field to store the precise value and a tag to mark the category.

    Example: store last_purchase_date as a custom field (for time-based logic and reference), then apply a buyer tag (for quick filtering and automation branching). The field gives you precision; the tag gives you speed.

    This hybrid approach works especially well for high-cardinality data—attributes that can take many values but where you still want fast segment access. Referral source is another good candidate: store the exact UTM in a field, apply a referral_traffic tag.

    Before you create your next custom field or tag, ask: am I storing a value I need to reference, or am I marking a state I need to check? The answer tells you which to use.

    Got a ConvertKit setup question? Reply to this email—I read every one, and reader questions become future articles.

  • Zapier vs. Make vs. n8n: which automation platform to pick

    Zapier vs. Make vs. n8n: which automation platform to pick

    Workflow automation tools promise to glue your stack together—newsletter platform to CRM, form submissions to Slack, payment webhooks to spreadsheets. But the three most popular platforms—Zapier, Make, and n8n—differ sharply in pricing model, learning curve, and where they break under load.

    Here’s how to pick the right one for your online business, based on technical comfort and workflow complexity.

    Zapier: the fastest onramp, highest per-task cost

    Zapier is the incumbent. Its library covers 6,000+ apps, the UI is beginner-friendly, and most workflows take under ten minutes to build. You connect two apps, pick a trigger, map fields, and hit publish.

    Pricing starts at $19.99/month for 750 tasks. A “task” is any action—sending an email, creating a row, posting to Slack. Multi-step Zaps consume one task per step. If you’re running a weekly digest that pulls 50 rows from Airtable, formats them, and sends via Postmark, that’s 100 tasks per send (50 read actions + 50 format actions). At moderate volume, you’ll hit the 750-task ceiling fast.

    The $49/month tier gives you 2,000 tasks and multi-step Zaps. The $69 tier unlocks 5,000 tasks and premium apps like Salesforce. After that, overage is $0.01–0.03 per task depending on plan.

    Zapier is best for: solo operators who want speed over cost efficiency, workflows under 1,000 tasks/month, and teams with zero dev resources.

    Where it breaks: Complex conditional logic requires nested filters that eat tasks. Error handling is limited—if step three fails, the whole Zap dies unless you add error-catching steps (more tasks). Debugging multi-step Zaps means clicking through each execution log manually.

    Make: visual logic, cheaper at scale

    Make (formerly Integromat) uses a node-based canvas. You drag modules onto a board, draw connections, and configure routers, filters, and loops. It’s more visual than Zapier but steeper to learn.

    The key difference: Make charges by operations, not tasks, and counts more efficiently. A single HTTP request that returns 50 records and pipes them through a filter counts as two operations (fetch + filter), not 50. For bulk workflows, this cuts cost dramatically.

    Pricing starts at $9/month for 10,000 operations. The $16 tier gives you 40,000. Even the free tier includes 1,000 operations—enough to test real workflows. Make also includes built-in error handling, rollback, and scenario versioning at every tier.

    Make is best for: operators comfortable with flowcharts, workflows that process batches (RSS to social, CSV imports, webhook queues), and anyone hitting Zapier’s task ceiling.

    Where it breaks: The UI intimidates non-technical users. App connectors are less polished than Zapier’s—some require manual API setup. The execution log is powerful but dense; reading it requires understanding JSON structure.

    n8n: self-hosted, unlimited operations, steepest learning curve

    n8n is open-source. You host it yourself (DigitalOcean, AWS, or your own VPS) or pay for n8n Cloud. The workflows look similar to Make—nodes, connections, conditional branches—but you control the infrastructure and pay nothing per operation.

    Self-hosting costs $5–20/month depending on server size. n8n Cloud starts at $20/month for 2,500 executions (an execution is one full workflow run, regardless of steps). The free Cloud tier allows 50 executions/month.

    n8n’s power is in flexibility. You can write JavaScript inside nodes, call custom APIs, manipulate data inline, and trigger workflows via webhook, cron, or manual button. It’s the only platform where you can run a workflow locally, version-control it in Git, and deploy via CI/CD.

    n8n is best for: technical founders, dev-friendly teams, workflows that need custom code, and businesses running 10,000+ operations/month where per-task pricing becomes prohibitive.

    Where it breaks: You’re responsible for uptime, backups, and security. The app connector library is smaller (400+ apps vs. Zapier’s 6,000). Some integrations require OAuth app setup or API keys that aren’t documented. If you’re non-technical and your server goes down at 2am, you’re stuck.

    How to decide

    Start with your monthly operation count and technical comfort.

    If you’re running under 1,000 tasks/month and want zero friction, use Zapier. If you’re processing batches—pulling 200 RSS items daily, importing CSVs, or handling webhook queues—switch to Make before you hit Zapier’s $69 tier. If you’re technical, running 10,000+ operations, or need version control and custom logic, self-host n8n.

    One non-obvious move: use Zapier for the first 90 days to validate workflows, then migrate the high-volume ones to Make or n8n once you know they’re permanent. Rebuilding a workflow in Make takes 20–40 minutes if you understand the original logic.

    Most solo operators land on Make. It’s the sweet spot between Zapier’s ease and n8n’s control, and the $16/month tier covers 95% of real-world use cases.

    Have a workflow question? Reply to this email—we read every one and answer the best questions in future issues. Subscribe here if someone forwarded this to you.