Author: onetwothreeadmin

  • Buffer vs. Publer vs. Hootsuite: which scheduler fits your workflow

    Buffer vs. Publer vs. Hootsuite: which scheduler fits your workflow

    Buffer vs. Publer vs. Hootsuite: which scheduler fits your workflow
    Photo: Raysonho @ Open Grid Scheduler / Scalable Grid Engine via Wikimedia Commons (CC0)

    Social media schedulers all promise the same outcome—posts go out on time across multiple platforms—but the workflow, pricing, and feature ceiling vary enough that the wrong choice costs you hours every month.

    Here’s how Buffer, Publer, and Hootsuite compare for solo operators and small teams running content-driven businesses, with real pricing and the non-marketing-page details that matter when you’re scheduling 40+ posts a week.

    Pricing structure and profile limits

    Buffer’s free tier allows three social profiles and ten scheduled posts per profile. The Essentials plan starts at $6/month per profile. If you manage a newsletter brand across Twitter, LinkedIn, Facebook, and Instagram, that’s $24/month before you add a second team member.

    Publer offers a free tier with one brand (unlimited profiles within that brand) and ten posts per profile. The Professional plan runs $12/month for five brands, 500 posts per brand, and bulk scheduling. For most solo operators with one core brand and multiple platforms, this is the better math.

    Hootsuite’s free tier disappeared in late 2025. The Professional plan starts at $99/month for one user and ten profiles. It’s built for agencies and mid-size marketing teams, not solo operators. Unless you need advanced analytics integrations or client approval workflows, the price floor doesn’t match the operator profile reading this newsletter.

    Bulk upload and content recycling

    Buffer’s bulk scheduling works via CSV upload, but the free and Essentials tiers don’t support it—you need the Team plan at $12/month per profile. The CSV format is strict: one row per post, with separate columns for each network. If you cross-post the same content to three platforms, you duplicate rows.

    Publer’s bulk uploader accepts CSV and allows content recycling within the Professional plan. You can set a post to repeat every 30 days or queue evergreen content to rotate automatically. The interface supports drag-and-drop rearrangement after upload, which saves time when you need to reorder a week’s worth of posts.

    Hootsuite’s bulk scheduler is robust but overkill for most solo setups. It supports CSV and RSS feed ingestion, with conditional logic for different networks. You pay for power you likely won’t use unless you’re managing ten+ brands with unique posting cadences per platform.

    Platform-specific quirks

    All three handle the major platforms—Twitter, LinkedIn, Facebook, Instagram, Pinterest—but implementation details differ.

    Buffer auto-threads Twitter posts if you exceed 280 characters. Publer requires manual thread setup but shows a live preview of how the thread will render. Hootsuite supports threads but buries the feature in a submenu most users miss.

    For LinkedIn carousels, Publer is the only one of the three that natively supports PDF upload and conversion without requiring a workaround. Buffer and Hootsuite treat carousels as image sets, which means you’re exporting slides individually and uploading them in sequence.

    Instagram first-comment scheduling—where you want a link or CTA to post as the first comment under your image—is supported in Publer’s Professional tier and Hootsuite’s all plans. Buffer added it in mid-2025, but only on Team tier and above.

    Who each tool fits

    Pick Buffer if you manage three or fewer profiles, post fewer than ten times per week per platform, and don’t need bulk uploading. The per-profile pricing scales badly, but the interface is clean and the mobile app is reliable.

    Pick Publer if you run one brand across four to six platforms, post daily, and want bulk scheduling and content recycling without jumping to a $99/month tool. The Professional plan at $12/month covers most solo and two-person team use cases.

    Pick Hootsuite if you’re managing multiple clients, need approval workflows, or require integration with enterprise analytics platforms like Sprout Social or Salesforce. For a single online business, it’s overbuilt and overpriced.

    One non-obvious consideration: check each platform’s API rate limits during high-traffic periods. Hootsuite and Buffer have priority API access with most networks due to partnership agreements. Publer, as a smaller player, occasionally hits Instagram’s rate limit if you schedule more than 25 posts in a single batch. It’s rare, but it happens.

    What’s working for you? Reply with the scheduler you’re using and whether the pricing model still makes sense as your posting volume grows. We’ll compile operator feedback in a future piece.

    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 image optimization plugins: WebP vs. AVIF in 2026

    WordPress image optimization plugins: WebP vs. AVIF in 2026

    WordPress image optimization plugins: WebP vs. AVIF in 2026
    Photo by Growtika on Unsplash

    Image optimization plugins for WordPress promise faster page loads and better Core Web Vitals scores. Most now offer WebP conversion by default, and a growing number add AVIF support. But the two formats solve different problems, and choosing the wrong one can break images for a slice of your traffic or bloat your hosting bill.

    Here’s how to pick the right format—and when to use both.

    WebP: mature, compatible, predictable file sizes

    WebP has been around since 2010. Browser support hit critical mass years ago: Chrome, Firefox, Safari, and Edge all support it natively. According to Can I Use data, WebP coverage sits above 97% of global users in 2026.

    WebP handles both lossy and lossless compression. For most photography and blog post images, WebP lossy at 80–85 quality produces files 25–35% smaller than JPEG at equivalent visual quality. Lossless WebP beats PNG for screenshots and graphics with transparency, though the size advantage is smaller—usually 15–20%.

    The format supports alpha-channel transparency, which makes it a direct PNG replacement. If your site uses transparent logos, icons, or overlays, WebP won’t break them.

    Most WordPress image optimization plugins—ShortPixel, Imagify, Smush, EWWW Image Optimizer—convert to WebP automatically. The format is stable, well-tested, and won’t surprise you with edge cases.

    AVIF: smaller files, slower encoding, spottier support

    AVIF arrived later—Chrome added support in 2020, Safari in 2021. Browser coverage in 2026 hovers around 92%, which means 8% of visitors still can’t decode it. That gap includes older Android devices and some corporate-locked browsers.

    AVIF’s advantage is file size. At equivalent quality settings, AVIF images run 20–30% smaller than WebP and 40–50% smaller than JPEG. For image-heavy sites—portfolios, e-commerce galleries, photo blogs—that difference compounds fast.

    The tradeoff is encoding time. Generating AVIF files takes 3–5× longer than WebP on most hosting environments. If your plugin converts images on-upload or via cron job, AVIF processing can time out on shared hosting or budget VPS plans. Plugins like ShortPixel and Imagify offload conversion to their own servers, which sidesteps the timeout problem but adds API dependency.

    AVIF also handles transparency, but browser support for alpha-channel AVIF lags slightly behind opaque AVIF. If you’re converting transparent PNGs to AVIF, test rendering in Safari and Firefox before deploying site-wide.

    When to use WebP, AVIF, or both

    If your hosting plan is shared or low-tier VPS, stick with WebP. AVIF encoding can spike CPU usage and trigger resource limits. WebP delivers most of the file-size benefit without the processing cost.

    If you’re on managed WordPress hosting or a dedicated server with headroom, enable AVIF with WebP fallback. Most modern plugins support this: serve AVIF to browsers that accept it, WebP to the rest, and original JPEG/PNG as a last resort. The plugin detects support via the Accept HTTP header or generates a <picture> element with multiple <source> tags.

    For e-commerce sites with hundreds of product images, AVIF’s size advantage matters. A 30% reduction in image payload can shave a full second off Largest Contentful Paint on mobile. That translates to measurable conversion lift. Just make sure your plugin queues conversions in the background—batch-converting 500 images at once will lock up most hosting environments.

    For blogs with a few images per post, WebP is enough. The complexity of AVIF fallback chains and the 8% browser gap aren’t worth the incremental file-size win.

    One plugin detail that trips people up

    Most WordPress image optimization plugins store multiple formats of the same image—original JPEG, WebP, and AVIF—on disk. A single uploaded image can generate three or four files, depending on your settings. If you’re on a hosting plan with tight disk quotas, this adds up fast. A 2 GB media library can balloon to 5–6 GB after enabling both WebP and AVIF.

    Check your plugin’s settings for an option to delete originals after conversion. Some plugins keep the source file for rollback purposes; others let you purge it once the optimized versions are live. If disk space is tight, delete originals—but keep a local backup of your media library first.

    Want more tool breakdowns like this? Subscribe to One Two Three Send for weekly deep-dives on the platforms and plugins that run online businesses.

  • Newsletter sponsorship pricing: flat rate vs. CPM vs. performance

    Newsletter sponsorship pricing: flat rate vs. CPM vs. performance

    Newsletter sponsorship pricing: flat rate vs. CPM vs. performance
    Photo by Woliul Hasan on Unsplash

    Most newsletter operators pick a sponsorship pricing model based on what they’ve seen other people charge. That works until a sponsor pushes back, or you realize you left money on the table because your open rate spiked after you locked in a flat fee.

    The three common models—flat rate, CPM (cost per thousand impressions), and performance-based—aren’t interchangeable. Each one shifts financial risk between you and the sponsor, and the right choice depends on your list size, niche stability, and how predictable your engagement is.

    Flat rate: predictable revenue, capped upside

    A flat rate means you charge the same amount regardless of opens, clicks, or conversions. A sponsor pays $500 for a mention in next Tuesday’s issue, period.

    When it works: Flat rates make sense when your list is under 5,000 subscribers and fluctuates week to week, or when sponsors care more about brand association than measurable performance. If you write about a tight niche—like SaaS financial planning or Webflow development—sponsors often pay for access to a specific audience, not raw impressions.

    The risk you carry: If your open rate drops from 45% to 32% because you changed your subject line format or your ESP flagged a spam issue, you still owe the sponsor the same exposure. You absorb that variance.

    One non-obvious move: If you sell flat-rate sponsorships, specify a minimum list size or a floor open rate in your media kit. That way, if your list shrinks unexpectedly, you can renegotiate or pause the deal without breaching the agreement.

    CPM: scales with engagement, requires volume

    CPM pricing charges per thousand opens (or sends, depending on how you define “impression”). If you charge $40 CPM and 6,000 people open the email, the sponsor pays $240.

    When it works: CPM makes sense once your list exceeds 10,000 subscribers and your open rate is stable. Sponsors who buy CPM deals usually run them across multiple newsletters simultaneously and care about cost efficiency at scale. They’re comparing your $40 CPM to someone else’s $55.

    The risk the sponsor carries: If your open rate climbs from 38% to 50% because you tightened your subject lines, the sponsor pays more for the same ad slot. That’s why CPM buyers almost always ask for historical open rate data before committing.

    Pricing benchmark: As of mid-2026, B2B newsletter CPMs range from $30 to $80 depending on niche. Developer tools and finance newsletters command the high end; general business content sits closer to $35–$45.

    Performance-based: aligned incentives, delayed payment

    Performance deals tie payment to outcomes: cost per click (CPC), cost per signup, or cost per sale. The sponsor pays $2 for every click to their landing page, or 20% of revenue from customers who convert within 30 days.

    When it works: Performance pricing works when the sponsor has a tight attribution model and you’re confident your audience acts on recommendations. Affiliate programs and course launches almost always use performance terms. SaaS trials sometimes do, too.

    The risk you carry: You can send to 15,000 people, write a strong callout, and still earn nothing if the sponsor’s landing page loads slowly or their offer doesn’t resonate. You’ve spent the inventory; they’ve paid $0.

    What to negotiate: If you agree to performance pricing, ask for a small flat fee as a baseline—say, $200 guaranteed plus $1.50 per click above 100 clicks. That way, you’re compensated for the list access even if conversion falls short.

    Hybrid models: combining upfront + performance

    Some operators charge a modest flat fee ($300) plus a performance kicker (10% of attributed revenue, or $0.75 per click). This splits risk more evenly and works well when both parties are testing fit for the first time.

    Hybrid deals also make renewals easier. If the performance component delivers, the sponsor comes back and often agrees to raise the flat-rate floor. If it underperforms, you’ve still covered your baseline cost.

    Which model to lead with

    If your list is under 5,000 and growing inconsistently, start with flat rates. Lock in predictable revenue while you stabilize open rates and refine your pitch.

    Once you cross 10,000 subscribers and your monthly open rate variance drops below 5 percentage points, switch to CPM. You’ll earn more as your list grows, and sponsors will find your pricing easier to compare.

    Save performance-based deals for sponsors who already know your audience converts—typically after you’ve run one flat-rate or CPM test with them and the click-through rate exceeded 3%.

    One last note: whichever model you pick, write it into a short sponsorship agreement before you send the invoice. Specify the pricing model, the measurement window, and what happens if you need to reschedule the send. It’s a two-paragraph PDF, and it prevents confusion when the sponsor’s finance team asks why the invoice doesn’t match their internal estimate.

    Want more on running a content business without guessing? Subscribe to One Two Three Send and get one operator-focused article like this in your inbox weekly.

  • AI model context windows: when to chunk prompts vs. send everything

    AI model context windows: when to chunk prompts vs. send everything

    AI model context windows: when to chunk prompts vs. send everything
    Photo by Shubham Dhage on Unsplash

    Claude can handle 200,000 tokens. GPT-4 Turbo manages 128,000. Gemini 1.5 Pro claims two million. The context window arms race makes it sound like you should dump everything into a single prompt and let the model sort it out.

    That works sometimes. Other times it burns tokens, produces worse output, and costs more than splitting the work into smaller chunks.

    Here’s when each approach makes sense.

    When the full context window actually helps

    Long context windows shine when the model needs to cross-reference information scattered throughout a document. If you’re asking an AI to summarize a 40-page research report and identify contradictions between sections, sending the whole thing makes sense. The model needs simultaneous access to all sections to spot inconsistencies.

    Same logic applies to code review across multiple files. If you’re debugging an API integration where the authentication logic lives in one file, the request handler in another, and error handling in a third, the model benefits from seeing all three at once.

    Legal document analysis, long-form content editing where voice consistency matters, and multi-chapter manuscript feedback all benefit from full-document context.

    The pattern: tasks that require the model to hold multiple pieces of information in working memory and reason about relationships between them.

    When chunking produces better results

    Counterintuitively, smaller prompts often generate more accurate output for sequential tasks.

    If you’re processing 50 customer support tickets to categorize them by issue type, sending all 50 at once degrades accuracy. Models perform better when they focus on 5–10 tickets at a time. The attention mechanism that makes transformers work distributes focus across the entire context window—more tokens means more diluted attention on each individual item.

    Translation work follows the same rule. Translating a 10,000-word article in one shot produces inconsistent terminology and awkward phrasing in the middle sections. Breaking it into 1,000-word chunks with a glossary of key terms maintains quality.

    Batch operations—tagging blog posts, extracting structured data from invoices, generating social media variants from a content calendar—almost always work better in smaller groups.

    The cost calculation nobody runs

    Context window pricing isn’t linear with most providers. Claude charges the same per token whether you use 1,000 or 100,000, but response quality degrades as you approach the upper limit. You end up re-running prompts, which doubles your cost.

    More importantly: larger contexts generate longer responses by default. The model tries to address everything you’ve included. A 50,000-token input often produces a 3,000-token response when a 5,000-token input would have generated 400 tokens with the same useful information.

    If you’re running 100 operations per day and the difference between chunked and full-context prompts is 2,000 output tokens per run, that’s 200,000 tokens—roughly $2.40 on Claude’s current pricing. Compounds to $876 annually for a task that might not benefit from the extra context.

    How to decide for your specific task

    Run the same operation both ways with a sample of 10–20 items. Score the outputs on accuracy, consistency, and usefulness. Time the runs. Calculate the token cost from your provider’s API logs.

    If chunked prompts score within 5% of full-context quality but cost 40% less and run twice as fast, chunk. If full-context produces meaningfully better results—especially for tasks you run infrequently—use the full window.

    For tasks you’ll repeat weekly or daily, optimization matters. For one-off research deep-dives, throw everything at the model and move on.

    The other variable: how often you iterate. If you’re refining prompts and testing variations, smaller chunks let you experiment faster. Full-context prompts take longer to run and make it harder to isolate what changed between versions.

    Want more practical breakdowns of AI tools for online operators? Subscribe to One Two Three Send—we test this stuff so you don’t have to.

    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.

  • Stop buying productivity tools you’ll abandon in 30 days

    Stop buying productivity tools you’ll abandon in 30 days

    Stop buying productivity tools you'll abandon in 30 days
    Photo by Dmitriy Demidov on Unsplash

    Online operators waste more money on abandoned productivity tools than failed ad campaigns. The pattern is identical every time: you see a demo, imagine the workflow, subscribe to the annual plan for the discount, use it for three weeks, then revert to your old system.

    The problem isn’t the tools. It’s that most operators evaluate software by counting features instead of mapping workflows. A task manager with 47 integrations sounds impressive until you realize you only need three—and your current system already has them.

    The 30-day abandonment window

    Most SaaS products see the steepest drop-off between days 14 and 30. You’re past the onboarding dopamine hit but haven’t built enough muscle memory for the tool to feel faster than your old method. That’s when friction wins.

    The tools that stick past 30 days share one trait: they eliminate a specific pain point you experience multiple times per day. A social scheduling tool saves you from logging into four platforms every morning. A snippet expander removes the need to retype the same email three times a week. A invoice tracker stops you from hunting through Gmail for payment confirmations.

    Tools that promise to “organize your entire business” almost always fail because the pain they solve is abstract. You don’t feel disorganized multiple times per day—you feel it as a low-grade background hum. That’s not enough activation energy to change behavior.

    Run the replacement audit first

    Before you subscribe to anything new, open a text file and list every tool the new one would replace. Not the features it could replace—the tools you will actually stop paying for if you switch.

    If the list is empty, you’re adding to your stack, not replacing. That’s fine for tools under $15/month, but anything above that needs to justify itself by removing something else. Otherwise you’re paying twice for the same capability.

    Next, track how many times per week you currently experience the problem the tool claims to solve. If it’s fewer than five, the tool won’t stick. You need repeated friction to build new habits. A monthly invoicing tool might be perfect for agencies, but if you only invoice quarterly, you’ll forget the workflow between uses.

    Test with the free tier longer than you think

    Most free trials last 14 days. That’s barely enough time to import your data, let alone stress-test a workflow under real conditions. If a tool offers a free tier with limits instead of a time-boxed trial, use it for 60 days minimum.

    The goal isn’t to see if the tool works—that’s what demos are for. You’re testing whether you naturally reach for it when the problem appears. Do you open the new task manager when a client emails a request, or do you still add it to your old system and tell yourself you’ll migrate it later?

    Watch for workflow duplication. If you’re maintaining two systems in parallel “just in case,” the new tool hasn’t actually replaced the old one. That’s a strong signal it won’t stick.

    When annual plans actually save money

    Annual discounts only save money if you use the tool for the full year. Obvious, but operators ignore it constantly. A 40% annual discount on a $25/month tool means you prepay $180 instead of $300. If you abandon it at month four, you’ve spent $180 to use a $100 tool.

    The break-even point for annual plans is usually month eight or nine. If you’re not confident you’ll still be using the tool then, pay monthly. The extra cost is insurance against switching costs.

    One exception: tools with painful migration paths. If moving your email list or content library to a new platform takes more than four hours, an annual plan makes sense earlier—because the switching cost itself will keep you locked in whether you prepaid or not.

    If you’ve caught yourself opening a tool you pay for and forgetting what you use it for, reply and tell me which one. I’m tracking the most commonly abandoned categories for a future piece.

    The tools that survive past 30 days aren’t the ones with the best feature lists. They’re the ones that remove something you hated doing every single day—and make the replacement feel faster than the original from day one.

  • Most online operators track revenue wrong—it compounds

    Most online operators track revenue wrong—it compounds

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

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

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

    Cash accounting hides what’s actually happening

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

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

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

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

    Gross revenue vs. net revenue: the numbers diverge fast

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

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

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

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

    Platform dashboards lie by omission

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

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

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

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

    Why this compounds

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

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

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

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

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

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

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

  • Paid social campaign dashboards update on different schedules

    Paid social campaign dashboards update on different schedules

    Paid social campaign dashboards update on different schedules
    Photo by Nick Brunner on Unsplash

    You check your Facebook Ads Manager at 9 a.m. and see $247 spent with 18 conversions. You open Google Ads in the next tab: $198 spent, 22 conversions. LinkedIn shows $310 spent, 11 conversions. You drop the numbers into a spreadsheet to calculate blended cost-per-acquisition, then check again two hours later. Facebook hasn’t moved. Google added three conversions. LinkedIn subtracted one.

    Welcome to the reality of paid social dashboards: every platform updates metrics on its own schedule, and none of them tell you when the last refresh happened.

    Why update intervals matter

    Most operators treat ad dashboards like bank balances—refresh the page, get the current number. But ad platforms process billions of events across millions of campaigns. They batch updates, prioritize certain metrics over others, and apply attribution windows that shift as more data arrives.

    Facebook Ads Manager typically updates spend and impression data every 15 to 30 minutes during active campaign hours. Conversion data can lag by one to four hours, especially if you’re using the Conversions API or tracking offline events. If you’re running campaigns across multiple time zones, the dashboard may show blended data from different collection windows.

    Google Ads refreshes most metrics every three hours for standard campaigns. Search campaigns update faster—often within 60 minutes—because query data arrives in near-real-time. Display and YouTube campaigns batch less frequently. Conversion imports from Google Analytics 4 can take six to twelve hours to appear, and the attribution window keeps adjusting for up to 90 days if you allow data-driven attribution.

    LinkedIn Campaign Manager updates impressions and clicks roughly every two to four hours. Lead gen form fills appear faster, usually within 30 minutes, because LinkedIn processes them as first-party events. Conversion tracking via the Insight Tag can delay by four to eight hours depending on pixel fire volume and whether you’ve enabled enhanced conversions.

    When stale data breaks decisions

    The update lag becomes a problem when you’re making budget decisions across platforms in the same session. If Facebook shows yesterday’s conversion total but Google reflects this morning’s, your cost-per-acquisition comparison is measuring different time windows. You might pause a LinkedIn campaign that looks expensive at 11 a.m., only to see five conversions arrive by 3 p.m. that would have changed the math.

    One operator I spoke with pulls reporting at the same time every day—8 a.m. Eastern—and assumes all platforms are showing data through midnight. He realized Facebook was still processing West Coast conversions from 9 p.m. to midnight, so his morning snapshots undercounted by 12 to 18 percent. He shifted his reporting pull to 10 a.m. and saw the discrepancy drop to under 5 percent.

    The other trap: platforms don’t update all metrics simultaneously. Facebook might refresh spend in real time but hold conversion data for the next batch cycle. If you calculate return on ad spend while spend is current but conversions are three hours old, you’re blending live and lagged numbers.

    How to work around dashboard lag

    Set a consistent daily reporting time at least four hours after your highest-traffic period ends. If most conversions happen between 6 p.m. and 10 p.m., pull reports after 2 p.m. the next day. That gives every platform time to process overnight events and apply attribution.

    Use each platform’s bulk export or API for anything you’re putting into a spreadsheet. Exports often pull from a more complete dataset than the live dashboard. Facebook’s Ads Reporting API lets you specify a date range and attribution window; the response includes a data freshness timestamp so you know exactly when the numbers were last calculated.

    When comparing platforms, add a staleness column. Mark which metrics updated in the last hour, the last four hours, or earlier. If LinkedIn conversion data is eight hours old and Facebook’s is two hours old, flag the comparison and revisit it later rather than making a budget shift on mismatched windows.

    For operators running high-frequency tests—creative swaps, bid adjustments, audience shifts—check the platform’s API documentation for real-time vs. batched endpoints. Google Ads has a real-time reporting service for search campaigns that updates every few minutes, but it costs more in API quota. Facebook offers a similar accelerated delivery insight, though it’s not exposed in the standard dashboard.

    When to trust the dashboard and when to wait

    Spend data is almost always current within 30 minutes across all three platforms. Impressions and clicks update faster than conversions. If you need a quick pulse check on whether a campaign is delivering traffic, the dashboard is reliable. If you’re calculating return on ad spend or cost per lead, wait until the conversion window closes.

    One pattern I’ve seen work: check dashboards for pacing and delivery in the morning, but reserve budget and optimization decisions for end-of-day when conversion data has caught up. If a campaign looks like it’s burning budget without results at 10 a.m., tag it for review but don’t pause it until 5 p.m. unless spend is wildly over target.

    Platforms won’t fix this. Real-time everything is expensive to build and even more expensive to scale. The update intervals we have now are design choices that balance infrastructure cost against operator needs. Understanding the lag is part of the job.

    What’s your reporting routine? Do you pull data at the same time daily, or do you check dashboards ad hoc? Hit reply and let me know—I’m collecting operator workflows for a future piece on attribution reconciliation.

  • WordPress REST API rate limits: what trips automated workflows

    WordPress REST API rate limits: what trips automated workflows

    WordPress REST API rate limits: what trips automated workflows
    Photo: Matinbeigi via Wikimedia Commons (CC BY-SA 4.0)

    If you’re running any kind of automated workflow that touches WordPress—content sync tools, headless front-ends, analytics dashboards, social schedulers—you’re making REST API requests. And sooner or later, you’ll hit a wall you didn’t know existed.

    Most managed WordPress hosts impose rate limits on REST API calls. The problem: they rarely document the exact threshold, and when you exceed it, the failure mode is rarely obvious. Your workflow just… stops working.

    Where the ceiling sits

    Rate limits vary wildly by host and plan tier. WP Engine enforces approximately 60 requests per minute per IP on mid-tier plans. Kinsta’s limit sits closer to 100 requests per minute but drops during peak traffic windows. SiteGround and Bluehost shared plans can throttle as low as 30 requests per minute under load.

    The limit isn’t always per-account—it’s often per-IP or per-site. If you’re running multiple automations from the same server (a Zapier workflow pulling post data while a headless front-end fetches metadata), both count toward the same ceiling.

    BigScoots and similar VPS-adjacent hosts give you more headroom, but you’re still sharing infrastructure unless you’ve negotiated custom limits or moved to dedicated resources.

    How you trip it without noticing

    The most common culprit: bulk operations during content imports or migrations. If you’re syncing 500 posts from an external CMS via REST API, and each post requires three API calls (create post, upload featured image, assign taxonomy terms), that’s 1,500 requests. At 60 requests per minute, you’re looking at 25 minutes—but only if nothing else touches the API during that window.

    Social scheduling tools that auto-pull post excerpts or featured images can silently hammer the API every time they refresh your queue. Analytics plugins that log every page view via REST API can push you over the edge during traffic spikes.

    Headless WordPress setups are especially vulnerable. If your Next.js or Gatsby build process fetches all posts, taxonomies, and media in parallel during deployment, you can blow through your limit in seconds. Incremental static regeneration helps, but only if you’ve tuned the request concurrency.

    How to test your actual limit

    Most hosts won’t tell you the number until you ask support directly—and even then, the answer is often vague (“we recommend staying under 60 requests per minute”). The fastest way to find your real ceiling: controlled load testing.

    Use a tool like curl with a loop, or a lightweight Node script with axios, to hit a non-destructive endpoint (/wp-json/wp/v2/posts?per_page=1) at increasing rates. Start at 30 requests per minute, then 60, then 100. Watch for HTTP 429 responses or sudden timeouts.

    Log the exact rate where you start seeing failures. That’s your ceiling. Build in a 20% buffer—if you hit 429 at 80 requests per minute, design your workflows to stay under 65.

    The non-obvious fix

    Most developers reach for caching or request batching first. Both help, but the real leverage is in request sequencing and backoff logic.

    If you’re building a custom integration, implement exponential backoff: when you hit a 429, wait two seconds, then four, then eight. Most WordPress hosts reset rate-limit counters every 60 seconds, so a brief pause often resolves the issue without killing your workflow.

    For third-party tools (Zapier, Make, n8n), check if they expose retry settings or rate-limit handling. Zapier’s “Delay After Queue” action lets you throttle outbound requests manually. Make’s HTTP module supports custom retry logic via error handlers.

    If you’re running a headless build process, switch from parallel to sequential fetching for high-volume endpoints, or split your build into smaller incremental chunks. Gatsby’s GATSBY_CONCURRENT_DOWNLOAD environment variable lets you cap parallel requests; start at 10 and tune down if you’re still hitting limits.

    One last thing: if you’re on a managed host and legitimately need higher limits for a production workflow, ask. Most hosts will raise the ceiling for established accounts with predictable traffic patterns—but only if you ask before you break something.

    Have a question about WordPress infrastructure or automation limits? Reply to this email—we cover reader questions every Sunday.

  • Lemon Squeezy’s webhook signature verification: how it works

    Lemon Squeezy’s webhook signature verification: how it works

    Lemon Squeezy's webhook signature verification: how it works
    Photo: Iamovichowdhury via Wikimedia Commons (CC BY-SA 4.0)

    If you’re running a paid newsletter, course platform, or membership site on Lemon Squeezy, you’re probably listening for webhooks to provision access, update subscriber status, or log revenue events. But every webhook Lemon Squeezy sends includes a signature header that most operators ignore—and that’s a problem.

    Webhook signature verification isn’t optional security theater. It’s the only way to confirm that the POST request hitting your server actually came from Lemon Squeezy, not a malicious actor replaying old payloads or inventing fake subscription events.

    Here’s how Lemon Squeezy’s signature system works, how to validate it in your code, and one non-obvious edge case that breaks verification even when you’ve done everything right.

    How Lemon Squeezy generates the signature

    Every webhook Lemon Squeezy sends includes an X-Signature header. This is an HMAC-SHA256 hash of the raw request body, signed with your webhook signing secret.

    You’ll find your signing secret in the Lemon Squeezy dashboard under Settings → Webhooks. It looks like a long alphanumeric string starting with whsec_. Each webhook endpoint you create gets its own secret—if you rotate or delete an endpoint, the secret changes.

    The signature process is straightforward: Lemon Squeezy takes the entire JSON payload, hashes it using HMAC-SHA256 with your secret as the key, then sends the resulting hash in the header. Your server’s job is to recreate that hash and compare it to what was sent.

    Validating the signature in your code

    Here’s a minimal Node.js example using Express and the built-in crypto module:

    const crypto = require('crypto');
    const express = require('express');
    const app = express();
    
    const WEBHOOK_SECRET = process.env.LEMON_SQUEEZY_SECRET;
    
    app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
      const signature = req.headers['x-signature'];
      const payload = req.body;
    
      const hash = crypto
        .createHmac('sha256', WEBHOOK_SECRET)
        .update(payload)
        .digest('hex');
    
      if (hash !== signature) {
        console.error('Invalid signature');
        return res.status(401).send('Unauthorized');
      }
    
      // Signature valid—process the webhook
      const event = JSON.parse(payload);
      console.log('Event type:', event.meta.event_name);
    
      res.status(200).send('OK');
    });
    

    Two things to note: you must use express.raw() to preserve the raw request body. If you parse the JSON first with express.json(), the signature won’t match—whitespace, key order, and encoding all matter. You also need to compare the hash as a hex string, not a buffer.

    If you’re using PHP, the process is similar with hash_hmac(). Python uses hmac.new() from the standard library. The algorithm is the same across languages.

    The edge case: timestamp drift and replay attacks

    Lemon Squeezy doesn’t include a timestamp in the signature itself, which means a valid webhook payload can be replayed indefinitely. If an attacker intercepts a legitimate webhook—say, a subscription cancellation event—they can resend it to your endpoint days or weeks later, and your signature check will pass.

    The mitigation is to track processed webhook IDs. Every Lemon Squeezy webhook includes a unique meta.webhook_id field in the payload. Before processing the event, check whether you’ve already seen that ID. If you have, return a 200 status but skip the business logic.

    Store processed IDs in a database table with a TTL—30 days is reasonable. This prevents replay attacks without requiring you to implement a sliding timestamp window.

    Here’s the addition to the earlier example:

    const event = JSON.parse(payload);
    const webhookId = event.meta.webhook_id;
    
    // Check if already processed (pseudo-code)
    if (await db.webhookExists(webhookId)) {
      console.log('Duplicate webhook, ignoring');
      return res.status(200).send('OK');
    }
    
    await db.saveWebhookId(webhookId);
    // Continue processing...
    

    When verification fails even though it shouldn’t

    If your signature check is failing consistently and you’re confident the secret is correct, check your server’s request body size limit. Some frameworks—Express included—default to a 100kb limit. Lemon Squeezy’s order_created webhooks can exceed that if the order includes multiple line items or custom data fields.

    Increase the limit in your middleware config: express.raw({ type: 'application/json', limit: '1mb' }).

    The other common culprit is proxies or load balancers that rewrite the request body. If you’re behind Cloudflare, Nginx, or AWS ALB, confirm that the raw body is being forwarded unmodified. A single trailing newline or whitespace change will break the hash.

    Lemon Squeezy doesn’t currently offer a signature verification test mode or sample payloads with pre-signed signatures, so the easiest way to debug is to log both the computed hash and the received signature, then compare them character by character.

    If you’re handling payments or provisioning access via webhooks, signature verification isn’t optional. It’s the only way to distinguish legitimate events from forged requests. Set it up once, test it with a live webhook, and add replay protection if your product handles high-value transactions.

    Got a question about webhook security or payment automation? Reply to this email—we cover this stuff every week.

  • Substack’s custom domain setup: DNS propagation vs. SSL timing

    Substack’s custom domain setup: DNS propagation vs. SSL timing

    Substack's custom domain setup: DNS propagation vs. SSL timing
    Photo: F1jmm via Wikimedia Commons (CC BY-SA 3.0)

    When you point a custom domain at Substack, the platform tells you it can take “up to 24 hours” for everything to work. That’s technically true, but misleading—because your domain will resolve to Substack’s servers long before the SSL certificate provision finishes.

    This creates a specific problem: readers who visit your new domain during the gap see either a certificate warning or a “not secure” flag in their browser. If you’re migrating from a self-hosted archive or another platform, that window matters more than Substack’s docs suggest.

    What actually happens during the setup window

    Substack’s custom domain process has two distinct phases that don’t finish at the same time.

    First, DNS propagation. You add a CNAME record pointing your subdomain (usually newsletter.yourdomain.com) to subdomain.substack.com. Most DNS providers push that change globally within 5–15 minutes. Once it propagates, requests to your domain resolve to Substack’s infrastructure.

    Second, SSL certificate provisioning. After Substack detects the CNAME, it requests a Let’s Encrypt certificate for your domain. This involves a challenge-response validation that can take anywhere from 10 minutes to several hours, depending on Let’s Encrypt’s queue and how aggressively Substack’s automation retries.

    The gap between the two creates the problem. Your domain is live and serving pages—but without a valid HTTPS certificate. Browsers warn users. Email clients flag links. If you announce the migration before SSL finishes, a portion of your audience sees errors.

    How long the SSL delay actually lasts

    In practice, most Substack custom domains finish SSL provisioning within 90 minutes of DNS propagation completing. I’ve migrated eight newsletters to custom domains on Substack since early 2025; six took under an hour, one took three hours, and one required a support ticket after stalling for 12 hours.

    The stall case was a domain that had previously pointed to another newsletter platform. Substack’s system appeared to cache an older validation attempt and didn’t retry automatically. Support cleared it manually within 20 minutes of the ticket.

    Substack doesn’t expose provisioning status in the dashboard. The settings page shows “DNS configured” as soon as the CNAME resolves, but there’s no separate indicator for “SSL active.” You have to test the domain in a browser and check for the padlock.

    The non-obvious timing trick

    Set up your custom domain at least 48 hours before you announce the migration.

    Substack allows you to configure a custom domain without switching your publication’s primary URL immediately. Once the CNAME is added and SSL finishes provisioning, the custom domain works as an alias—readers can visit it, links resolve correctly, but your posts and emails still use the yourname.substack.com address.

    This gives you a clean testing window. Verify SSL, check that archive links work, confirm that your website’s nav links resolve correctly. Then switch the primary domain in Substack’s settings when you’re ready to announce.

    The switch itself is instant. Substack updates all post URLs and sets up 301 redirects from the old subdomain to the new domain. Emails sent after the switch use the custom domain in from-addresses and link URLs.

    One edge case: if you’re moving from another platform entirely (say, WordPress or Ghost) and pointing an existing domain at Substack, you lose the testing window. The CNAME change is live immediately, so there’s no way to preview without breaking the old site. In that case, either accept the SSL gap or set up the Substack publication on a temporary subdomain first, migrate content, then switch the CNAME during a low-traffic window.

    What to tell readers during the gap

    If you’re migrating during business hours and can’t wait out the SSL delay, send a heads-up email before you flip the CNAME. Something like: “We’re moving to [new domain] today. If you see a security warning in the next hour, that’s expected—refresh in 15 minutes and it’ll clear.”

    Most readers won’t visit during the gap, but the ones who do will appreciate knowing it’s temporary. Browser warnings make people assume they’ve been phished; a short explanation prevents that panic.

    For newsletters with large back catalogs, check your most-linked posts after SSL provisions. Substack’s redirects handle yourname.substack.com/p/post-slug to newsletter.yourdomain.com/p/post-slug correctly, but if you’ve previously shared links with UTM parameters or other query strings, test a few samples to confirm they resolve.

    One Two Three Send publishes operator-focused breakdowns like this one every day. Subscribe to get them in your inbox, or reply with questions about your own custom domain setup—we’ll cover edge cases in a future Q&A piece.