Tool · tool guide

JSON SEO Report: Validate the Payload Before You Gate

Validate a JSON SEO report against an explicit schema, preserve original issue fields, reject missing or malformed artifacts, and map domain errors to exit 1.

Published Updated 13 min readBy William Zhu & InfiniSynapse Data Team

Author credentials: William Zhu is cofounder of InfiniSynapse (GitHub @allwefantasy · auto-coder). Desk: shipping SEO Health and the /en/tool/ visibility pages. No personal LinkedIn published. About: team / editorial standards.

Publisher credentials: InfiniSynapse has public profiles on GitHub, YouTube, and X. A 2026 WAIC Future Tech OPC excellence award covers the Agentic Data Infra entry (WAIC; third-party recap: Xinhua / Caohejing). That award does not review this page or SEO Health. Method CLI: infinitegrowth on npm. Archive: recognition and scope notes.

JSON SEO Report: Validate the Payload Before You Gate
On this page

By William Zhu · Cofounder, InfiniSynapse · Published: 2026-09-09 · Last updated: 2026-09-16 · Last verified: 2026-09-14 · Reviewed by the InfiniSynapse Data Team.

Reviewed by: InfiniSynapse Data Team · method review 2026-09-14. Six first-party CLI documents plus local gate.mjs fixtures; speed is present in live JSON and unused by the gate.

Trust / COI: About · Corrections. SEO Health is a commercial product from this team. A 72 KB parse success is not a domain pass and not a hosted artifact upload.

TL;DR

A JSON SEO report is a file with named keys, not a screenshot of a green process. gate.mjs requires url plus issues or onPage. It does not require speed.

Direct answer: Treat a JSON SEO report as shape first, policy second. On 2026-09-14 the malformed fixture (url only) exited 2 with SEO_CONTRACT missing_result_arrays. Six live documents shared the same top-level keys. The hub file was about 72 KB, CLI exit 0, Image Alt Text still warning.

The sample is six first-party documents plus five local fixtures. It cannot establish a market schema. A note that drops the required keys is already unreplayable.

What you will learn

  • Which keys every live document shared, and which ones gate.mjs requires
  • How to read the file with jq or json.loads before any policy
  • Why Lighthouse JSON, crawler exports, and on-page JSON-LD are different files
  • How a url-only object becomes exit 2, and why CLI exit 0 can still carry a warning

What the payload is allowed to omit

Key definition: A JSON SEO report is the versioned object you keep after `seo-health check --format json`. It does not score SERPs, promise clicks, or mint a Google certificate. Its useful output is original fields plus a parser exit tied to one commit.

RFC 8259 is the text grammar. If JSON.parse throws, the file is not a report. It is debris. Exit 2. Do not map that to a content red.

People open this page when they already have bytes on disk and still need to know whether those bytes are evaluable. They need url and at least one result array.

JSON Schema 2020-12 is one way to write that requirement down. The local gate is smaller: url must be a string, and issues or onPage must be an array. Live documents still included speed. The gate did not read it.

The malformed fixture is the proof. It has a url. It has no issues and no onPage. gate.mjs printed SEO_CONTRACT missing_result_arrays and exited 2. A check that only tests typeof url === "string" would have gone green.

page on live documents carried title, description, canonical, and wordCount. Those fields help a reviewer. The gate shown here does not require them. Change the fixture set before you tighten the contract.

Minimal schema for a JSON SEO report

People searching seo audit json or seo report json format usually want this field list, not a new crawler. The 2026-09-14 live documents shared one JSON SEO report shape. gate.mjs required a subset.

KeyRequired by gate.mjsWhat it held on the desk
urlYes. Must be a string.The fetched tool URL
issuesYes, if onPage is missingRows with name and status
onPageYes, if issues is missingSame job under a second key
pageNotitle, description, canonical, wordCount
speedNoPresent on every live file; unread by the gate
images, keywords, seoData, cachedNoSeen on all six live documents

The local gate is smaller than a published schema: url plus one result array. Do not add speed as required in a comment. Change the fixture set first.

Desk record for six documents

Node v22.18.0 ran gate.mjs against the fixtures, then the pinned CLI against six first-party tool URLs. Package infinitegrowth@0.1.1. Every live process exited 0. Every live document had 0 error rows and 1 warning (Image Alt Text 2/5). cached was false on every row.

slugtitle_lenwordsspeedttfbhtml
seo-ci-cd493332721488 ms100.9 KB
github-actions-seo-audit473276721274 ms101.3 KB
infinitegrowth-npm463250721195 ms100.9 KB
json-seo-report413319721144 ms101.5 KB
batch-seo-url-check403362721241 ms100.2 KB
cursor-seo-skill453409721192 ms102.8 KB
Grouped bars for the 2026-09-14 desk: gate exit beside required-array presence on five fixtures

Figure: Two series from the same desk. Gate exit is not array presence. Neither series is a search-engine score.

Shared top-level keys

Every live document exposed url, page, onPage, issues, images, keywords, speed, seoData, and cached. issues[].status values seen were good and warning. No error appeared in this sample. A JSON SEO report parser that assumes error will always show up will miss the warning-only case.

Fixture exits from gate.mjs

InputExitstderr
red.fixture.json1SEO_RED Title Length
malformed.fixture.json2SEO_CONTRACT missing_result_arrays
warn.fixture.json0(empty)
good.fixture.json0(empty)
no path2usage: node gate.mjs <report.json>

Exit 1 is a valid object with a red row. Exit 2 is usage, missing file, invalid JSON, or missing arrays. Keep those codes distinct. Ajv can sit in front if you want a published schema. The desk gate did not require Ajv to produce the table above.

The hub CLI wrote about 72 KB and still left process 0 with a warning. Size is not health. Keep the file. Read issues[].

Node.js ECMAScript modules is how gate.mjs loads (import { readFileSync }). It does not decide SEO policy. It only makes the parser a file you can commit.

How to reproduce the contract

Save the fixtures. Run the gate. Then parse one live file with json.loads if you want a second language on the same keys.

node gate.mjs malformed.fixture.json; echo exit:$?
# SEO_CONTRACT missing_result_arrays · exit 2
node gate.mjs red.fixture.json; echo exit:$?
# SEO_RED Title Length · exit 1
python3 -c "import json; d=json.loads(open('hub.json').read()); print(sorted(d))"

The wrong npx argv still fails before a fetch: npx --yes infinitegrowth@0.1.1 seo-health check URL printed error: unknown command 'seo-health' and exited 1. The working form is npx --yes -p infinitegrowth@0.1.1 seo-health check --format json --lang en URL. As of 2026-09-14 that form wrote the six documents in the table.

MDN JSON.parse is the parser the gate calls. Invalid JSON is exit 2, not a soft warning. OWASP Input Validation is why untrusted bytes fail closed. Do not evaluate status on an object you have not shaped.

Parse a JSON SEO report with jq or Python

Developers search for the command, not for gate.mjs. After seo-health check --format json writes a JSON SEO report, read the arrays. Do not grade a process code.

jq -e '.url and ((.issues|type=="array") or (.onPage|type=="array"))' report.json
# nonzero jq status → treat as exit 2
jq '.issues[]? | select(.status=="error" or .status=="warning") | {name,status}' report.json
python3 -c "import json,sys; d=json.load(open('report.json')); a=d.get('issues') or d.get('onPage'); sys.exit(0 if isinstance(d.get('url'),str) and isinstance(a,list) else 2)"

jq manual is the filter language those two lines use. Missing url or a missing result array must fail closed. Keep name and status as the CLI wrote them.

Why parse before you grade

HTTP 200, shell exit 0, valid JSON, schema validity, and a green domain decision are five checkpoints. This page owns the middle two. The SEO CI/CD complete guide owns the last one after the arrays exist.

Collapse 1 and 2 and incident triage starts with a guess. Exit 2 is shape; exit 1 is a red row. The red fixture proves 1. The malformed fixture proves 2. A JSON SEO report runbook that only documents “nonzero” has already thrown away the useful bit.

GitHub workflow artifacts describe how a later job can retain the same file on failure. This website still has no Actions directory. A JSON SEO report on disk is enough to start.

JSON SEO report vs Lighthouse JSON

A Lighthouse JSON report and a PageSpeed Insights API JSON file are performance documents. They are not a JSON SEO report.

Lighthouse configuration stores scores under categories and audit objects under audits. PageSpeed Insights API wraps that same Lighthouse payload. Neither file is required to expose issues[] with good / warning / error.

gate.mjs will reject a Lighthouse export that only has lighthouseVersion and audits. That is exit 2, not a content red. Do not pipe PSI JSON into the SEO gate and call the red a title-length miss. Keep both artifacts if you run both tools. Grade each against its own keys.

This file is not on-page JSON-LD

A page can ship <script type="application/ld+json"> and still fail this contract. Those are different JSON files. A JSON SEO report is the CLI disk file, not the script.

JSON-LD 1.1 describes @context, @type, and @id inside the HTML. The JSON-LD checker owns that script. This page owns the CLI disk file after --format json.

If you searched “json seo” meaning structured data, leave here. This file is not a BlogPosting block and does not prove rich results. Do not run gate.mjs on a copied @graph.

Keep the JSON SEO report as a GitHub Actions artifact

The workflow page owns YAML. This page owns the file that YAML must keep. If you later add a job, retain the same path on failure:

- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: json-seo-report
    path: report.json
    if-no-files-found: error

if-no-files-found: error is how a missing JSON SEO report stays exit 2 instead of a silent skip. This website still has no Actions directory, so the snippet is a contract, not a hosted run. The Actions walkthrough owns the job that would upload it.

Diff two JSON SEO reports

Regression is a join on issues[].name, not a byte diff of 72 KB. Diff two JSON SEO report files that way.

Keep yesterday’s file and today’s file. For each shared name, record whether status moved among good, warning, error, and missing. New names and disappeared names are their own rows. Pretty-print noise is not a finding.

A batch SEO URL check or a scheduled site audit should still fail per file, then diff the survivors. Do not collapse fifty URLs into one blob.

Crawler export JSON is a different file

Screaming Frog export help describes crawler tables written to JSON or CSV. Those files list crawl columns. They do not have to include issues[] or onPage[] with this product’s status enum.

Do not feed a spider export to gate.mjs unless you first prove url is a string and a result array exists. A JSON SEO report from seo-health is one URL, one object. A crawl export is usually many rows and a different vocabulary. Translate only after you write the mapping.

Exit 0 can still carry a warning

The live hub CLI wrote about 72 KB, exited 0, and left Image Alt Text at warning. Process 0 is transport plus parse, not a clean JSON SEO report.

seo-health --format json can return 0 while issues[].status is warning. The warn fixture did the same locally. A JSON SEO report gate that ORs only the process code will merge that row. Read the array. Reserve exit 1 for a red you chose to block. Reserve exit 2 for missing shape.

What this page does not own

This page owns required keys, allowed status values, and the 1-versus-2 split. A pull_request workflow that later uploads the file lives on GitHub Actions SEO audit. A typed adapter that spawns the CLI lives on SEO API for developers. The registry lists a CLI bin, not an HTTP SEO API.

Store argv, the pinned version, the desk date, the URL, the key list, and the parser status. Never hand-edit the file between generation and parsing. Validate one object first. Do not start at audit --pages 50.

Four reviewable commits

Do not land schema, fixtures, live CLI, and branch protection in one opaque commit. Commit the malformed fixture and the red fixture first. Record exits 2 and 1 in the pull-request body. Then fetch one public URL and keep the raw object. Promote the parser to a required check only after the fixtures already fail closed. Shape work starts at exit 2, not at a color.

Failures that look like valid JSON

Url only. The malformed fixture. Exit 2. The most important JSON SEO report failure.

Unknown status. A row whose status is not good, warning, error, or missing is a contract fail. Do not coerce it to warning.

Hand-edited bytes. An agent “fixed” a title in the JSON. The file no longer matches stdout. Re-run the CLI.

Missing file treated as skip. Disk and network failures need exit 2.

Version drift. A global install that is not infinitegrowth@0.1.1 can change the shape. Pin, then upgrade where old and new keys can be compared.

Adjacent pages

Use this page for the object contract. Open a sibling when the next question is not the payload.

Job you actually haveGuide to open next
Shared file, fields, and required-check ruleSEO CI/CD
A later job that retains the same fileGitHub Actions SEO audit
A merge-request job that must parse this objectGitLab CI SEO check
Spawn the CLI from application codeSEO API for developers
On-page <script type="application/ld+json">JSON-LD checker
The CLI command that writes --format jsonSEO CLI

Inspect one live file before you write a schema

Run the browser form once, then reproduce the same JSON from the pinned CLI and keep the file.

Run SEO Health Checker

Use a public URL and never paste secrets.

Frequently Asked Questions

Does validation of the gate artifact require login?

No. Default check, audit, and links do not need a CLI key. eeat, aivisibility, and gsc do. Keep those deep commands out of the default JSON SEO report pull-request job unless a named owner accepts the spend.

What is the correct outcome for malformed JSON?

Fail closed. The desk used exit 2 for a missing issues array. Keep stderr. Malformed data is not a warning, because a JSON SEO report cannot prove the required checks ran.

Can schema validation rely on the CLI exit alone?

No. The live hub CLI exited 0 while Image Alt Text was warning. Parse issues[] and onPage[]. Preserve name and status. Process 0 is incomplete for a JSON SEO report.

Is the validated status an official Google score?

No. A JSON SEO report uses product traffic lights to enforce a local merge rule. It is not a Google grade, not an index promise, and not a rank forecast.

Does the gate require a speed object?

No. Live documents included speed. The gate required url plus a result array. A url-only object still exits 2. Revalidate the JSON SEO report after you change required keys.

Is a Lighthouse JSON file a JSON SEO report?

No. Lighthouse and PageSpeed JSON store categories and audits. This CLI stores url plus issues or onPage. Feed the wrong file to gate.mjs and you get exit 2.

How do I parse the file in CI without Node?

Use jq or json.loads on the same keys. Fail if url is missing or neither result array exists. A JSON SEO report job that only checks the process exit will merge the warning-only hub file.

Conclusion

Use a JSON SEO report when the job is to prove the object is evaluable before anyone debates a traffic light. The 2026-09-14 desk shows six documents with the same keys, fixture exits 1 and 2, and a hub file of about 72 KB that still carried a warning. Close the contract when url plus a result array are present, speed is optional, and exit 2 stays reserved for missing shape. Lighthouse JSON, crawler exports, and on-page JSON-LD stay on their own pages.

Use the InfiniSynapse web app only after a signed-in deep command is approved. The routine path remains the pinned CLI and the retained JSON.

WZ

William Zhu · Cofounder, InfiniSynapse · GitHub @allwefantasy

Desk-validated SEO Health methods. Corrections: zhuhl@infinisynapse.com · corrections policy.

JSON SEO Report: Validate the Payload Before You Gate