I turn a spreadsheet into hundreds of static SEO pages — so I built PageForge (free beta)
I run a couple of small niche sites — a pet-health tool and a travel-info site. The thing that actually moves the needle for both isn't clever copywriting. It's having a lot of pages that each answer one specific, low-competition question. "Puppy vaccine schedule by breed." "Is [neighborhood] worth visiting." That kind of thing. This approach has a name: programmatic SEO . You take a data set (one row = one page), pour it into a template, and generate pages at scale. Done well, it's how directories, comparison sites, and tool sites quietly rank for thousands of long-tail terms. Done badly, it's a spam farm that Google buries. More on that later, because it matters. The problem: the tooling is either expensive or a Rube Goldberg machine When I went looking for a way to do this without hand-coding every page, I found two camps: Agency-grade SaaS — powerful, but priced at $99–$299/month . That's a lot of money to spit out HTML when you're a solo operator running sites that make beer money. No-code stacks — wire a spreadsheet to a CMS to a static-site generator with a couple of automation tools in between. It works, but now you maintain a fragile chain of four services, and your pages live inside someone else's platform. Neither felt right. I just wanted: spreadsheet in, clean HTML out, files I own. What I actually built (for myself, first) So I wrote a generator for my own sites. Every morning it reads a CSV, applies a template, and produces a folder of static HTML pages — each with valid JSON-LD, proper meta tags, an internal-link hub, and a sitemap. I deploy the folder. Done. After running it daily for months on my own properties, I cleaned it up and turned it into a product: PageForge . The core idea is deliberately boring: CSV + template → a ZIP of clean static HTML pages you own. No dashboard you have to log into forever. No lock-in. The output is just files. If you stop using PageForge tomorrow, your pages keep working because they're plain HTML sitting in your r