<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: RankLoop</title>
    <description>The latest articles on DEV Community by RankLoop (@rankloop).</description>
    <link>https://dev.to/rankloop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4017366%2F1ad7345d-1a60-4c69-af8a-66727b44a5f9.png</url>
      <title>DEV Community: RankLoop</title>
      <link>https://dev.to/rankloop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rankloop"/>
    <language>en</language>
    <item>
      <title>I'm building a $19 rank tracker in public — the stack, and why "daily" is harder than it sounds</title>
      <dc:creator>RankLoop</dc:creator>
      <pubDate>Mon, 06 Jul 2026 09:05:02 +0000</pubDate>
      <link>https://dev.to/rankloop/im-building-a-19-rank-tracker-in-public-the-stack-and-why-daily-is-harder-than-it-sounds-43fc</link>
      <guid>https://dev.to/rankloop/im-building-a-19-rank-tracker-in-public-the-stack-and-why-daily-is-harder-than-it-sounds-43fc</guid>
      <description>&lt;p&gt;I do SEO for my own side projects, and for years I bounced between rank trackers. The expensive ones (Ahrefs, Semrush) are overkill when all I want is "did my 200 keywords move today?". The cheap ones had two recurring problems that drove me up the wall:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"Daily" that wasn't daily.&lt;/strong&gt; A lot of budget trackers quietly refresh every 2–5 days and still call it daily. When you're reacting to a Google update, that lag is the whole game.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Billing friction.&lt;/strong&gt; Cancel-by-email-only, surprise charges after you thought you'd stopped, keyword counts that double-count mobile vs desktop so your "250" is really 125.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I started building the thing I actually wanted. It's called &lt;a href="https://getrankloop.com" rel="noopener noreferrer"&gt;RankLoop&lt;/a&gt;, and this is the build log — the stack, the decisions, and the unglamorous parts that turned out to matter most.&lt;/p&gt;

&lt;p&gt;I'm writing this at the &lt;em&gt;start&lt;/em&gt;, not after some victory lap. No revenue screenshots here. It's live, the free plan is open, and I'm looking for people to poke holes in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one design decision everything else hangs off
&lt;/h2&gt;

&lt;p&gt;Most trackers store your position for a keyword: "you're #7." I decided to store the &lt;strong&gt;entire top 10 of every search, on every check.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That sounds like a lot more data (it is), but it's the feature I always wished I had: when you drop from #4 to #7, you can look back and see &lt;em&gt;exactly which page moved in above you and when&lt;/em&gt;. Ranking is relative — knowing "I dropped" is useless without "because these three competitors climbed."&lt;/p&gt;

&lt;p&gt;Concretely, each check writes a row with the keyword, your position, and a JSON snapshot of the top 10 (URL + title + position). History becomes a time series you can diff. It costs storage, but storage is cheap and the insight isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack (and why, as a solo dev)
&lt;/h2&gt;

&lt;p&gt;Nothing exotic. The bias was: fewer moving parts, fewer things that can page me at 2am.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16 on Vercel.&lt;/strong&gt; App Router, route handlers for the API, one deploy target. As a team of one, "git push and it's live" is worth a lot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; for Postgres + Auth + &lt;strong&gt;Row Level Security&lt;/strong&gt;. More on RLS below — it's doing more work than any other single piece.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lemon Squeezy&lt;/strong&gt; as the billing layer. This one deserves a paragraph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend&lt;/strong&gt; for transactional email (alerts, welcome, support). This one deserves a &lt;em&gt;cautionary&lt;/em&gt; paragraph.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why a Merchant of Record instead of Stripe directly
&lt;/h3&gt;

&lt;p&gt;If you sell software to people in the EU as a solo founder, you inherit VAT/tax obligations that are genuinely miserable to handle correctly. A Merchant of Record (Lemon Squeezy, Paddle) becomes the seller of record and handles that global tax mess for you. You trade a bit of margin for not having to become a part-time tax compliance department. For a one-person shop that trade is a no-brainer.&lt;/p&gt;

&lt;p&gt;It also let me make cancellation &lt;strong&gt;self-serve from the dashboard in one click&lt;/strong&gt; — which, given that billing traps were half my motivation, felt non-negotiable.&lt;/p&gt;

&lt;h3&gt;
  
  
  RLS is the whole security model, not a nice-to-have
&lt;/h3&gt;

&lt;p&gt;Every table that holds user data (domains, keywords, checks, reports) has Row Level Security policies so a user can only ever read/write their own rows — enforced in the database, not in my application code. The app could have a bug; the database still won't hand user A's keywords to user B.&lt;/p&gt;

&lt;p&gt;The subtle bit: the columns that decide a user's &lt;em&gt;plan and billing status&lt;/em&gt; live on the profile row, and those are only writable by the service role (the billing webhook), never by the user's own session. Otherwise anyone could &lt;code&gt;PATCH&lt;/code&gt; their own row to "plan: pro" and give themselves the paid tier for free. If you're building multi-tenant on Supabase, audit that specific thing — the gap between "RLS is on" and "RLS is actually scoped correctly" is where the bugs live.&lt;/p&gt;

&lt;h3&gt;
  
  
  The email deliverability rabbit hole
&lt;/h3&gt;

&lt;p&gt;Here's the one that ate a full day and taught me the most.&lt;/p&gt;

&lt;p&gt;Transactional email "works" in dev instantly, so you assume it's fine. Then you realize your first emails went out from a shared sandbox sender and landed straight in spam — because they weren't authenticated for your domain. Fixing it meant actually understanding the three records everyone waves at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SPF&lt;/strong&gt; — which servers are allowed to send for your domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DKIM&lt;/strong&gt; — a cryptographic signature proving the mail wasn't forged in transit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DMARC&lt;/strong&gt; — what receivers should do when SPF/DKIM fail, plus reporting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get those set up on your sending subdomain &lt;em&gt;before&lt;/em&gt; you send a single real email, or you'll poison your domain reputation with your own test messages. Domain reputation is slow to build and slower to repair. I learned this the annoying way so you don't have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it actually is right now
&lt;/h2&gt;

&lt;p&gt;Honest status:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free plan: 10 keywords, checked weekly, no card.&lt;/li&gt;
&lt;li&gt;Paid: $19/mo for 250 keywords daily across 5 domains; $49/mo for 1,000 keywords daily.&lt;/li&gt;
&lt;li&gt;Full top-10 history, one-email alerts (only when a keyword enters/leaves the top 10 or crosses your threshold — not daily noise), shareable read-only report links for clients, CSV export.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it is &lt;em&gt;not&lt;/em&gt;: a keyword research tool, a backlink index, or an all-in-one suite. It tracks Google positions reliably and gets out of your way. That's the whole pitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd love feedback on
&lt;/h2&gt;

&lt;p&gt;If you track rankings for your own projects or clients, I'd genuinely like to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is stored top-10 history something you'd use, or do you only care about your own number?&lt;/li&gt;
&lt;li&gt;What's the smallest feature that would make you switch from whatever you use now?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can try the free plan (no card) at &lt;strong&gt;&lt;a href="https://getrankloop.com" rel="noopener noreferrer"&gt;getrankloop.com&lt;/a&gt;&lt;/strong&gt; and tell me where it falls short. I'm building this in the open, so critical feedback is the useful kind.&lt;/p&gt;

&lt;p&gt;More build-log posts to come — next one is probably the scheduling layer that makes the daily checks actually fire on time, which was less trivial than I expected.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>saas</category>
      <category>nextjs</category>
      <category>indiehackers</category>
    </item>
  </channel>
</rss>
