Author: onetwothreeadmin

  • SEO meta keywords tag: why platforms still offer it in 2026

    SEO meta keywords tag: why platforms still offer it in 2026

    SEO meta keywords tag: why platforms still offer it in 2026
    Photo by Sumaid pal Singh Bakshi on Unsplash

    Open any SEO plugin settings panel in WordPress, Shopify, or Wix in 2026 and you’ll likely find a field for meta keywords. Right there alongside meta title and description, asking you to fill it in. Some plugins even count it as part of your “SEO completion score.”

    Google hasn’t used the meta keywords tag for ranking since 2009. Bing confirmed they ignore it too. Yandex stopped weighting it years ago. No major search engine reads this field for ranking purposes, yet it persists across platforms, themes, and paid SEO tools as if it still matters.

    So why does everyone keep building support for a useless tag?

    The legacy code problem

    Most SEO plugins and themes inherit their feature sets from older codebases. When Yoast SEO, Rank Math, and All in One SEO launched their first versions in the 2000s and early 2010s, meta keywords were standard practice. Even though Google had already deprecated the tag by then, other engines hadn’t yet followed suit, and SEO guides still recommended it.

    Removing a field from a popular plugin or theme creates support overhead. Users who learned SEO in 2008 expect to see it. Removing it triggers “Where did meta keywords go?” support tickets and one-star reviews claiming the tool is “incomplete.” For plugin maintainers, it’s easier to leave a harmless field in place than to explain its removal to thousands of users.

    The field costs almost nothing to maintain. It’s a single input box in a settings panel and one line of HTML in the document head. There’s no performance penalty, no indexing cost, and no user-facing consequence. From a product maintenance perspective, it’s safer to leave it there.

    The illusion of completeness

    SEO tools love progress bars and completion scores. “Your page is 73% optimized—add meta keywords to reach 100%.” It’s gamification that makes users feel productive, even when the action has no search impact.

    Meta keywords fill out that checklist. Removing the field means recalibrating the scoring system, rewriting help documentation, and fielding complaints from users who see their “SEO score” drop. Keeping it maintains the illusion that every field matters equally.

    Some tools go further and auto-generate meta keywords by pulling phrases from your content. This creates the appearance of intelligent optimization while doing nothing for ranking. But it feels thorough, and for SaaS products competing on feature count, that perception drives retention.

    When meta keywords actually cause problems

    In most cases, the meta keywords tag is harmless clutter. Search engines ignore it, and users waste a few minutes filling it out. But there are two scenarios where it creates real issues.

    First: keyword leakage. If you’ve spent time researching low-competition long-tail keywords, listing them in a public meta tag gives competitors a free audit of your targeting strategy. Any scraper or SEO spy tool can pull your meta keywords and see exactly what you’re going after. It’s rare that this matters, but for operators in competitive niches running narrow content strategies, it’s an unforced error.

    Second: misallocated effort. Solo operators and small teams have limited time for SEO work. If your dashboard or plugin tells you to fill in meta keywords, and you spend 15 minutes per post doing it, that’s 15 minutes you didn’t spend improving title tags, meta descriptions, header structure, internal linking, or actual content quality—all of which do affect ranking and click-through rate.

    The opportunity cost isn’t huge on a per-post basis, but across hundreds of posts and multiple team members, it adds up. And because the field sits alongside legitimately useful settings, newer operators often assume it carries equal weight.

    What to do about it

    If your SEO plugin or theme includes a meta keywords field, ignore it. Don’t fill it in, don’t auto-generate it, don’t let it drag down your completion score. If the tool penalizes you for leaving it blank, that’s a signal the scoring system isn’t aligned with actual search engine behavior.

    If you’re choosing between SEO tools and one prominently features meta keywords as a selling point or optimization target, that’s a red flag. It suggests the tool’s feature set hasn’t been updated to reflect how search engines actually work in 2026.

    And if you’re building or customizing a WordPress theme, Shopify app, or site builder: consider removing the field entirely. Yes, you’ll get support questions. But you’ll also signal to users that your tool reflects current SEO practice, not outdated checklists. Include a help doc explaining why it’s gone, link to Google’s 2009 announcement, and move on.

    The meta keywords tag isn’t hurting anyone by existing. But its persistence across platforms is a reminder that just because a tool offers a setting doesn’t mean that setting does anything. And in a space where operators are already drowning in conflicting SEO advice, every fake lever is one more thing to ignore.

    Got a question about SEO settings, platform features, or tool choices? Reply to this email—we cover one reader question every Sunday.

  • WordPress CDN purge delays: what ‘instant’ invalidation really means

    WordPress CDN purge delays: what ‘instant’ invalidation really means

    WordPress CDN purge delays: what 'instant' invalidation really means
    Photo by Erik Mclean on Unsplash

    You push a WordPress post update, hit the CDN purge button, and refresh the page. The old version still loads. You check the dashboard—it says the cache cleared thirty seconds ago. So why is your reader in Singapore still seeing yesterday’s headline?

    CDN cache invalidation isn’t instant, even when providers claim sub-second purge times. What they measure and what you experience are two different things.

    What “purge time” actually measures

    When Cloudflare or BunnyCDN reports a three-second purge, they’re measuring how long it takes their control plane to mark the cached object as stale across their network. That’s an internal API call, not the moment your updated content becomes visible globally.

    Propagation delay comes from three sources:

    • Edge node sync latency: The purge command reaches regional POPs (points of presence) at different times. A node in Frankfurt might invalidate in two seconds; one in Sydney might take twelve.
    • In-flight requests: If a user’s browser already fetched the HTML but is still loading CSS and images, those assets may serve from cache even after purge completes.
    • Browser cache headers: If your origin sent Cache-Control: max-age=3600, the browser won’t even ask the CDN for an hour, regardless of purge status.

    Most CDN dashboards report control-plane purge time, not edge-node propagation time. The difference can be thirty seconds to two minutes under normal conditions.

    How WordPress caching plugins trigger purges

    Plugins like WP Rocket, W3 Total Cache, and LiteSpeed Cache hook into WordPress’s clean_post_cache action. When you update a post, they fire a purge request to your CDN’s API.

    But the plugin only knows the purge request sent—it doesn’t wait for confirmation that all edge nodes updated. If your CDN API returns a 200 status after queuing the purge, the plugin considers it done.

    This creates a false positive: the plugin shows a success message, but the purge is still propagating. If you immediately test the live URL, you might hit an edge node that hasn’t received the invalidation yet.

    Some CDNs—Cloudflare included—offer a “purge everything” option that’s faster than purging individual URLs. It’s a sledgehammer, but for time-sensitive updates (like fixing a pricing error in a sales post), it’s often the safer choice.

    Testing purge propagation across regions

    Don’t trust the dashboard timestamp. Test actual propagation using a multi-region tool or curl from different locations.

    Here’s a quick manual test using curl and a timestamp query string:

    curl -I "https://yoursite.com/post-slug/?t=$(date +%s)"

    Check the CF-Cache-Status (Cloudflare), X-Cache (BunnyCDN), or equivalent header. If it says HIT, that edge node is still serving cached content. Run this from multiple geographic locations—services like Pingdom or GTmetrix can help—to see how long full propagation actually takes.

    In practice, expect sixty to ninety seconds for global propagation on most CDNs, even when the dashboard says “purged” after five seconds.

    When purge delays break workflows

    Two scenarios make this particularly painful:

    Newsletter send timing: You schedule a post to publish at 9:00 AM, then queue a newsletter linking to it at 9:05 AM. If the CDN hasn’t fully propagated, early openers see a 404 or stale content. Add a ten-minute buffer, or use a staging URL in the newsletter and redirect after confirming the purge.

    Sponsored content updates: A sponsor asks you to correct a product name or price. You make the edit, purge, and send them the updated link. They check it thirty seconds later and still see the error. Now you’re explaining CDN propagation on a support call.

    For critical updates, use a purge-and-verify workflow: trigger the purge, wait two minutes, then test the live URL from at least two geographic regions before confirming the change is live.

    Want more infrastructure deep-dives like this? Subscribe to One Two Three Send for weekly breakdowns of the tools and systems that actually run online businesses—no fluff, just what works and what breaks.

    Reducing purge delays

    You can’t eliminate propagation time, but you can reduce uncertainty:

    • Set shorter max-age values on high-churn content (e.g., homepage, latest posts). Sixty seconds instead of 3600 means users re-check the CDN more frequently.
    • Use versioned asset URLs (e.g., style.css?v=1.2.3) instead of purging CSS and JS. WordPress plugins like WP Rocket do this automatically.
    • Enable stale-while-revalidate headers (Cache-Control: max-age=60, stale-while-revalidate=300) so the CDN serves stale content while fetching a fresh copy in the background.

    And if you’re on a budget host with inconsistent purge APIs, consider switching to a provider with better WordPress integration. BigScoots, for example, includes enterprise CDN tooling even on shared plans, with purge confirmation hooks that actually report edge-node status.

    The next time your CDN dashboard says “purged,” give it two minutes and a multi-region test before you trust it. Instant invalidation is a marketing term, not a technical reality.

  • Newsletter referral programs reward shares, not subscribers—here’s why

    Newsletter referral programs reward shares, not subscribers—here’s why

    Newsletter referral programs reward shares, not subscribers—here's why
    Photo by Team Nocoloco on Unsplash

    Newsletter referral programs look simple: existing subscriber shares your newsletter, someone new signs up through their link, original subscriber gets credit. But the mechanics under the hood reveal a design choice that changes how you should think about rewards, milestones, and fraud prevention.

    Most platforms—including Beehiiv, Sparkloop, and UpViral—credit the referrer at the moment of the share action or initial click, not when the new subscriber confirms their email or becomes active. That’s not an oversight. It’s a deliberate trade-off between attribution accuracy and user experience.

    Why platforms credit the share, not the conversion

    If a platform waits to credit the referrer until the new subscriber confirms their email, you introduce a 24-to-48-hour delay before the referrer sees any progress toward their reward. That delay kills momentum. People share once, see no movement on their dashboard, and assume the system isn’t working.

    Crediting the share immediately gives the referrer instant feedback. They see their count tick up within seconds. That dopamine hit encourages them to share again.

    The downside: you’re counting referrals that never convert. If someone shares your newsletter and ten people click but only three confirm their email addresses, the referrer gets credit for ten. Your dashboard shows ten referred subscribers, but your email list only grows by three.

    Beehiiv‘s referral system, for example, increments the referrer’s count when someone lands on the signup page via their unique link and submits an email address—before double opt-in confirmation. If that person never clicks the confirmation email, Beehiiv doesn’t automatically deduct the referral credit. You’re left with inflated referral counts and a smaller list than your referral leaderboard suggests.

    What this means for milestone design

    If you’re running a referral program with tiered rewards—get five referrals, unlock a PDF; get 25, get a one-on-one call—you need to account for the gap between credited referrals and confirmed subscribers.

    A conservative multiplier: assume 60-70% of credited referrals will actually confirm and stay active. If you want someone to genuinely deliver 25 new subscribers to your list, set the milestone at 35-40 credited referrals. That’s not padding—it’s compensating for the way the system counts.

    Some operators do the opposite. They set lower thresholds and accept that referral credits overstate real growth. The logic: referral programs are about engagement and word-of-mouth momentum, not precise list-building math. If someone shares your work enough to rack up 40 credited referrals, they’ve done the work even if only 25 people actually joined.

    Both approaches work. The mistake is setting a milestone at, say, ten referrals, expecting ten confirmed subscribers, and then feeling cheated when your list only grows by six.

    Fraud and gaming the system

    Because platforms credit the share action, referral programs are vulnerable to bulk fake signups. Someone creates a dozen disposable email addresses, uses their own referral link, submits all twelve emails, and immediately gets credit for twelve referrals—even though none of those addresses will ever open an email.

    Most platforms have basic fraud detection: they flag referrals from the same IP address, block known disposable email domains, and penalize accounts that rack up referrals with zero engagement. But enforcement is reactive, not preventive. If someone wants to game your referral leaderboard, they can do it for at least a few days before the system catches up.

    The operator-side fix: build engagement thresholds into your rewards. Don’t just offer a reward at ten referrals—require that at least seven of those ten people open an email in the first 30 days. Sparkloop and some custom-built systems let you set that kind of conditional unlock. Beehiiv doesn’t natively support engagement-based milestones, so you’d need to manually audit your leaderboard before delivering high-value rewards like coaching calls or physical products.

    When conversion-based crediting makes sense

    A few platforms—mostly custom-built referral systems using tools like Rewardful or ReferralCandy—let you choose when to credit the referrer. You can configure the system to wait until the new subscriber confirms their email, opens their first email, or even makes a purchase (if you’re running a paid newsletter).

    That approach eliminates inflated counts, but it introduces the delay problem. If your audience is sophisticated enough to understand that referral credit takes 24-48 hours to appear, it works. If your audience skews toward casual readers who expect instant feedback, the delay will depress sharing behavior.

    One middle path: credit the share immediately, but display two numbers on the referrer’s dashboard—”total shares” and “confirmed subscribers.” Transparency costs you nothing, and it sets expectations. If someone sees they’ve sent 15 people to your signup page but only 9 confirmed, they understand the gap without feeling penalized.

    Most newsletter platforms don’t offer that dual display by default. You’d need to build it yourself or accept that your referral dashboard is a directional indicator, not a precise accounting tool.

    What to do now

    If you’re running a referral program, audit your current milestone structure. Check the gap between credited referrals and actual confirmed subscribers over the last 30 days. If the gap is more than 20%, adjust your milestones upward or add engagement requirements before delivering rewards.

    If you’re designing a new referral program, decide whether you’re optimizing for momentum (credit the share) or accuracy (credit the conversion). Most solo operators should optimize for momentum. Referral programs live or die on early enthusiasm, and nothing kills enthusiasm faster than a dashboard that doesn’t move.

    Got a referral program question? Reply to this email—I read every response.

    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.

  • Zapier filters fail silently when field types mismatch

    Zapier filters fail silently when field types mismatch

    Zapier filters fail silently when field types mismatch
    Photo by Annie Spratt on Unsplash

    Zapier filters are supposed to stop workflows when conditions aren’t met. But when the data type coming from your trigger doesn’t match the filter’s expected type, the filter step often passes data through instead of stopping it—and you won’t see a warning.

    This happens most often when API responses return numbers as strings, or when form fields send boolean values as text. Your filter logic looks correct in the Zapier editor, but the workflow keeps running when it shouldn’t.

    Why type mismatches break filter logic

    Zapier’s filter conditions use different comparison rules depending on the data type. When you set a filter to check if a number is “greater than 100,” Zapier expects an integer or float. If the incoming field is a string—even if it contains the characters “150”—the comparison often evaluates as true regardless of the actual value.

    The most common culprits:

    • Webhook payloads that wrap numbers in quotes
    • Airtable formula fields returning text instead of numbers
    • Google Sheets cells formatted as text
    • Form builders that send checkbox values as “true” or “false” strings instead of booleans

    Zapier doesn’t flag these mismatches in the test step. The filter appears to work, because the test data happens to pass. But in production, edge cases slip through.

    How to test for type problems

    The fastest way to confirm a type issue: add a temporary Formatter step immediately before your filter. Use Numbers > Format Number or Text > Default Value to force the field into the type you expect. If your filter suddenly starts behaving correctly, you’ve found the mismatch.

    You can also check the raw data in Zapier’s task history. Open a completed task, expand the trigger step, and look at the field in question. If you see quote marks around a number—"42" instead of 42—it’s a string. Zapier won’t coerce it during comparison.

    For boolean checks, the problem is worse. A field containing the string “false” evaluates as true in existence checks, because non-empty strings are truthy. Your filter checking “if subscription_active exists” will pass even when the value is explicitly “false.”

    Three fixes that actually work

    Option one: Add a Formatter step before every filter that touches numbers or booleans. Use Numbers > Spreadsheet-Style Formula with a formula like VALUE(input) to convert strings to numbers, or Text > Length combined with a secondary filter to check boolean strings explicitly.

    Option two: Fix the data at the source. If you control the webhook or API, return proper JSON types. If you’re using Airtable, switch formula fields to rollup or lookup fields that preserve number types. In Google Sheets, use TO_PURE_NUMBER() in a hidden helper column.

    Option three: Use Zapier’s “Text Contains” or “Text Exactly Matches” conditions instead of numeric comparisons—but only if you’re comparing against a small set of known values. This works for status fields (“active,” “paused,” “cancelled”) but breaks down for ranges.

    The Formatter approach costs an extra task per workflow run. At $0.01–0.03 per task depending on your plan, that adds up—but it’s cheaper than the support emails from users who slipped through a broken filter.

    When to rewrite the workflow entirely

    If you’re chaining three or more filter steps to work around type issues, you’re better off moving the logic upstream. Platforms like Make (formerly Integromat) handle type coercion more gracefully, and their routers let you branch on complex conditions without stacking fragile filters.

    For high-volume workflows—anything over 10,000 tasks per month—the cumulative cost of workaround Formatter steps often exceeds the price difference between Zapier and a code-optional alternative. Make’s operations are roughly 30% cheaper per unit, and n8n self-hosted is effectively free at scale if you’re comfortable with Docker.

    Zapier’s filter UX is clean and approachable, but it’s built for the 80% case. When your data sources don’t play along, you’re either adding duct-tape steps or migrating.

    Have a workflow automation question? Reply to this email—we’re building a library of operator-tested fixes for the automation gaps platforms don’t document.

  • Subscription churn dashboards: what they hide about your real retention

    Subscription churn dashboards: what they hide about your real retention

    Subscription churn dashboards: what they hide about your real retention
    Photo by prashant hiremath on Unsplash

    Open your subscription dashboard—Stripe, Patreon, Memberful, whatever you use—and look at the churn number. Maybe it’s 4.2%. Maybe 8%. That single percentage is supposed to tell you how many paying members you’re losing each month.

    It doesn’t. Not really. Because that number conflates voluntary cancellations, failed payment retries, paused subscriptions that never resume, and members who downgrade to free. Each of those behaviors means something different, and grouping them into one metric makes it nearly impossible to fix the actual problem.

    Here’s what most churn dashboards don’t show you—and how to calculate it yourself.

    Voluntary vs. involuntary churn

    Most platforms lump these together. A member who clicks “Cancel subscription” counts the same as someone whose credit card expired and failed after three retry attempts.

    The fix rate for those two scenarios is radically different. Voluntary churn requires rethinking your content, pricing, or onboarding. Involuntary churn—also called passive churn—is often fixable with better dunning emails, payment method update prompts, or switching to a payment processor that retries smarter.

    Stripe reports this breakdown in the Billing dashboard under “Revenue churn” if you dig into the details tab. Memberful and Patreon don’t surface it by default. If your platform doesn’t split these, export your cancellation data and tag each one manually by looking at the cancellation reason field. Stripe’s API returns cancellation_details.reason as either cancellation_requested or payment_failed.

    In a 2025 Stripe data sample across SaaS and membership businesses, involuntary churn accounted for 20–40% of total monthly churn. That’s revenue you can recover without changing your product.

    Paused subscriptions that don’t resume

    Some platforms let members pause instead of cancel. Memberful, Patreon, and Stripe Billing all support this. The idea: give people a break, and they’ll come back.

    Except most don’t. In practice, pause behavior clusters into two groups: people who resume within 30 days, and people who never resume. If someone pauses for more than 60 days, the likelihood they reactivate drops below 12%, based on Memberful’s 2024 creator survey data.

    But paused accounts don’t show up in your churn number. They sit in limbo. Your dashboard still counts them as “members,” even though they’re not paying and statistically won’t return. This inflates your retention rate and makes your churn look better than it is.

    Fix: track “effective churn” by counting any pause longer than 60 days as a cancellation. Export your active and paused subscriber lists monthly, tag pauses by start date, and flag anyone past the 60-day mark. Add that to your churn count.

    Downgrades to free tiers

    If you offer a free tier, a member who downgrades from paid to free isn’t technically churned—they’re still subscribed. But they’re no longer paying you. Revenue-wise, they’re gone.

    Stripe and Memberful count this as “downgrade,” not churn. Patreon counts it as churn only if the member drops to $0. Beehiiv‘s dashboard treats it as a “tier change.” The taxonomy varies, and that inconsistency makes cross-platform comparison nearly impossible.

    What matters: are you tracking revenue churn (loss of MRR) or logo churn (loss of accounts)? Most solo operators look at logo churn because it’s the default number. But if you have multiple tiers, revenue churn is the better signal. A $10/month member canceling hurts less than a $100/month member downgrading to $10.

    Stripe’s MRR movement report breaks this down. For other platforms, calculate it manually: take last month’s MRR, subtract this month’s MRR from the same cohort, divide by last month’s MRR. That’s your revenue churn rate.

    Cohort decay vs. headline churn

    Your dashboard’s churn percentage is almost always a cross-sectional average: total cancellations this month divided by total active subscribers. That number is useful for month-over-month tracking, but it doesn’t tell you if your problem is early churn (people leaving in month one) or late churn (long-time members leaving after a year).

    Cohort retention does. Group members by signup month, then track what percentage of each cohort is still paying after 1 month, 3 months, 6 months, 12 months. If your month-one retention is 70% but month-twelve retention is 85%, your onboarding is the problem, not your long-term content. If month-one retention is 90% but month-twelve is 50%, you have a content staleness issue.

    Stripe’s cohort analysis tool shows this under Billing > Reports > Retention analysis. For platforms without native cohort tracking, export your subscriber data with signup dates and payment statuses, then pivot it in a spreadsheet by cohort month.

    What to do with this

    If you’re running a paid membership or subscription product, calculate these four numbers separately every month:

    • Voluntary churn rate
    • Involuntary churn rate
    • Pauses older than 60 days (as a percentage of total members)
    • Revenue churn rate (if you have multiple tiers)

    Then track month-one and month-twelve retention for at least three cohorts. That gives you six data points instead of one, and each one points to a different fix.

    Most subscription dashboards want to show you a single number because it’s easier to design and easier to sell. But one number can’t tell you why people leave, and you can’t fix churn if you don’t know which kind you have.

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

    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 transcription tools: when per-minute pricing beats subscription

    AI transcription tools: when per-minute pricing beats subscription

    AI transcription tools: when per-minute pricing beats subscription
    Photo by Sasun Bughdaryan on Unsplash

    Most solo operators pick a transcription tool the same way they pick a newsletter platform: they sign up for a monthly subscription and hope they use it enough to justify the cost. But transcription pricing works differently—and if you’re not processing hours of audio every week, you’re probably overpaying.

    The breakeven point between pay-per-minute and subscription models sits around 300 minutes per month for most tools. Below that threshold, usage-based pricing almost always costs less. Above it, subscriptions start to make sense. Here’s how to figure out which model fits your workflow.

    How per-minute pricing actually works

    Services like AssemblyAI, Deepgram, and Gladia charge between $0.00025 and $0.0015 per second of audio—roughly $0.015 to $0.09 per minute. You upload a file, get charged for the exact runtime, and walk away. No monthly commitment, no unused quota rolling over.

    If you’re transcribing two podcast episodes a month at 45 minutes each, that’s 90 minutes total. At $0.05 per minute (a typical API rate), you’re spending $4.50. Compare that to Descript’s $12/month starter plan or Otter.ai’s $16.99/month Pro tier, and the math is straightforward.

    The catch: per-minute tools are almost always API-first. You’re not logging into a dashboard and dragging files into a browser window. You’re either sending HTTP requests directly or using a lightweight wrapper tool. That’s fine if you’re comfortable with Postman or a basic Python script, but it’s friction if you just want to upload and download.

    When subscriptions stop being a waste

    Monthly plans make sense when you’re consistently crossing 300–400 minutes. Descript’s $24/month Creator plan includes 10 hours of transcription (600 minutes). If you’re using 500+ minutes, you’re paying $0.048 per minute—cheaper than most pay-as-you-go rates.

    But subscription value isn’t just about volume. You’re also paying for the interface, collaboration features, and integrated editing. Descript lets you edit transcripts like a text document and exports video with cuts applied. Otter.ai syncs with Zoom and auto-titles meeting transcripts. Those features have value if you use them. If you don’t, you’re subsidising someone else’s workflow.

    The real waste happens when you pay for a subscription tier you don’t fully use. Otter.ai’s Business plan is $30/user/month and includes 6,000 minutes per user per month. Unless you’re transcribing 100 hours of audio, you’re paying for capacity you’ll never touch.

    Hybrid setups that actually work

    Most operators don’t need to pick one model forever. If you’re running a podcast, you might process 200 minutes a month during regular seasons and 800 minutes during a launch sprint. Paying per-minute during off-months and subscribing for two months during launches saves more than either model alone.

    Another option: use a pay-per-minute API for bulk transcription and a free-tier tool for ad hoc work. Otter.ai’s free plan includes 300 minutes per month. If you’re transcribing client calls or quick voice memos, that’s enough to avoid paying anything. Save the API budget for long-form content.

    One non-obvious trick: batch your transcription requests if you’re using a per-minute service. Most APIs charge the same rate whether you send one 60-minute file or twelve 5-minute files, but some tools (like Deepgram) offer volume discounts that kick in at 100+ hours per month. If you’re close to that threshold, consolidating requests in a single billing cycle can drop your per-minute rate by 20–30%.

    What to check before you switch

    Pricing models aren’t the only variable. Accuracy, turnaround time, and language support all matter—and they vary widely even among tools charging similar rates.

    AssemblyAI’s word error rate is around 5% for clean English audio, but climbs to 12–15% with heavy accents or background noise. Deepgram handles noisy audio better but costs slightly more. If you’re transcribing Zoom calls with multiple speakers and mediocre microphones, paying an extra $0.02 per minute for better accuracy saves you more time than the cost difference.

    Turnaround time also fluctuates. Most API-based tools process audio at 10–15x real-time speed (a 30-minute file takes 2–3 minutes). Subscription tools with built-in editors can be slower, especially during peak hours. If you need transcripts immediately after recording, per-minute APIs usually win.

    Finally, check how each tool handles speaker identification and formatting. Some charge extra for diarisation (labelling who said what). Others include it by default but cap the number of speakers. If you’re transcribing panel discussions or group calls, that feature isn’t optional—and the upcharge can flip the breakeven math.

    If you’re transcribing under 300 minutes a month, start with a pay-per-minute API and a free-tier tool for overflow. Track your usage for two months. If you’re consistently crossing 400 minutes, switch to a subscription. If you’re hovering around 200, stay on usage-based pricing and pocket the difference.

    Have a transcription workflow that doesn’t fit these models? Reply and let us know—we’ll cover edge cases in a future piece.

  • Sponsored content disclosure placement: where platforms require it

    Sponsored content disclosure placement: where platforms require it

    Sponsored content disclosure placement: where platforms require it
    Photo by Szabo Viktor on Unsplash

    If you run sponsored posts, you already know you need to disclose the relationship. What most operators don’t realize is that each platform has its own technical requirements for where that disclosure appears—and some of them override what you write.

    Instagram and Facebook: the built-in toggle wins

    Meta’s platforms require you to use the “Paid partnership with” label when posting branded content through a business or creator account. You enable it in the advanced settings before publishing.

    If you add #ad or #sponsored in the caption, that’s fine—but it doesn’t replace the toggle. Meta’s terms treat the built-in label as the compliant disclosure. The hashtag is supplementary.

    The label appears at the top of the post, above the image and caption. It’s styled in Meta’s UI and can’t be customized. If you’re working with a brand that wants specific language, send them Meta’s official branded content guidelines—they’re locked into this format.

    YouTube: the first five seconds, or the description box

    YouTube’s policy requires disclosure “in the video itself” or “in the description.” If you choose the description route, it needs to appear above the fold—before the “Show more” link.

    For video content, the safest approach is a verbal mention in the first five seconds, paired with on-screen text that stays visible for at least three seconds. The FTC has cited creators for burying disclosures at the end of a video or in collapsed description text.

    YouTube also has a “Includes paid promotion” checkbox in the upload flow. Checking it adds a small disclaimer to the lower-left corner of the video player. That checkbox is required for certain ad categories (political, prescription drugs), but for standard sponsorships, it’s considered supplementary—not a replacement for in-video or description disclosure.

    TikTok: the branded content toggle is mandatory

    TikTok requires the “Branded Content” toggle for any post that promotes a third-party product or service. You’ll find it in the post settings under “Advanced settings” → “Content disclosure.”

    Enabling it adds a “Paid partnership” label to the top of the video. Like Meta, TikTok treats this as the primary disclosure mechanism. Adding #ad in the caption is recommended but not sufficient on its own.

    One catch: TikTok’s branded content toggle is only available to accounts in the Creator Marketplace or those that meet specific eligibility thresholds (10,000+ followers and 100,000+ video views in the last 30 days as of mid-2026). If you don’t have access yet, you’re stuck with caption-based disclosure—and the platform may limit your reach or remove the post if it’s flagged as undisclosed advertising.

    LinkedIn: text-based disclosure still rules

    LinkedIn doesn’t have a built-in sponsored-content label for individual posts (its “Sponsored Content” product is for paid ads run through Campaign Manager, not organic posts).

    For organic posts that include a paid partnership, you need to disclose it in the text itself. The FTC’s guidance applies: the disclosure should appear before the call to action and be clear without requiring users to click “see more.”

    Common phrasing: “Sponsored by [Brand],” “Paid partnership with [Brand],” or “This post is sponsored by [Brand].” Put it in the first two lines of the post. Burying it after three paragraphs or in a comment doesn’t meet the standard.

    What the FTC actually enforces

    Platform-specific tools help, but the FTC’s core rule is simple: disclosures must be “clear and conspicuous.” That means:

    • Visible without scrolling, clicking, or hovering
    • In plain language—no ambiguous hashtags like #partner or #collab
    • Close to the claim being made, not buried at the end

    In practice, the FTC has gone after creators and brands for disclosures that appeared only in collapsed text, in light-colored fonts against light backgrounds, or in strings of hashtags where #ad was the eighth tag.

    If a platform’s built-in tool exists and you don’t use it, that’s a red flag during an audit. If you use it and add text disclosure, you’re in the clear.

    One disclosure format that works everywhere

    If you’re cross-posting the same sponsored content to multiple platforms, lead with a text-based disclosure in the first sentence and enable any platform-specific toggles where available.

    Example: “Sponsored by [Brand]. Here’s why I’ve been using [product]…”

    That satisfies LinkedIn and Twitter (which has no built-in tool). Then layer on Meta’s partnership toggle, TikTok’s branded content flag, and YouTube’s in-video mention. It’s redundant, but redundancy is the point—regulators and platforms both want the relationship to be unmissable.

    Got a question about disclosure rules for a specific platform or sponsor agreement? Reply to this email—we’ll cover it in a future Q&A.

  • WordPress plugin update rollbacks: how automatic reversions work

    WordPress plugin update rollbacks: how automatic reversions work

    WordPress plugin update rollbacks: how automatic reversions work
    Photo: Simpson, Thomas via Wikimedia Commons (Public domain)

    WordPress 6.3 shipped a feature most operators don’t know exists until it saves them: automatic plugin rollback after a fatal error. If a plugin update crashes your site, WordPress will—under specific conditions—revert to the previous version without you touching anything.

    That safety net isn’t as wide as it sounds. Understanding when it fires, what it catches, and what it misses will save you from assuming your site is protected when it isn’t.

    What triggers an automatic rollback

    WordPress monitors plugin updates for fatal PHP errors during a brief window immediately after activation. If a newly updated plugin throws a fatal error that would break your site, WordPress detects it during the next loopback request—a self-ping the system uses to verify the site is still responding.

    If that loopback fails, WordPress rolls the plugin back to its previous version and sends an email to the site admin address on file. The entire process happens within seconds to minutes, depending on how quickly the loopback request completes.

    Three conditions must be true for rollback to fire:

    • The plugin was updated through the WordPress admin dashboard or WP-CLI with the --defer-site-health flag
    • The fatal error occurs during plugin load or initialization—not later during a page request
    • The loopback request detects the failure before the request timeout (default: 10 seconds)

    If you update via SFTP, the rollback system never sees the change. If the plugin loads fine but crashes when a visitor hits a specific page, rollback won’t catch it. If your server is slow and the loopback times out before detecting the error, you’re on your own.

    What gets reversed, what doesn’t

    Rollback restores the plugin’s PHP files to the previous version. That’s it. Any database changes the new version made—schema migrations, new rows, updated option values—stay in place.

    This creates a mismatch problem. If version 2.0 of a plugin adds a database column and version 1.9 expects it not to exist, rolling back the code doesn’t undo the schema change. You’re now running old code against a new database structure.

    Most well-coded plugins handle this gracefully by checking for the existence of columns or tables before querying them. Poorly coded plugins assume the database structure matches the code version and break in new ways after rollback.

    Settings changes are similarly sticky. If the new version migrated your settings array to a new format, rollback won’t revert that migration. You’ll need to manually restore settings from a backup or reconfigure the plugin.

    When rollback fails silently

    Rollback depends on the loopback request succeeding. If your server blocks loopback requests—common on shared hosts with aggressive firewall rules or when using localhost SSL certificates—WordPress can’t verify the site is broken, so it won’t roll back.

    You can test whether loopbacks work by visiting Tools > Site Health in the WordPress admin. If the “Loopback request” test fails, automatic rollback won’t work either. Fixing it usually requires whitelisting your own domain in your firewall or adjusting WP_HTTP_BLOCK_EXTERNAL settings.

    Rollback also won’t fire if the fatal error occurs outside the plugin’s initialization phase. If a plugin loads successfully but crashes when you try to access its settings page, that’s a runtime error, not an initialization failure. WordPress considers the update successful because the site didn’t break immediately.

    Manual rollback as fallback

    Even when automatic rollback works, it’s worth knowing how to roll back manually. WordPress doesn’t keep old plugin versions on your server—it deletes them after update. To manually revert, you’ll need to download the previous version from the WordPress.org plugin repository.

    Visit wordpress.org/plugins/[plugin-slug]/advanced/ to access the developer view, which lists all previous versions. Download the version you need, delete the current plugin folder via SFTP or your host’s file manager, and upload the old version. Reactivate if necessary.

    If you’re on a managed WordPress host, check whether they offer automatic snapshots before updates. Kinsta, WP Engine, and similar hosts take filesystem snapshots before applying updates, letting you restore the entire plugin folder—and sometimes the database state—from before the update.

    For operators managing multiple sites, a staging environment is the better insurance. Test plugin updates on staging first. If they break, your production site never sees the bad code. If automatic rollback is your only safety net, you’re relying on a system that only catches a subset of failures.

    One Two Three Send covers WordPress operations, hosting, and the tools solo operators use to keep sites running. Subscribe for one operator-focused article every day.

  • SEO title tags: why Google rewrites 33% of them automatically

    SEO title tags: why Google rewrites 33% of them automatically

    SEO title tags: why Google rewrites 33% of them automatically
    Photo by Mitchell Luo on Unsplash

    You spend twenty minutes crafting the perfect title tag. You test it in the preview tool. You publish. Two days later, you check Google Search Console and discover Google’s rewritten it entirely—pulling text from your H1, your brand name, or some fragment of body copy you never intended as a title.

    This isn’t a bug. It’s how Google’s been operating since August 2021, and internal studies suggest it now rewrites roughly 33% of all title tags that appear in search results. Understanding when and why this happens gives you more control over what searchers actually see.

    When Google rewrites your title tag

    Google replaces your title tag when its algorithm decides the original doesn’t accurately describe the page content, is too long, stuffs keywords, or doesn’t match the query intent. The rewrite pulls from:

    • Your H1 heading
    • Visible text near the top of the page
    • Anchor text from internal or external links pointing to the page
    • Structured data markup, especially for products or articles

    The most common trigger is a mismatch between the title tag and the H1. If your title says “Best Email Marketing Tools” but your H1 says “9 Email Platforms We Tested in 2026,” Google often uses the H1 verbatim. The second most common trigger is length: titles over 60 characters get truncated or rewritten entirely, especially on mobile.

    Keyword stuffing still triggers rewrites. If your title is “Email Marketing Software | Email Tools | Best Email Platforms | Email Services,” Google will almost certainly replace it with something shorter pulled from your page.

    What gets rewritten most often

    Homepage titles get rewritten more than any other page type—close to 50% of the time. Google typically replaces them with the brand name, sometimes appended with a tagline or category descriptor pulled from the page. If your homepage title is “Welcome to Acme Newsletter Tools,” Google might show “Acme | Newsletter Platform for Solo Operators” if that phrase appears prominently on the page.

    Product and landing pages get rewritten when the title is too sales-heavy. “Buy the #1 Newsletter Tool Today!” becomes “Acme Newsletter Tool” in search results. Google strips superlatives, urgency language, and calls-to-action that don’t describe the page.

    Blog post titles survive more often, but only if they’re descriptive and under 60 characters. Opinion pieces, how-tos, and case studies have the highest survival rate because the title and H1 usually match and clearly describe the content.

    How to write titles that stick

    Keep your title tag and H1 identical or nearly identical. Google’s algorithm treats divergence as a signal that one of them is inaccurate. If you need a different H1 for design reasons, make sure it’s semantically similar to the title.

    Stay under 60 characters, including spaces. Google’s display limit fluctuates based on pixel width, but 60 characters is the safe zone for desktop and mobile. If your title is longer, Google will either truncate it with an ellipsis or replace it entirely.

    Front-load the topic, not the brand. “ConvertKit vs. MailerLite comparison | Acme Blog” becomes “ConvertKit vs. MailerLite comparison” in search results far more often than “Acme Blog | ConvertKit vs. MailerLite.” Put the descriptor first, brand last.

    Avoid keyword repetition. One mention per keyword is enough. Google’s algorithm interprets repetition as manipulation and replaces the title with a cleaner version.

    When a rewrite is better than your original

    Not every rewrite is bad. If Google pulls a clearer, more specific phrase from your H1 or body copy, your click-through rate might improve. Check Google Search Console’s performance report: compare impressions, clicks, and CTR before and after the rewrite. If CTR increases, leave it alone.

    If CTR drops or the rewritten title misrepresents the page, you have two options: rewrite the title tag to better match the H1 and body content, or rewrite the H1 to align with the title tag. Either way, alignment is what stops the rewrite.

    One non-obvious fix: add structured data. Google’s algorithm gives more weight to titles in Article or Product schema markup. If your title appears in structured data and matches the visible title, Google’s less likely to replace it.

    Want more operator-level breakdowns of how platforms actually work? Subscribe to One Two Three Send—one article every morning, no fluff, no affiliate listicles.

  • 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.