DEV Community

George Kioko
George Kioko

Posted on

How I lost $540/month in 30 days to silent user churn (and didn't notice)

Last week my 30 day profit dropped from $268 to $50 and I assumed it was a bug in the dashboard.

It wasn't a bug. It was me not paying attention for 29 days straight.

This is a postmortem of how I lost roughly $540/month from two agency buyers who just quietly stopped running my actor on March 25. I found out on April 23. That's 29 days of ambient denial while every other part of my portfolio was also quietly rotting.

Writing this partly so I remember the lesson, partly because I know at least three other Apify devs are about to make the same mistake.

The numbers

I run a bunch of scrapers and APIs on Apify. Here's what the revenue split actually looked like in early April before things went sideways.

Actor Users Monthly revenue % of total
Google Maps Lead Intel 2 $540 83%
LinkedIn Employee Scraper 37 $42 6.5%
YouTube Transcript 40 $28 4.3%
Google Scholar 18 $14 2.1%
Email Validator API 46 $11 1.7%
Website Intelligence API 22 $8 1.2%
Everything else (5 actors) 188 $7 1.1%
Total 353 $650 100%

Read that first row again. Two users. $540. More than the other 351 users combined, by a factor of like 7x.

I told myself this was fine because the product was working and the buyers were happy. Both things were true in early March. Neither was true by late March. I just didn't know.

The silence

March 25 was the last run either agency executed. I didn't flag it because nothing explicit broke. No error email, no angry message, no refund request. My Apify dashboard just showed fewer runs but the rolling 30 day number still looked okay because it was still averaging in the fat weeks from before.

Here's roughly how the number moved:

  • Early April: $268 rolling 30d profit. I'm feeling smug.
  • Mid April: $92. I figure maybe it's a slow week.
  • April 22: $50. I finally open the run logs.

By the time I looked, the last run from either agency was 29 days ago. Whatever issue they had (I still don't fully know), they decided it wasn't worth telling me about. They just left.

Agency users don't complain. They just stop paying. If you're building for them, burn that into your forehead.

What I found when I actually looked

This is the part that made me feel physically ill. Once I started doing a proper audit of my portfolio, I found that 6 of my other 10 monetized actors were broken in some way. Not all catastrophic. Some were returning partial data. One had a silently failing selector from a site redesign in February. One was charging $0 per run because of a broken Actor.charge() signature I'd introduced in a refactor.

Let me repeat that: I had actors that were executing successfully, returning data to users, and billing them exactly nothing. For weeks.

If one of those 2 agencies had tried a second actor of mine during that period, they'd have gotten rot. That's probably why they didn't come back.

The root cause wasn't the bugs though. Bugs happen. The root cause was that my attention was entirely on the $25/run cash cow because it was paying the bills. The cash cow was hiding the state of the herd.

Why 2 agency users paid more than 353 devs

I want to sit with this one because I think most solo devs misunderstand it.

My Google Maps Lead Intel actor charges $25 per successful run. It scrapes a geography, enriches each business with a website audit, scores them, and hands the agency a ranked list of cold outreach targets. One agency was running it on a schedule against 40 US cities per week. At $25 a pop, that's serious money.

The 353 devs on my other actors were paying $0.003 to $0.01 per row. They're hobbyists, students, one guy building a thesis scraper. They're lovely. They're also economically irrelevant to whether I can pay rent.

Two lessons fell out of this.

First, your paying users and your popular users are almost never the same people. Popularity on Apify Store is a vanity metric. Agency retention is the only metric that buys groceries.

Second, concentrated revenue is fragile in ways that only hurt you once. When I had 2 whales, my revenue was 83% dependent on their mood. The moment either whale left, my month was destroyed. Worse, because they paid so much, I built no alerting around them. I assumed I'd notice. I did not notice.

If you're reading this as an agency owner looking for scraping tools, what you actually want is a vendor who is not dependent on you. Someone with 50 paying clients will answer your ticket faster than someone with 2, because the guy with 2 is terrified of you and therefore weirdly slow to respond to bad news.

What I'm changing

Not writing a manifesto. Just the four things I'm doing this week.

Push alerting on every run. Apify has webhooks. I never wired them up because my dashboard was enough. It wasn't. Every successful run, every failed run, every billing event now pings a private Telegram channel I actually read. Here's the whole snippet, it's embarrassingly short:

// webhooks config in actor.json points at this endpoint
// payload is whatever Apify sends plus the run metadata
export async function notify(req, res) {
    const { eventType, eventData, resource } = req.body;
    const text = `[${eventType}] ${resource?.actId} run ${resource?.id}\n`
               + `status: ${resource?.status}\n`
               + `charged: $${resource?.usageTotalUsd ?? 0}`;
    await fetch(`https://api.telegram.org/bot${TG_TOKEN}/sendMessage`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ chat_id: TG_CHAT, text }),
    });
    res.status(200).end();
}
Enter fullscreen mode Exit fullscreen mode

That's it. If I'd had this on March 25, I'd have noticed the absence of runs within 48 hours, not 29 days. If you run anything that bills users, stop reading and go wire this up. Seriously.

Weekly self test of every monetized actor. Every Sunday I run each of my paid actors against a known input and diff the output against last week's. If the schema changes or the row count collapses, I know before the user does. This is stupid simple and I should have been doing it from day one.

Diversifying the buyer pool. $25/run is staying. But I'm actively building out the $5 to $10 tier with two new actors targeted at small agencies, because I want the bottom of the revenue chart to be less wobbly. Ten $50/month buyers survive any one of them leaving. Two $270/month buyers don't.

Public status page. Still building this but the idea is: if an actor is degraded, the user knows before they hit it. Trust compounds and I just burned a chunk of it, so I'm paying interest now.

Close

If you run any product that bills users on autopilot, go wire a Telegram or Slack webhook today. Not tomorrow. The 30 day rolling dashboard lies to you when things trend down because it's still averaging in good weeks. Push alerts don't lie. Runs either happen or they don't.

I'm writing this mostly for me. But if you want to see what the portfolio looks like now, or hire the actor that caused all this drama in the first place, it's here:

  • Apify profile: https://apify.com/george.the.developer
  • Google Maps Lead Intel (the $25/run one): on the same profile
  • Everything else I've shipped (27 public actors, some broken last week, all fixed now): same profile

Ask me anything in the comments. Especially if you're an agency buyer thinking about pulling the trigger on a vendor. I have thoughts about what you should actually be looking for.

Top comments (0)