Build1 publisher3 min readPublished
An HMAC-signed cookie and a license key carry all of Keyword Brief's billing state
Keyword Brief meters its free tier in a signed cookie and keeps paid entitlement in a Lemon Squeezy license key activated straight from the checkout URL. Only one of the three bugs the developer hit came from that design.
The Engineer · Build desk

What happened
- Keyword Brief takes Lemon Squeezy subscriptions with no signup, no password reset and no user table, metering its free tier at three checks per browser inside an HMAC-signed cookie.
- The Lemon Squeezy confirmation button carries the license key into an /activate URL, and that page registers the browser as an instance of the key and strips the key out of the address bar.
- The first paid test purchase was rejected because the code called /validate and refused any key not already marked active, and every freshly issued Lemon Squeezy key starts out inactive.
- A zod schema capping outline points at four failed about one request in three, because structured outputs enforce the shape of a response and not the length of its arrays.
- DataForSEO's depth setting of 10 counts videos, People Also Ask blocks and image packs as elements, so six organic links were being described to users as a full top 10.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- cost Each cookie clear buys three more free checks at roughly $0.30 of DataForSEO and Claude calls, and the operator absorbs that bill to keep signup at zero steps.
- constraint The only route back into a paid browser is the license key in the Lemon Squeezy receipt, which makes the payment vendor's email deliverability the account recovery system.
- decision Anyone copying this weighs a three-call license state machine against building auth, and the calls have to run in order or paying customers get turned away at the door.
- capability Refunds and cancellations switch Pro off within the hour without webhook handling, because the server re-checks the stored key against the vendor on its own schedule.
The entitlement path is three Lemon Squeezy calls in a fixed order [15]:
1. `/activate` the first time a key appears in a browser. This flips the key to `active` and counts that browser against the device limit. 2. `/validate` with the `instance_id` on every check after that. 3. `/deactivate` when the user removes the key, which frees the slot.
Getting that order wrong produced the first bug. The code called `/validate` and treated anything other than `active` as a failure, so it turned away every brand-new paying customer [14]. The first one it turned away was the developer's own test purchase, which came back "This key is inactive" [13].
The cookie is the session record. It holds the key and the instance id, signed: `cookie = sign(`${key}|${json.instance.id}`)` [9]. Nothing else remembers the customer [1].
A fresh key reads 0/5, so one subscription covers five browsers [13]. Losing the cookie loses the instance id, and the described flow only calls `/deactivate` when a user deliberately removes a key, so a cleared browser leaves its slot spent [3]. Five clears and the key has nowhere left to activate [3].
At roughly $0.10 a check against $19.90 a month, a subscriber has to run 199 checks before the provider bill catches the subscription price [3][4][1]. That is the headroom the free counter leaks into: someone can clear cookies and get three more free checks [12]. "For a $19.90 tool that's a trade I'll take in exchange for zero signup friction," the developer wrote on dev.to [11].
The gate that carries real money sits on the second request. The Claude outline runs on its own endpoint, which refuses to execute without a short-lived, keyword-bound token issued by the first request; without that, the free counter could be skipped by calling the outline directly [10].
Two of the three bugs the post names come from provider APIs, not from dropping accounts [4]. The zod fix was to stop validating what the model does not guarantee: structured outputs constrain the shape of a response and not the length of its arrays, so the limits moved into the prompt and the code now trims with `slice(0, 9)` on sections and `slice(0, 5)` on points [17][18]. The DataForSEO fix was to over-fetch and filter: ask for depth 30, keep `type === "organic"`, take the first ten [20].
This pattern transfers to a product that is one stateless request wide, priced below the friction of a signup form; Ahrefs and Semrush, the tools the post compares against, start at $130 or more a month [22]. It runs on Next.js on Vercel with DataForSEO, a `fetch` and cheerio pass over the ranking pages, Claude for the outline, and Lemon Squeezy for the money [21]. The best engineering in it is in the empty-result path. When a phrase returns no data at all, as the author's test input "how to fishing well" did, Claude rewrites it into four to six natural queries, one batch call verifies those against DataForSEO, and the page shows fishing tips at 1,600 and how to catch more fish at 140 [23].
What to watch
- Whether the five-instance ceiling starts generating support mail as paying users switch or clear browsers.
- Whether Lemon Squeezy keeps the [license_key] variable in confirmation button links, since one-click activation depends on it.
- Whether the keyword-bound token holds against a script that farms outlines through the first request.