DEV Community

Profiterole
Profiterole

Posted on

I Built 50 SEO-Optimized Static Pages with Schema.org JSON-LD and Got Indexed in 48 Hours — Here's the Exact Template

Static sites and SEO don't have to be mutually exclusive. In fact, for niche B2B SaaS, a well-structured static site with proper Schema.org markup can outrank dynamic apps that never thought about structured data. Here's exactly how I built a library of 50+ SEO-optimized pages for a financial services SaaS — and had them indexed within 48 hours.

The Problem: Niche B2B Pages Nobody Searches For (Until They Do)

If you're building for a niche audience — say, registered investment advisors who need compliant client letter templates — generic content marketing won't cut it. Your buyers search for specific things: "form CRS delivery letter template", "RIA privacy policy notice template". These are low-volume, high-intent queries.

The play: generate one highly-structured static page per query. No backend, no CMS. Just HTML with the right metadata.

Step 1: The HTML Template Structure

Every page follows the same skeleton. The key is semantic HTML + consistent <head> metadata:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <!-- Primary SEO -->
  <title>Form CRS Delivery Letter Template for RIAs | RIALetters</title>
  <meta name="description" content="Compliant Form CRS delivery letter template for registered investment advisors.">
  <link rel="canonical" href="https://hlteoh37.github.io/ria-letters/form-crs-delivery-letter-ria">

  <!-- Open Graph -->
  <meta property="og:title" content="Form CRS Delivery Letter Template for RIAs">
  <meta property="og:type" content="website">
  <meta property="og:url" content="https://hlteoh37.github.io/ria-letters/form-crs-delivery-letter-ria">

  <!-- Schema.org JSON-LD -->
  <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "SoftwareApplication",
    "name": "RIALetters — Form CRS Delivery Letter",
    "applicationCategory": "BusinessApplication",
    "offers": {
      "@type": "Offer",
      "price": "49.00",
      "priceCurrency": "USD"
    },
    "url": "https://hlteoh37.github.io/ria-letters/"
  }
  <\/script>
</head>
<body><!-- content --></body>
</html>
Enter fullscreen mode Exit fullscreen mode

Step 2: Schema.org JSON-LD — What Actually Moves the Needle

Plain HTML tells Google what the page says. JSON-LD tells Google what the page is. For SaaS landing pages, combining SoftwareApplication + FAQPage schemas is the winning pattern:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is a Form CRS delivery letter?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A Form CRS delivery letter is required by SEC regulations for RIAs to document when they delivered the Client Relationship Summary to a retail investor."
      }
    },
    {
      "@type": "Question",
      "name": "How do I customize this template?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Fill in your firm name, advisor name, client details, and delivery date. No legal modification needed for standard RIA use cases."
      }
    }
  ]
}
<\/script>
Enter fullscreen mode Exit fullscreen mode

The FAQPage schema is the secret weapon — it triggers rich results in Google Search, showing expandable Q&A directly in the SERP. For niche queries, this often doubles click-through rate.

Step 3: Generating Pages at Scale with the Claude API

The template is fixed. The variable parts are: page title, meta description, H1, intro paragraph, and FAQ content. I used the Claude API to generate all variable content from a prompt template:

const Anthropic = require('@anthropic-ai/sdk');
const anthropic = new Anthropic();

const generatePageContent = async (letterType, audience) => {
  const response = await anthropic.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 1024,
    messages: [{
      role: 'user',
      content: `Generate SEO content for a B2B SaaS page.
        Audience: ${audience}
        Letter type: ${letterType}

        Return JSON with: title (60 chars), metaDescription (155 chars),
        h1, intro (2 sentences), faqs (3 x {question, answer})`
    }]
  });

  return JSON.parse(response.content[0].text);
};
Enter fullscreen mode Exit fullscreen mode

Then a build script injects content into the template:

const buildPage = (content, outputPath) => {
  const html = PAGE_TEMPLATE
    .replace(/{{TITLE}}/g, content.title)
    .replace(/{{META_DESC}}/g, content.metaDescription)
    .replace(/{{H1}}/g, content.h1)
    .replace(/{{INTRO}}/g, content.intro)
    .replace(/{{FAQ_JSON_LD}}/g, JSON.stringify(buildFaqSchema(content.faqs), null, 2))
    .replace(/{{CANONICAL_URL}}/g, buildCanonicalUrl(outputPath));

  fs.writeFileSync(outputPath, html);
};

const letterTypes = [
  'Form CRS Delivery Letter',
  'Fee Disclosure Letter',
  'Privacy Policy Notice',
  'IRA Distribution Letter',
  'RMD Required Minimum Distribution Letter',
  // ... 45 more
];

(async () => {
  for (const lt of letterTypes) {
    const content = await generatePageContent(lt, 'registered investment advisors');
    const slug = lt.toLowerCase().replace(/\s+/g, '-');
    buildPage(content, `public/${slug}-ria.html`);
    console.log(`Built: ${slug}-ria.html`);
  }
})();
Enter fullscreen mode Exit fullscreen mode

Step 4: GitHub Pages Deployment

For static sites at this scale, GitHub Pages is the right call:

  • Free hosting, no bandwidth limits on public repos
  • HTTPS by default (direct Google ranking signal)
  • Custom domain support via CNAME
  • Deploys on every git push — zero CI config needed

Repo structure:

repo/
  index.html                                       # Hub page
  sitemap.xml                                      # Auto-generated
  form-crs-delivery-letter-ria.html
  fee-disclosure-letter-ria.html
  privacy-policy-letter-ria.html
  rmd-required-minimum-distribution-letter-ria.html
  # ... 46 more
Enter fullscreen mode Exit fullscreen mode

Submitting to Google's Indexing API

Don't wait for organic crawl. Submit immediately after deploy:

# After verifying your property in Search Console:
for slug in form-crs-delivery-letter fee-disclosure privacy-policy; do
  curl -s -X POST \
    "https://indexing.googleapis.com/v3/urlNotifications:publish" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    -H "Content-Type: application/json" \
    -d "{\"url\": \"https://yourdomain.github.io/your-site/${slug}\",
         \"type\": \"URL_UPDATED\"}"
done
Enter fullscreen mode Exit fullscreen mode

Free tier allows 200 URL submissions/day — more than enough for 50 pages.

Step 5: The Sitemap Generator

const generateSitemap = (pages, baseUrl) => {
  const today = new Date().toISOString().split('T')[0];

  const urls = pages.map(p => `
  <url>
    <loc>${baseUrl}/${p.slug}</loc>
    <lastmod>${today}</lastmod>
    <changefreq>monthly</changefreq>
    <priority>${p.isHub ? '1.0' : '0.8'}</priority>
  </url>`).join('');

  return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls}
</urlset>`;
};

fs.writeFileSync('public/sitemap.xml', generateSitemap(allPages, BASE_URL));
Enter fullscreen mode Exit fullscreen mode

Reference it in every page <head> and submit the sitemap URL in Google Search Console.

Real Results

The live example is RIALetters — a niche SaaS for registered investment advisors who need SEC-compliant letter templates. After deploying with Schema.org markup and submitting the sitemap:

  • 48 hours: First pages appeared in Google's index
  • 1 week: Rich FAQ results showing in SERPs for letter-type queries
  • Zero ad spend: 100% organic traffic

The niche is everything here. Broad terms like "letter templates" would take months. But "form CRS delivery letter RIA" — a query with real purchase intent and near-zero competition — now has a structured-data-rich page answering it precisely.

Takeaways

  1. JSON-LD is mandatory for B2B SaaS — it communicates page semantics to Google at scale
  2. FAQPage schema triggers rich results; implement it programmatically for every page
  3. GitHub Pages + Indexing API gets you indexed in 48 hours, not 2 weeks
  4. One page per keyword intent beats one page trying to rank for everything
  5. Claude API + template injection scales 50+ pages in a few hours of work

This approach works best when your product maps to many specific queries — compliance tools, HR templates, legal docs, API documentation, and similar process-heavy B2B products all fit this pattern perfectly.


The full technique generalizes far beyond financial services. Anywhere there are many specific, low-competition, high-intent searches, this pattern will outperform generic content marketing.

Top comments (0)