I Found 289 Invoicing Signals and Replied to Zero

What I learned building a small demand listener, breaking my own fetcher, and testing whether useful context matters more than finding people with a problem.

1. Why I built a listener at all

The advice is everywhere: go where the pain is. Fine as a direction, but not very useful as a coordinate. I knew roughly where to look. I wanted to know what was actually there. I build invoicing software for freelancers, so the pain I care about has a vocabulary: chasing unpaid invoices, cancelling subscriptions, tracking bills in spreadsheets, wondering whether a quiet client will ever pay. I wanted to know how often that vocabulary appears in public, right now, in rooms I don't belong to. Not to pitch anyone. To learn whether I could find it at all, and what it looks like at full length instead of as a headline. So I built a small listener and let it run for a week.

2. What the listener actually was

Five subreddits via RSS. Seven Hacker News search phrases, quoted so they match real wording instead of my imagination of it. Five keyword groups: pain ("chasing invoices", "client hasn't paid"), churn ("FreshBooks too expensive", "cancel my invoicing subscription"), privacy ("offline invoicing", "own my invoice data"), spreadsheets ("excel invoice tracking"), and plain asks ("what do you use for invoices"). Every hit lands in a local SQLite file. Twelve sources in total. No keys, no accounts, no private data; public feeds and public search only. One run per day, with delays between requests so the listener behaves like a guest, not a flood. Even so, four of five subreddits answered with HTTP 429 at an 8-second delay. Public patience is thinner than my curiosity.

3. Week one: the numbers

12 sources. 289 raw keyword matches. 4 of 5 subreddits rate-limited even at an 8-second delay. 232 of the 289 snippets truncated at 300 characters — by a bug in my own fetcher, not by the sources. After I fixed the bug and re-ran, 9 conversations survived with full context. And of those nine, zero were conversations I felt I could enter with a reply that wasn't a pitch in disguise.

289 → 232 broken snippets → 9 readable conversations → 0 replies. That chain is the whole story, and each arrow is a different failure. The first arrow is my own code: I broke most of the context before I read it. The second arrow is what raw matches actually are: after the fix, only nine rows were conversations with a beginning, a middle, and a question. The third arrow is judgment: even with full context, none of the nine gave me a legitimate way in. Only one of those failures is about the market. The other two are about me.

4. The correction: signals, not complaints

I initially called the 289 "complaints." That's too generous, and I want the record corrected here. They were keyword matches: rows where someone's wording crossed my keyword groups. Some were real pain. Some were jokes, quotes, templates, or someone arguing about someone else's invoice. I cannot even give you the ratio, because my own bug destroyed most of the context before I read it.

Match count is not demand. It is raw material. I skipped the part where I actually checked what the matches meant. A counter that goes up is not evidence of anything, except that your keywords exist in the world.

5. The nine

After the fix, nine conversations survived with full context. Here are three examples, paraphrased and stripped of identifying details. One: a freelancer waiting on an unpaid invoice, client gone quiet, asking how long others wait before sending the second follow-up. Real stakes, real question, real room. Another: someone choosing between a spreadsheet and a cheap subscription, mostly worried about losing history when the tool changes. A third: a person cancelling a subscription after a price rise, angrier at the lock-in than at the money.

All three were exactly the kind of problem I was looking for. And none of them were mine to answer. I had no history in those rooms. My only credential would have been my product, and that is the credential that reads as spam.

6. Why zero replies was the only honest answer

A stranger dropping a product into a pain thread usually reads as spam. A member with a useful answer reads differently. The difference is not disclosure wording or a softer landing page. It is weeks of useful answers before you have anything to sell.

I checked each of the nine against a two-step test I later wrote down formally: can I answer fully without mentioning my product, and did this person actually ask for a tool? The first answer was usually yes. The second was no, nine times out of nine. Nobody asked for a tool. So nothing was shareable, and silence was the only reply I could defend out loud. I could have replied "I built an offline invoicing tool that..." Technically relevant. Also exactly the reply everyone in those rooms has learned to ignore.

Zero is not a failure of nerve. It is the test working as designed.

7. The hypothesis: earned context over discovery

The practical lesson was simpler: getting more matches wasn't helping if I couldn't tell which conversations were worth entering. The listener was good at finding things. It was much worse at helping me decide what deserved my time. My bottleneck was never finding pain; it was triage, and then context.

So the hypothesis I now test is narrower and harder: discovery isn't the scarce resource. Earned context is. In practice that means being useful in one room for long enough that people know your answers before they know your product. That hypothesis is cheap to state and expensive to test, which is why it needs a timed experiment instead of another opinion post.

8. The mirror

The same week the listener collected its 289 matches, my own post about publishing my product's data format before the product exists got zero comments on the same platform. Read those two facts together and they say something uncomfortable: I can find the rooms. I just haven't given people a reason to listen to me there yet. Discovery may not be my problem on that platform. Presence might be. The listener measured the first half of that question. The four-week experiment measures the second half. I wrote about the format decision in why I published the format before the product because it is the same ownership argument from the data side: proof you can check beats promises you have to trust.

9. What the thread changed

I published the experiment as a short post on Indie Hackers, with one binary metric: how many conversations naturally lead someone to ask what I'm building. The thread is here. Within a day, three founders pointed out that the binary count misses an earlier signal: whether a conversation actually deepens. WorkForce2020 asked for depth signals — clarifying the problem, describing a workaround, returning with a sharper question — as proof of access before any product interest. Third_Eye_Cyborg made the reply itself the unit of learning: grade each response, then compare which grades produce follow-up. Ultimateaviator turned "no pitch" from an intention into a two-step gate I can fail.

So the experiment gained a second layer, defined in public after launch rather than before: reply grade (purely helpful, clarifying question, or concrete next step), conversation depth (they clarified, they described a workaround, they returned sharper), and the two-step no-pitch gate from section six, now written down as a rule I can fail. This page is therefore a case study in progress, not a retrospective. The metrics evolved in the open because the community saw a blind spot I did not. I would rather ship that correction visibly than pretend the first design was final.

10. Listener v2: what changes

Three changes, in order of importance. Triage before volume: use whatever signal is available to decide which hits deserve full-context fetching, instead of treating every keyword match equally; v1 fetched everything and read nothing, which is the expensive way to learn that fetching is not reading. Own the failure surface: the truncation bug was mine, so v2 gets a context-integrity check — a snippet that arrives truncated is flagged and quarantined, not silently stored next to good data. Log the second layer: every reply I write during the experiment gets a grade and a depth note at write time, plus a one-line post-hoc note after I can see what landed.

Nothing here is clever. It is just refusing to repeat a mistake I already paid for, and writing down the metrics before the results arrive so I cannot tune them to flatter me.

11. What happens next

The four-week window ends on October 20, 2026. On that date this page gets a dated update: the primary count, the secondary metrics, what actually happened in the room, and what listener v2 learned from the wreckage of v1. If the count is zero, zero gets published, and the update says what zero means and what it does not mean. I will name the community in that update, not before; naming it now would turn a listening experiment into a publicity stunt for the room.

Until then I answer questions where they are asked, keep the product out of the conversation, and let the numbers arrive at their own speed. A listener that cannot tell you where to sit is a counter. The point of this whole exercise is to stop building counters.

Related: LockMargin Manifesto →

Vlad Shiyan, founder of LockMargin

About the Author

Vlad Shiyan — Founder & Developer, Kharkiv, Ukraine. Building LockMargin since December 2025. Offline-first invoicing for freelancers who are tired of subscriptions. Standard is $49 one-time. Read more about Vlad →

Back to top ↑