Deepmox / Programming

learning path

every-app/open-seo

Open source alternative to Semrush and Ahrefs

5 chapters 0 audio lessons Article-first 3 free previews Fresh topic

Start here

1. The_Day_The_SEO_Bill_Arrived

The Day the SEO SaaS Bill Arrived

A $7,200-a-year habit died in 2026. The open-source SEO tools that replaced it are not cheaper Semrush — they are a different product, designed around an AI agent instead of a human dashboard.

Key Takeaways

  • The "expensive" SaaS SEO stack (Semrush + Ahrefs + a couple of long-tail add-ons) typically runs a small agency $400–$700 a month per seat, or roughly $7,200 a year per analyst.
  • A new generation of open-source tools — OpenSEO, SerpBear, SEONaut, LibreCrawl, SEOMachine — replaces most of those seats at $50 minimum top-up + pay-as-you-go data, often under $30 a month for a single user.
  • The interesting part is not the bill. The interesting part is the architecture: Cloudflare Workers, D1, R2, an MCP server, and pre-built agent skills. The bill is a side effect of a different product decision.
  • This series is a teardown of every-app/open-seo, the most ambitious of the new tools — the one that ships a hosted product *and* self-host, *and* an MCP server, *and* seven agent skills — and what its decisions mean for the rest of the category.

---

I watched the GitHub star counter on every-app/open-seo cross 2,000 in May 2026, and I thought about the first time I paid for Semrush. It was 2017. I was a freelance technical writer with a vague SEO side hustle, and the price felt reasonable: $99 a month, all the data I could want, a UI that taught me what a SERP was. I renewed for eight years. By the time I cancelled in 2025 I was paying $449 a month for a "Guru" seat, and I had not logged into the site in eleven months — Claude was doing the SERP lookups for me, the way I once did them by hand.

That gap is the story. The bill kept climbing. The product kept receding into the background. The dashboard stopped being where the work happened.

I went looking for a replacement and ended up reading the source of a tool I had never heard of, with a README that opens with the words "Open source alternative to Semrush and Ahrefs." Twenty-eight stars turned into 2,000 in a year. The Discord is busy. The maintainer replies to issues in hours. The DataForSEO cost table in the README is printed in plain numbers, not in "contact sales."

What I want to do in this series is treat OpenSEO as a case study in what a real, working, open-source SEO tool looks like in 2026 — not because it is the only one, but because it is the most opinionated. The opinion is: stop trying to be Semrush. Be the workflow an AI agent already wants to do. Ship the dashboard as an inspector for what the agent did, not as the place where the work happens.

A quick map of what I am going to argue across the next four chapters.

Chapter 1 is the cost economics. The headline is misleading: "open source" sounds like "free," but open-source SEO tools still pay for data, and the cheapest path is not always the one you expect. Iceland — a country DataForSEO Labs does not cover — costs 96 credits per keyword research call, three times the standard 32. I will walk through the act

6m / Article + audio

2. The_Real_Cost_of_Free

The Real Cost of Free

"Open source" is not a price tag. The cheapest way to use OpenSEO is $1 in free DataForSEO credit and a free GitHub account. The most expensive way is the same code, the same UI, and a $400-a-month SERP bill. The interesting question is not which one is cheaper. It is which one is *yours*.

Key Takeaways

  • The DataForSEO pricing table in the OpenSEO README is the most honest pricing page in SEO software. It is also a small, quiet rebuke of every "contact sales" page in the industry.
  • At the project's defaults, one keyword research call costs 32 credits (~$0.025) without clickstream, 64 with it. The 2× cost is the cost of refined volume numbers. The standard volume is the same Google-Ads-derived number every other tool shows.
  • Iceland and ~47 other countries are not covered by DataForSEO Labs. OpenSEO routes them to the Google Ads Keywords Data endpoint at a flat 96 credits per call, no difficulty, no intent, no SERP features. The README's "Iceland problem" is the cleanest example of a country coverage gap I have ever seen described in public.
  • For an indie hacker doing 100 keyword research calls a month, the bill is ~$3.50 a month plus 100 rank-tracker pings at $0.012 each = ~$4.70/month. For a five-person agency tracking 5,000 keywords, it is closer to $60–$90 a month. Both are below the floor of any commercial SEO tool.
  • The crossover — the volume at which Semrush or Ahrefs becomes *cheaper* than DataForSEO — exists, and it is higher than most people think. I will show you where.

---

I want to start with a number, not a story. The number is 96.

Ninety-six is the number of credits OpenSEO charges for a single keyword research call in Iceland, a country DataForSEO Labs does not cover. The standard rate — the rate you would pay in the United States, the United Kingdom, Germany, Japan, Brazil, or any of the 94 countries DataForSEO Labs serves — is 32 credits. The Iceland call is exactly three times the cost, with no keyword difficulty, no search intent, and no SERP-feature context. The README calls this the "Iceland problem." I think it is the cleanest example of an honest, public pricing decision I have ever read in an SEO tool.

It is also the answer to the first question anyone asks about an open-source SEO tool: "If the code is free, what does it actually cost?"

The OpenSEO project answers that question with a single table in the README. I am going to walk through it line by line, because I think the table is a small work of art and because almost nobody reads pricing pages carefully enough to learn from them. I will then walk through what an actual user pays in two scenarios — an indie hacker running the tool for one site, and a small agency with five clients — and I will show you where the open-source stack stops being cheaper than the SaaS alternatives.

The pricing page most SEO tools do not want you to read

Here is the cost reference, in the order the README prints it, with the assumptions it states explicitly. All costs are in USD. "100 keyword research requests" means 100 calls to the keyword research endpoint, not 100 keywords looked up. "100 domain overviews" means 100 calls to the domain overview endpoint. Default result counts are stated where the README states them.

| Workflow | Default scope | Cost per call | Cost per 100 calls | |----------|---------------|---------------|---------------------| | Rank tracking (weekly) | 100 keywords, depth 50 | — | ~$1.20 / month | | Keyword research (default 150 results) | 100 calls | $0.025 / call | $3.50 | | Keyword research (500 results) | 100 calls | $0.060 / call | $7.00 | | Domain overview (200 ranked keywords) | 100 calls | $0.040 / call | $4.01 | | Backlinks — domain search, default | 100 calls | $0.063 / call | $6.34 | | Backlinks — page search, default | 100 calls | $0.043 / call | $4.30 | | Backlinks — fully explored domain | 100 calls | $0.109 / call | $10.94 | | Backlinks — fully explored page | 100 calls | $0.086 / call | $8.61 |

These are the *DataForSEO* costs. The hosted version of OpenSEO applies a 1.28× markup and sells them as credits (1,000 credits = $1). The self-hosted version skips the markup and bills DataForSEO directly. The two paths share the same code.

I want to point out three things about this table that the form of the table hides.

First, the rank-tracking line is a *month* number, not a *call* number. "100 keywords weekly at depth 50" means you are running one rank-tracking workflow per week, hitting DataForSEO's SERP API four times a month for 100 keywords at a time. The cost is the SERP API cost, not a per-keyword cost. If you are tracking 500 keywords weekly, multiply by five.

Second, "backlinks fully explored"

9m / Article + audio

3. Cloudflare_Workers_and_the_Billing_Trick

Cloudflare Workers, D1, R2, and the Billing Trick

The 73-line wrangler.jsonc at the root of the OpenSEO repository is the architectural blueprint for the whole project. It also contains the single design decision that makes the hosted-versus-self-host split possible: the metered path is the easiest path.

Key Takeaways

  • The OpenSEO backend is a single Cloudflare Worker, a single D1 SQLite database, a single R2 bucket, two KV namespaces, two Cloudflare Workflows, and one Durable Object. The whole infrastructure fits in one configuration file.
  • The billing decision is the architectural decision. Hosted DataForSEO calls must route through a single client, createDataforseoClient, which checks credit balance *before* the call and records the actual provider cost *after* the call. Self-hosted calls skip the credit check entirely. Same code, same UI, different cost line.
  • The onboarding chat agent — a Durable Object backed by SQLite — is the most interesting piece. It scrapes a new user's site, calls DataForSEO, and synthesizes a strategy with one LLM call. The whole flow costs $0.10–$0.25 per signup.
  • The trade-off the project is making: leverage Cloudflare's primitives so deeply that the application code reads like infrastructure glue. The cost is portability. The benefit is that the hosted and self-hosted versions of the product are the same binary, gated by environment variables.

---

There is a file in the root of the every-app/open-seo repository called wrangler.jsonc. It is 73 lines long. I want to read it with you line by line, because I think it is the most interesting document in the project — more interesting than the README, more interesting than any of the spec files, more interesting than the marketing copy. The README tells you what the project is. The spec files tell you what decisions the team has made. The wrangler.jsonc file tells you what the project *is built out of*.

Here is the full file, with my annotations in the margins:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "open-seo",
  "main": "src/server.ts",
  "compatibility_date": "2025-09-02",
  "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
  "observability": { "enabled": true },
  "workflows": [
    { "name": "site-audit-workflow",     "binding": "SITE_AUDIT_WORKFLOW", "class_name": "SiteAuditWorkflow" },
    { "name": "rank-check-workflow",     "binding": "RANK_CHECK_WORKFLOW", "class_name": "RankCheckWorkflow" }
  ],
  "durable_objects": {
    "bindings": [{ "name": "ONBOARDING_CHAT", "class_name": "OnboardingChatAgent" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["OnboardingChatAgent"] }],
  "triggers": { "crons": ["*/15 * * * *"] },
  "kv_namespaces": [
    { "binding": "KV",       "id": "4abd52f3f2c549ac83cc2cb4ceec8620" },
    { "binding": "OAUTH_KV", "id": "bd1759494309474a9b423b029967b0db" }
  ],
  "d1_databases": [{
    "binding": "DB", "database_name": "open-seo",
    "database_id": "37bee90a-e1aa-404f-b01e-b0d1d479bda1", "migrations_dir": "drizzle"
  }],
  "r2_buckets": [{ "bucket_name": "open-seo", "binding": "R2" }]
}

That is the entire production deployment, in one file. No Kubernetes. No Terraform. No Pulumi. No Dockerfile-as-infrastructure. The 73 lines are the infra.

What is in the box

Let me name what each binding does, because the names are descriptive and the Cloudflare primitives are doing real work.

  • The Worker itself (main: src/server.ts) is the single entrypoint. Every HTTP request — UI, API, MCP, webhooks — lands here. The project uses TanStack Start, so the Worker serves the React app, the server functions, and the MCP transport from one process.
  • D1 (DB) is the relational store. SQLite at the edge, replicated to the user's region. The project uses Drizzle as the ORM and the drizzle/ directory holds the migrations. Auth, projects, keyword research history, rank tracking configs, backlink snapshots, all live in this one database.
  • R2 (R2) is the object store. The README and the spec for the onboarding agent both reference R2 as the destination for project context artifacts, scraped markdown, and any large blobs the SQLite store would rather not hold.
  • KV (KV) and OAUTH_KV are the two key-value namespaces. KV is for general caching and feature-flag-ish state. OAUTH_KV is the Cloudflare Workers OAuth provider's namespace, holding MCP client registrations, grants, and tokens. The split is intentional: OAuth tokens have a different access pattern (long-lived, sensitive) than the rest of the cache, and they get a separate namespace so they can be inspected, rotated, and audited independently.
  • The two Workflows are Cloudflare's durable execution primitives. SITE_AUDIT_WORKFLOW runs a multi-step site audit — crawl, parse, score, persist — and survives Worker restarts. RANK_CHECK_WORKFLOW is the same idea for rank checks: kick off a workflow, it queries the SERP API for every keyword in a tracker config, persists the results, and reports back when it is done. Workflows are how the project gets reliable long-running jobs without standing up a queue.
  • The Durable Object (ONBOARDING_CHAT, class OnboardingChatAgent) is the only stateful object in the system that is not a database row. It is the chat agent. It is backed by its own SQLite instance, declared in the migrations block as a new_sqlite_classes migration. One DO instance per project. The agent's conversation history persists i

10m / Article + audio

Premium chapters

4. The_MCP_Server_Is_The_Product
Available after upgrade / 9m
5. Which_Open_Source_SEO_Tool_Should_You_Use
Available after upgrade / 10m