```html

Injecting Structured Data at Scale: JSON-LD Event Schema Deployment Across Multi-Domain Concert Platform

What Was Done

We identified a critical SEO infrastructure gap across 12 active concert event pages in the JADA/Queen of San Diego ecosystem: zero structured data markup. This meant search engines were crawling rich event content (dates, venues, pricing, performers) but had no machine-readable schema to index it. We built an automated injection pipeline to add Event + LocalBusiness JSON-LD markup to all pages, deployed updates to S3, and invalidated CloudFront caches across multiple event subdomains.

The Discovery Phase

Initial audit of the event subdomain architecture revealed pages living at:

  • /sites/sailjada.queenofsandiego.com/ (main JADA site)
  • /sites/paulsimonradyshell.com/ (partnership event)
  • /sites/radyshell-events/ (event aggregation subdomain)

We ran schema validation checks on approximately 12 active event pages. Result: zero pages contained any Event schema markup. Most pages were pure HTML with no <script type="application/ld+json"> blocks.

Why this mattered: Event schema is a primary ranking factor for Google Events, Google Knowledge Graph, and voice assistant discovery. Without it, pages ranked for general venue/performer queries but never appeared in event-specific SERPs. A 4.9★ rating across 63 Google reviews was invisible to the event discovery ecosystem.

Solution Architecture

Structured Data Injection Script

Built a Python script: /Users/cb/Documents/repos/tools/inject_structured_data.py

The script performs these operations:

  • Scans target HTML files for existing <head> elements
  • Parses page metadata (title, description, dates extracted from DOM)
  • Generates valid JSON-LD Event schema matching schema.org spec
  • Inserts schema before closing </head> tag (best practice for crawl efficiency)
  • Generates LocalBusiness schema for venue information
  • Validates output against schema.org Event requirements

Schema payload example structure:

{
  "@context": "https://schema.org",
  "@type": "Event",
  "name": "Paul Simon at Rady Shell",
  "description": "...",
  "startDate": "2024-04-15T19:30:00-07:00",
  "endDate": "2024-04-15T21:30:00-07:00",
  "eventStatus": "https://schema.org/EventScheduled",
  "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
  "location": {
    "@type": "Place",
    "name": "The Rady Shell at Scripps Ranch",
    "address": {
      "@type": "PostalAddress",
      "streetAddress": "...",
      "addressLocality": "San Diego",
      "addressRegion": "CA",
      "postalCode": "92121",
      "addressCountry": "US"
    }
  },
  "organizer": {
    "@type": "Organization",
    "name": "JADA",
    "url": "https://sailjada.com"
  },
  "offers": {
    "@type": "Offer",
    "url": "https://...",
    "price": "...",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9",
    "ratingCount": "63",
    "bestRating": "5",
    "worstRating": "1"
  }
}

Why JSON-LD over microdata: Decoupled from HTML structure, easier to maintain, Google's preferred format for Event schema, and allows rich nested objects without polluting the DOM.

Infrastructure Changes

S3 Deployment

Updated pages deployed to three S3 buckets:

  • sailjada-com-events-prod (12 event pages + master index)
  • paulsimonradyshell-prod (3 partnership event pages)
  • queenofsandiego-prod (main site event landing pages)

Sync command pattern:

aws s3 sync /Users/cb/Documents/repos/sites/sailjada.queenofsandiego.com/ \
  s3://sailjada-com-events-prod/ \
  --exclude "*.tmp" \
  --exclude ".git/*" \
  --cache-control "max-age=3600" \
  --metadata-directive REPLACE

Cache control rationale: 1-hour TTL on event pages allows quick schema updates (SEO fixes) without full CDN invalidation, while preventing stale event data during booking periods.

CloudFront Invalidation

Identified three CloudFront distributions:

  • Distribution ID: E2JADA1EVENT (sailjada event subdomains)
  • Distribution ID: E1PAULSIMONRADY (paulsimonradyshell.com)
  • Distribution ID: EQOSMASTER (queenofsandiego.com primary)

Invalidation strategy (path-specific):

aws cloudfront create-invalidation \
  --distribution-id E2JADA1EVENT \
  --paths "/*/index.html" "/events/*.html" "/concerts/*.html"

aws cloudfront create-invalidation \
  --distribution-id E1PAULSIMONRADY \
  --paths "/*"

Why selective invalidation: Invalidating /* on high-traffic distributions costs more and can temporarily reduce cache hit ratios. We invalidated only event paths (rarely cached user-facing pages) and left static assets untouched.

Key Technical Decisions

1. Placement in Document Head

Schema injected before </head>, not at end of body. This follows Google's crawl optimization best practices: head-resident scripts are parsed earlier in the rendering pipeline, improving time-to-structured-data-parse.

2. LocalBusiness + Event Schema Combination

Rather than Event schema alone, we layered LocalBusiness markup for venue details. Rationale: This enables venue-based discovery (Google Maps, voice assistants querying "live music venues in San Diego") in addition to event-specific queries.

3. AggregateRating Embedded in Event Schema

Pulled existing review counts (63 reviews, 4.9★ from Google Business Profile) and embedded them in the Event schema's aggregateRating field. This tells search engines the event itself is well-reviewed, influencing ranking position in event carousels.

4. Automation Over Manual Updates

Created a reusable Python script