The Open Alternative To directory I launched in April has 80 programmatic pages — one per SaaS tool it covers. The first AdSense submission came back rejected. The scaled content abuse flag led to pruning down to 18 indexed pages. The re-application is a single-site attempt, and it required building E-E-A-T infrastructure I'd been treating as optional.
Here's what I built, what I think matters versus what doesn't, and what I still don't know.
What E-E-A-T actually means for a programmatic directory
E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness) is Google's framework, but AdSense doesn't publish its review rubric. My interpretation after two rejections: reviewers check whether the site has demonstrable decision logic that a scraper wouldn't replicate.
For a programmatic directory, that question narrows to something specific: can a reader understand why a page shows the alternatives it does, why they're ordered the way they are, and who made that call? A page that lists "Top 5 alternatives to Datadog" with no explanation of how the list was constructed is indistinguishable from scraped content, regardless of how sophisticated the generation was.
This is the frame I used to build three pages: methodology, about, and affiliate-disclosure.
The methodology page: DRY-ing curation code into prose
The gate that decides which pages are indexed lives in curation.ts:
export const CURATION = {
MIN_ALTERNATIVES: 4,
MIN_TOP_STARS: 1000,
MIN_INTRO_LEN: 80,
};
export function isCurated(s: SaasEntry): boolean {
if (!s.intro || s.intro.length < CURATION.MIN_INTRO_LEN) return false;
if (s.model_used && GENERIC_MODELS.has(s.model_used)) return false;
const alts = s.alternatives ?? [];
if (alts.length < CURATION.MIN_ALTERNATIVES) return false;
const topStars = alts.reduce((m, a) => Math.max(m, a.stars ?? 0), 0);
if (topStars < CURATION.MIN_TOP_STARS) return false;
return true;
}
The methodology page imports these constants directly:
import { CURATION, CATEGORY_MIN_CURATED } from "../lib/curation.ts";
const MIN_ALTS = CURATION.MIN_ALTERNATIVES; // → 4
const MIN_STARS = CURATION.MIN_TOP_STARS; // → 1,000
If I change the threshold from 4 to 5 alternatives, the methodology page updates on the next build without touching prose. The code is the single source of truth; the page renders the current value in a sentence like "at least 4 open-source alternatives with the most-starred project above 1,000 stars."
I'd hardcoded these numbers in prose for six weeks. After changing thresholds twice, the prose was wrong. The DRY approach took about 30 minutes to implement and removes a whole category of maintenance drift.
The methodology page also covers the AI/human split explicitly — which model generates summaries (Claude Haiku 4.5 for ETL, Claude Sonnet 4.6 for editorial review), what it's prompted with, what it can get wrong (licensing nuance, version numbers, recent project status), and what's deterministic (alternative card metadata is rendered straight from the GitHub REST API v3, never touched by a language model). The three-tier content quality ladder describes how the tiers work technically; the methodology page is the human-readable summary of those same tiers.
A content farm doesn't document the boundary between machine and human output because it has no such boundary. Documenting it is differentiating by definition.
The about page: visible authorship without fabricated authority
The original about page was two paragraphs of boilerplate. The re-application version adds four things: my GitHub handle (mori7ga2222), links to all three sister sites with their purposes, the self-hosting context that shapes my editorial lens, and an honest cost breakdown.
The cost breakdown is the uncomfortable part. Saying "this costs $25/month and I'm hoping to sell it in 12 months" feels like admitting something unflattering. It's also the most differentiating sentence on the page. Every "legitimate business" about page reads the same. Almost none say "this runs for $25/month — Vercel Pro, Turso, and Anthropic API calls — and the monetization hypothesis is affiliate, not AdSense." That level of specificity is verifiable, which is the point.
The self-hosting context matters because it's the basis for editorial judgment on comparison pages. I've run Keycloak and Authentik in personal projects. I've set up a Grafana + Loki monitoring stack and briefly evaluated OpenObserve. I haven't deployed any of the Airtable alternatives beyond reading their changelogs and the GitHub repository histories. The about page names both the areas of experience and the gaps. Overstating authority would be worse than admitting the gaps, especially for a site where the affiliate monetization path means readers trust my recommendations.
The affiliate disclosure: per-site additions to shared boilerplate
The packages/shared/legal/ directory has an affiliate disclosure template shared across all three sites. That template covers the generic FTC language. What it doesn't cover is the site-specific disclosure:
- Which affiliate programs are currently active on this site
- Whether affiliate status affects which alternatives appear or how they're ranked (it doesn't — the
isCurated()gate doesn't have an affiliate field) - How to flag a disclosure issue
The ossfind-specific section I added answers those questions in about 200 words. The key constraint I gave myself: don't claim full compliance with something we don't yet meet. The disclosure acknowledges we're building out affiliate links incrementally and is explicit about which categories currently have them. Saying "all affiliate links are marked" when the current implementation marks zero would be a false claim in a legal disclosure.
The editorial takes: the content that actually demonstrates judgment
Alongside the transparency pages, I wrote three per-alternative editorial takes — long-form assessments for the auth0, datadog, and airtable comparison pages. These live in src/content/per-alternative-takes/ as an Astro 5 content collection and render only when a file exists for that SaaS slug.
The editorial takes are not AI-generated. The auth0 take required verifying the AGPL §13 implication for embedded SaaS scenarios (yes, it does trigger), cross-checking Keycloak RAM requirements against the Quarkus operator sizing guide, and reading 18 months of Kratos release notes to accurately characterize what "deliberately headless" means in practice. Three hours, roughly. The datadog take required checking whether the netdata star count I cited reflected the current GitHub count. The airtable take required distinguishing NocoDB's AGPL from its hosted-version license terms.
The noindex gate pruned 80 pages to 18 curated ones. Three of those 18 — one per vertical — have editorial takes. At 3-4 hours each, writing takes for all 18 would be 54-72 hours. That's not the near-term plan. Three is enough to demonstrate that some pages on this site cannot be replicated by searching and paraphrasing GitHub metadata.
Whether that distinction reads to an AdSense reviewer as meaningful — I genuinely don't know. The application is submitted. I'll share the outcome, whatever it is, in a follow-up.
What I'd do differently
I built these pages in the wrong order: disclosure first (most formulaic), methodology second, about page last (most uncomfortable). The right order is about → methodology → disclosure. The about page forces you to articulate who you are and why you have standing to run this directory. That clarity makes the methodology easier to write — you're not describing an abstract process, you're describing why you specifically set those thresholds. The disclosure becomes easier to write honestly once you've committed to the about page's honesty about the monetization goal.
The other change: I'd write at least one editorial take before the first AdSense submission. The initial rejection included "low-value content" as a reason. Demonstrable editorial judgment might have changed that, or might not — but it would have been faster to produce than the subdomain migration that followed.
FAQ
Does the methodology page need to be long?
No. The ossfind methodology page is about 1,100 words rendered. Length doesn't signal legitimacy; specificity does. A 400-word page that explains the exact curation threshold and links the source code where that threshold lives is more credible than a 2,000-word page describing a process in vague terms.
Should the editorial takes be AI-assisted?
The ones I wrote are not. Whether they could be AI-assisted while remaining genuinely editorial is an interesting question. Editorial value comes from judgment — choosing which licensing clause matters for which use case, assessing whether a star trajectory reflects real adoption. That judgment could be framed by AI and edited by a human. My current takes are fully human-written because I want at least a few pages I can confidently say are not model-replicable. That's not a statement about AI capability; it's about what I can defend to myself.
What if AdSense rejects again?
The monetization path doesn't depend on AdSense approval for the other two sites. Affiliate is the primary hypothesis. The ossfind re-application is a parallel bet with limited downside — the transparency pages are worth building regardless, because they make the site more honest for readers.
Related: How I kept 62 of 80 pages alive while hiding them from Google | How I built a three-tier content quality ladder for programmatic ETL
Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.
Top comments (0)