Author: onetwothreeadmin

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

    SEO title tags: why Google rewrites them

    SEO title tags and Google rewrites
    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. Google says it may use text other than your title element when it thinks that describes the page better (Google Search Central: title links, checked October 2026). 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: long titles can be cut off or rewritten, 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 are a common case. Google can 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.

    Keep titles short. Google’s display limit is based on pixel width, not a fixed character count, so shorter is safer on 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.

  • Social media cross-posting tools treat replies inconsistently

    Social media cross-posting tools treat replies inconsistently

    Social media cross-posting tools treat replies inconsistently
    Photo by Alexander Shatov on Unsplash

    Cross-posting saves time. You write once, and your scheduler pushes the same content to Twitter, LinkedIn, Mastodon, and Threads. But while the original post goes everywhere, the responses don’t come back the same way—and that breaks conversations.

    Most cross-posting tools treat replies as read-only artifacts or don’t pull them at all. If someone comments on LinkedIn, you won’t see it in the dashboard unless the tool explicitly built LinkedIn comment ingestion. If someone replies on Twitter, it might appear in-app but won’t thread back to the Mastodon copy. You end up checking four inboxes anyway, which defeats half the point.

    What cross-posting tools actually sync

    Publer pushes your post to every connected account and pulls back basic engagement counts—likes, shares, retweets. But replies and comments stay on each platform. You can see that someone replied, but reading or responding requires clicking through to the native platform. There’s no unified inbox.

    Buffer works the same way: it shows reply counts in the dashboard, but you open Twitter or LinkedIn in a separate tab to read them. Buffer’s Analyze plan includes a basic reply view for Twitter, but it’s read-only. You still compose responses in the platform’s native interface.

    Hootsuite is the exception. Its inbox aggregates replies and mentions from Twitter, Facebook, Instagram, and LinkedIn into one feed. You can reply directly from Hootsuite, and the response posts back to the original platform. But it doesn’t support Mastodon, Threads, or Bluesky yet, so if you’re cross-posting to those, you’re back to checking them separately.

    Postpone, which is popular with solo operators, doesn’t pull replies at all. It’s purely a publisher. The analytics tab shows clicks and impressions via URL shorteners, but no social engagement data. If you want to see who replied, you open each platform manually.

    Why replies don’t unify

    APIs are the bottleneck. Twitter’s API lets apps read replies to your posts, but only if the reply mentions your username or is a direct thread descendant. Quoted tweets don’t always appear. LinkedIn’s API returns comments on your posts, but only for posts you published—comments on shares or reposts of your content are invisible to third-party tools.

    Mastodon technically allows full reply threading via ActivityPub, but most scheduling tools don’t support it yet because Mastodon instances rate-limit API calls differently. A tool built for Twitter’s global rate limits can’t assume Mastodon behaves the same way across hundreds of independent servers.

    Threads doesn’t have a public API yet. Tools that “support” Threads are using Instagram’s API as a proxy, which means reply ingestion is either unavailable or unreliable.

    How to design around it

    If you’re cross-posting to more than two platforms, accept that replies will scatter. Don’t promise to respond everywhere—pick one or two platforms where you’ll actually engage, and say so in your bio or pinned post. “I reply on Twitter and LinkedIn” sets expectations and consolidates where you spend attention.

    Use native notifications, not dashboard alerts. Turn on push or email notifications for replies on the platforms where you want to respond. Let the tool handle publishing, but let the platform handle reply alerts. Aggregated dashboards lag by minutes or hours, and you’ll miss time-sensitive conversations.

    If you’re running a team account, assign one platform per person. Cross-posting makes publishing faster, but reply management still requires humans. Split the work by platform, not by tool, so no one’s checking four inboxes trying to catch everything.

    When unified replies actually matter

    If you’re running a customer support account, use Hootsuite or Sprout Social—tools explicitly built for inbox unification. The extra cost is worth it when reply speed affects retention. For content-driven accounts, where replies are async conversations rather than service tickets, checking platforms individually is fine. You’re not losing money by waiting an hour.

    One exception: if you’re using social media as a lead channel and replies contain purchase intent or partnership inquiries, unified inboxes reduce response lag. But most solo operators and small teams don’t get enough inbound volume to justify the premium tier cost. You’ll know when you need it—your reply count will outpace your ability to check tabs.

    Want more tools and workflows that actually fit solo operators? Subscribe to One Two Three Send and get one operator-to-operator breakdown every morning.

    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.

  • ConvertKit vs. MailerLite: which one actually delivers better?

    ConvertKit and MailerLite both land on every “best newsletter platform” list. Both promise high deliverability. Both offer free tiers, automation builders, and landing pages. So which one actually gets your emails into inboxes more reliably?

    The short answer: it’s closer than you think, and deliverability alone probably shouldn’t decide this.

    Deliverability reputation and shared IP pools

    Both platforms use shared IP pools for most senders. That means your emails go out alongside thousands of other newsletters. If you’re on the Creator plan at ConvertKit (starts at $25/month for 1,000 subscribers) or any paid MailerLite tier (starts at $9/month for the same size), you’re sharing infrastructure with everyone else at your tier.

    ConvertKit’s advantage: they segment pools by sender reputation. High-engagement senders get routed through better-performing IPs. MailerLite uses a flatter pool structure, though they do isolate problematic senders.

    In practice, third-party inbox placement studies from mid-2025 through early 2026 show both platforms landing 92–96% of emails in primary inboxes for senders with clean lists and consistent engagement. The difference is statistically narrow—often within 2 percentage points depending on the test cohort.

    What matters more: your own list hygiene. A 40% open rate on MailerLite will outperform a 15% open rate on ConvertKit every time.

    Deliverability tooling: where they differ

    ConvertKit includes SPF and DKIM setup in onboarding, but custom domain sending (the “from” address uses your domain, not convertkit.com) requires a paid plan. MailerLite offers custom domain sending on all paid plans, including the $9 tier.

    Both platforms enforce list verification and double opt-in by default. ConvertKit is stricter about imported lists—they’ll flag high bounce rates faster and throttle sending if you upload a stale list. MailerLite gives you slightly more rope, which can be a liability if you’re not careful.

    Neither platform offers dedicated IPs on standard plans. ConvertKit Creator Pro (starts at $50/month) includes that option. MailerLite requires you to contact sales for dedicated IP pricing, typically starting around $80/month for higher-volume senders.

    Where ConvertKit pulls ahead

    Automation sophistication. ConvertKit’s visual builder lets you branch on link clicks, tag additions, custom field values, and purchase behavior. If you’re running a funnel with lead magnets, trip-wire offers, and product launches, ConvertKit’s logic handles complexity better.

    Subscriber tagging is more flexible. You can apply multiple tags per action, segment by tag combinations, and trigger sequences based on tag presence or absence. MailerLite has groups and segments, but the tagging system feels more rigid once you’re past 3,000 subscribers.

    Paid newsletter integration is native. ConvertKit Commerce lets you sell subscriptions and digital products without connecting Stripe separately. Revenue share is 3.5% + transaction fees. MailerLite requires third-party integrations for paid memberships.

    Where MailerLite wins

    Price. For the same 5,000 subscribers, ConvertKit charges $66/month. MailerLite charges $30/month. That’s $432/year in savings, which matters when you’re bootstrapped.

    Drag-and-drop email builder. ConvertKit’s editor is intentionally simple—plain text with minimal formatting. MailerLite gives you a visual editor with image blocks, buttons, columns, and templates. If your newsletter includes product showcases or event promotions, MailerLite’s design flexibility shows.

    Included features at lower tiers. Landing pages, pop-up forms, and A/B testing are available on MailerLite’s $9 plan. ConvertKit gates landing pages behind the $25 tier and limits forms on the free plan.

    Built-in website builder. MailerLite added a simple site builder in late 2025. It’s not WordPress, but if you need a landing page hub without spinning up hosting, it’s included.

    Who should pick which

    Choose ConvertKit if you’re running a creator business with multiple offers, a segmented audience, and automation sequences that branch based on behavior. The extra cost pays for itself if you’re monetizing through courses, coaching, or premium subscriptions.

    Choose MailerLite if you’re launching, your list is under 10,000, and you need a full-featured platform without spending $500+/year. The design flexibility and lower price make it easier to experiment before you’ve nailed product-market fit.

    Switch between them? Both platforms let you export your list as CSV. Automation sequences don’t transfer cleanly—you’ll rebuild those by hand. Expect half a day of work to migrate 3,000+ subscribers.

    Want more tool breakdowns like this? Subscribe to One Two Three Send and get operator-focused comparisons every week—no affiliate fluff, just what actually works.

  • Traffic source tagging: when UTM parameters break your funnel

    Traffic source tagging: when UTM parameters break your funnel

    Traffic source tagging: when UTM parameters break your funnel
    Photo: Ajiro Shinpei via Wikimedia Commons (CC BY-SA 4.0)

    UTM parameters are the standard way to tag inbound traffic. You append ?utm_source=twitter&utm_medium=social&utm_campaign=launch to a URL, and your analytics platform tells you exactly where visitors came from.

    Except when they don’t. Or when they tell you too much. Or when the tags themselves break the thing you’re trying to measure.

    Most operators treat UTM tagging as a best practice with no downsides. But tagging everything creates three specific problems that can corrupt your funnel data, kill affiliate commissions, and fragment user sessions in ways that make attribution worse, not better.

    Problem one: UTM parameters create new sessions

    Google Analytics 4 treats each unique UTM combination as a new traffic source. If a visitor lands on your site from an organic search, then clicks a UTM-tagged link in your own newsletter five minutes later, GA4 records two separate sessions with two different sources.

    This matters when you’re measuring time-to-conversion or return-visitor behavior. A subscriber who clicks three different UTM-tagged links in three emails over two weeks shows up as three separate users in your funnel reports. Your email conversion rate looks lower than it is, and your session duration gets artificially shortened every time someone re-enters via a tagged link.

    The fix: don’t use UTM tags on internal links—emails to your own subscribers, in-app notifications, or links between pages on your own domain. Use GA4’s built-in email campaign tracking instead, or tag only the first entry point (e.g., a landing page) and let internal navigation remain untagged.

    Problem two: tagged URLs break affiliate cookie attribution

    Most affiliate programs set a tracking cookie when someone clicks your referral link. That cookie persists for 30, 60, or 90 days, so you get credit even if the visitor returns later to purchase.

    But some affiliate platforms treat any URL with query parameters—including UTM tags—as a distinct destination. If you link to example.com/product?via=yourID&utm_source=newsletter, the affiliate platform may fail to recognize the destination URL, drop the cookie, or attribute the sale to someone else.

    Amazon Associates is particularly strict about this. Their link structure already includes tracking parameters, and appending UTMs can invalidate the referral. The sale still happens, but you don’t get credited.

    The workaround: test every tagged affiliate link in a private browser session before you send it. Clear cookies, click the link, and verify that the affiliate platform sets its tracking cookie. If it doesn’t, remove the UTM tags or use a redirect service that strips parameters after the cookie is set.

    Problem three: over-tagging fragments your source reports

    If you tag every social post, every email, and every ad with unique campaign names, your traffic source report becomes unreadable. Instead of seeing “newsletter traffic converts at 8%,” you see forty rows: one for each individual send, each with too few conversions to be statistically meaningful.

    This is especially common with automated tagging tools that append timestamps or post IDs to campaign parameters. You end up with utm_campaign=launch_email_20260904_v2 instead of just utm_campaign=launch, and every report requires manual grouping to be useful.

    The solution: use a consistent naming taxonomy. Limit utm_campaign to high-level initiatives (product launch, seasonal promo, onboarding series). Use utm_content or utm_term for granular variation tracking, and set up GA4 custom channel groups or BigQuery exports if you need to analyze individual sends.

    When UTM tagging actually helps

    UTM parameters are still the best tool for tracking external traffic sources that don’t pass referrer data: social media platforms, messaging apps, QR codes, and anywhere you can’t rely on the HTTP referer header.

    They’re also essential for paid campaigns where the ad platform doesn’t automatically pass source information, and for any A/B test where you’re comparing different traffic sources or creative variations.

    The rule: tag external traffic and high-value experiments. Skip internal links, test affiliate compatibility before sending, and keep your campaign taxonomy simple enough to read six months from now.

    Got a question about tagging, attribution, or analytics setup? Reply to this email—we’ll cover it in a future issue.

  • AI prompt versioning: when to save, fork, or start fresh

    AI prompt versioning: when to save, fork, or start fresh

    AI prompt versioning: when to save, fork, or start fresh
    Photo by Prayag Tejwani on Unsplash

    If you’re using AI assistants daily, you’ve hit the moment: a prompt that worked last week suddenly feels stale. You tweak a line. Then another. Twenty minutes later, you’re staring at a Frankenstein instruction set that produces worse output than your original—and you didn’t save the version that worked.

    Prompt versioning sounds like overkill until you need it. Most solo operators don’t version their prompts at all. A handful save everything obsessively. The right answer sits between those extremes, and it depends on what kind of prompt you’re working with.

    Three prompt types, three versioning strategies

    One-off exploratory prompts: Don’t save these. If you’re drafting a single social post, testing an outline format, or asking a research question, there’s no ROI in versioning. You’ll never reuse the exact phrasing. Let it live in your chat history and move on.

    Templated repeatable prompts: Save a canonical version, but don’t track every edit. These are prompts you run weekly—email subject line generators, product description templates, summary boilerplate. Keep one master copy in a note or doc. When you improve it, overwrite the old version. You don’t need a change log; you need a reliable starting point.

    High-stakes or multi-person prompts: Version these properly. If a prompt controls brand voice across a team, generates customer-facing content at scale, or took hours to tune, treat it like code. Save dated versions. Label what changed and why. Use a simple naming convention: email-outline-v3-2026-09-04 works fine. You’re not trying to impress a GitHub contributor; you’re protecting your ability to roll back when an edit breaks output quality.

    When to fork instead of edit

    Forking means duplicating a prompt and modifying the copy, leaving the original intact. Do this when you want to test a structural change but aren’t sure it’ll work. Examples: switching from bullet output to paragraph form, adding a new constraint that might conflict with existing instructions, or adapting a B2C prompt for B2B use.

    If the fork works, you now have two specialist prompts instead of one generalist. If it doesn’t, you delete it and keep the original. This is cheaper than A/B testing in production or wondering why your reliable prompt suddenly underperforms.

    What actually matters in a version note

    Most version notes are useless. “Updated prompt” tells you nothing. “Improved output quality” is only slightly better. Useful version notes answer one question: what will break if I roll back?

    Good examples:

    • “Added length cap of 280 chars—removes context but fits Twitter.”
    • “Switched from ‘you’ to ‘we’—changes tone from instructional to collaborative.”
    • “Removed brand name from system prompt—now relies on user message to supply it.”

    Each note tells you the trade-off. That’s what you need when you’re deciding whether to revert.

    Where to store versions without overthinking it

    Plain text files work. So do Google Docs with version history enabled. Notion databases let you tag by use case and date. Some operators use Claude Projects to pin multiple prompt versions as context artifacts—useful if you’re iterating inside a single long-running conversation.

    The tool doesn’t matter. What matters is that you can find the last known-good version in under 30 seconds when today’s experiment flops.

    The surprisingly common failure mode

    Here’s what breaks most often: you tune a prompt for weeks, it works beautifully, then the underlying model updates. Your prompt still runs, but output quality drops or formatting changes. You assume your prompt is the problem, so you rewrite it. Now you have a worse prompt and no clean comparison to isolate whether the model or your edits caused the regression.

    Versioning doesn’t prevent model drift, but it lets you test your old prompt against the new model behavior cleanly. If the old version also underperforms now, you know the model changed. If only the new version is bad, you know your edits were the culprit.

    When versioning is just procrastination

    Some operators version everything because it feels productive. It’s not. If you’re spending more time labeling and filing prompts than using them, you’re optimizing the wrong thing. Version control is a loss-prevention tool, not a productivity system. Use it when the cost of losing a prompt exceeds the cost of saving it. For most one-person operations, that threshold is higher than you think.

    If you’re running a content operation that depends on repeatable AI output, start with just two saved versions: the one you’re using now, and the last one that worked. Everything else is optional until it’s not.

    Want more on how solo operators are actually using AI day-to-day—without the hype? Subscribe to One Two Three Send. One focused piece like this, delivered every morning.

    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.

  • WordPress REST API rate limiting: how plugins enforce request caps

    WordPress REST API rate limiting: how plugins enforce request caps

    WordPress REST API rate limiting: how plugins enforce request caps
    Photo by Stephen Phillips – Hostreviews.co.uk on Unsplash

    If you run a WordPress site with any meaningful traffic—or if you’ve built automation that hits your own API—you’ve probably seen a 429 error at some point. The WordPress REST API doesn’t enforce rate limits by default, but most production sites end up with them anyway, often without realizing it.

    The limits come from security plugins, caching layers, or server-level middleware. They’re rarely documented in dashboards, and they trigger differently depending on whether the request is authenticated, what endpoint you’re hitting, and whether the plugin treats logged-in users differently from unauthenticated bots.

    Here’s how the most common rate-limiting mechanisms actually work, and what to check when legitimate requests start getting blocked.

    Where rate limits come from

    Core WordPress doesn’t rate-limit the REST API. It’s open by default. But most hosting providers and security plugins layer restrictions on top:

    • Security plugins like Wordfence, iThemes Security, and Sucuri impose per-IP request caps, usually between 10 and 60 requests per minute depending on the endpoint.
    • Hosting firewalls (Cloudflare, server-level mod_security rules, or host-specific WAFs) apply blanket limits to all /wp-json/ traffic, often as low as 5 requests per second.
    • Caching plugins (WP Rocket, LiteSpeed Cache) sometimes block repeat API calls as part of bot-protection rules, especially if the requests bypass the cache entirely.

    The problem: these tools don’t coordinate. A single legitimate automation script can trip multiple rate limiters at once, and the error messages rarely tell you which layer rejected the request.

    How plugins enforce caps

    Most WordPress security plugins use one of two methods:

    IP-based throttling: The plugin logs request timestamps per IP address. If you exceed the cap within the time window, it returns a 429 response. This breaks multi-user environments where users share an IP (office networks, VPNs). It also penalizes API clients that batch requests from a single origin.

    Endpoint-specific rules: Some plugins treat /wp-json/wp/v2/posts differently from /wp-json/custom-namespace/endpoint. Public read endpoints often get stricter limits than authenticated write endpoints, under the assumption that bots hammer public routes more aggressively. Wordfence, for example, applies a default 10-request-per-minute cap to unauthenticated API traffic, but allows 60 for logged-in users.

    The gotcha: if your automation uses application passwords or OAuth tokens, the plugin may still treat it as unauthenticated if the token isn’t passed correctly in the Authorization header. That drops you into the stricter bucket.

    What the 429 response doesn’t tell you

    When you hit a rate limit, WordPress returns 429 Too Many Requests. Sometimes you get a Retry-After header; often you don’t. The response body rarely names the plugin or layer that blocked you.

    To diagnose:

    • Check server access logs for the exact timestamp of the 429. If the response came from Apache or Nginx (not PHP), it’s a server-level firewall rule, not a plugin.
    • Temporarily disable security plugins one at a time. If the 429 disappears, you’ve found the source.
    • Look for rate-limit settings in your security plugin’s advanced options. They’re rarely on the main dashboard—Wordfence buries them under “Firewall Options,” iThemes under “Advanced Settings.”

    If you’re using Cloudflare, check the Security Events log. Cloudflare’s “Rate Limiting” rules fire independently of WordPress, and they don’t show up in your WordPress error logs at all.

    When to raise limits (and when not to)

    If you’re hitting limits with your own automation, the fix is straightforward: whitelist your script’s IP or user agent in the security plugin’s settings. Most plugins let you exempt specific IPs or authenticated users from throttling.

    If public traffic is hitting limits, don’t just raise the cap. Rate limits exist to block credential-stuffing attacks and brute-force POST spam. Instead:

    • Cache aggressively. If bots are hammering /wp-json/wp/v2/posts, serve that response from Redis or a CDN edge. The request never hits PHP, so it doesn’t count against your rate limit.
    • Require authentication for write endpoints. If your API accepts user-submitted data, force clients to authenticate. Unauthenticated POST requests should return 401, not 200.
    • Monitor which endpoints get hit. If 90% of your API traffic goes to a single route, that’s either a bot or a misconfigured frontend polling too frequently. Fix the source, not the symptom.

    Testing your own limits

    Most operators don’t know their rate limits until they hit them in production. To test:

    Use a simple loop to hit your API 100 times in 60 seconds. Start with an unauthenticated GET request to /wp-json/wp/v2/posts. Note when the first 429 appears, then repeat with an authenticated request using an application password.

    If the limits differ, your plugin treats auth and non-auth traffic separately. If they’re the same, you’re hitting a server-level cap that doesn’t distinguish between the two.

    Most hosting providers don’t advertise their API rate limits. If you’re on managed WordPress hosting, check the knowledge base or open a ticket. The limit is usually there—it’s just not on the pricing page.

    Want more infrastructure breakdowns like this? Subscribe to One Two Three Send—we dig into the mechanics other newsletters skip.