What an E-E-A-T audit checklist actually checks (and the four things it cannot judge)

Run a reproducible E-E-A-T audit with GSC, JSON-LD, authorship, HTTPS, and QRG checks—and learn what no audit can judge.

Article highlights

  • Estimated reading time: 10 minutes
  • Published on: August 28, 2026
  • Last updated: August 28, 2026
RA
Published · Updated · 10 min read
Branded cover for the article 'What an E-E-A-T Audit Checklist Actually Checks (and the Four Things It Cannot Judge)': TrustGrowth wordmark and title with callouts: 7 retrievable checks: GSC access to branded search, Map every check to the current QRG, The 4 judgments no audit tool can make.

Article

Finding

An E-E-A-T audit checklist verifies retrievable signals: Search Console access, indexation fields, JSON-LD declarations, HTML authorship markup, HTTPS headers, public trust paths, and branded-query rows. It cannot judge expertise, offline reputation, Google's ranking weights, or whether a visitor trusted the page after the click.

Those four judgments stay out of scope even when every check below returns a pass.

Priority checklist

Check Input Output Limitation GSC access OAuth token plus a verified property siteEntry list or an HTTP error A 403 is not an indexation finding Indexation URL Inspection plus Search Analytics rows you actually retrieved Coverage, crawl time, canonical, page rows Templates here are unexecuted JSON-LD Fetched HTML Declared types and keys Declarations are not credentials Authorship HTML Fetched HTML Byline, rel=author, credential-shaped words Selectors do not prove who wrote the page Technical trust HTTP status and headers Path availability and HSTS A 404 is a remediation item only if the site promises that path Branded queries Search Analytics filter you run Clicks, impressions, CTR, position Zero rows are not zero reputation QRG map Current rater PDF plus your tally Manual follow-up list The PDF is not a ranking formula

Method

I ran the public, token-free half of this checklist against trustgrowth.ai on 2026-08-26 (UTC). I did not hold a Google OAuth token, so I did not execute GSC.

  • I ran: curl -sSI https://trustgrowth.ai/ and path-level curl -o /dev/null -w for /privacy, /contact, /about, /authors, and /authors/ravi-yadav. Status codes and the HSTS header are in Step 5.
  • I ran: stdlib HTML fetches of https://trustgrowth.ai/about and the published article What an E-E-A-T Checker Can and Cannot Verify. JSON-LD and authorship outputs are in Steps 3 and 4. No JavaScript ran.
  • I ran: python3 -c "from urllib.parse import quote; print(quote('https://example.com/', safe=''))" to test GSC path encoding. Output: https%3A%2F%2Fexample.com%2F.
  • I fetched: the current Search Quality Rater Guidelines PDF. HTTP 200 on 2026-08-26.
  • I did not run: sites.list, searchanalytics.query, or urlInspection.index.inspect. Those blocks are copy-paste templates with example.com and a placeholder token. They are not a TrustGrowth GSC pull. I did not invent crawl times, impression counts, or a 50-URL sample.
  • Sample: 1 host, 6 public URLs, 2 HTML parses, 1 encoding probe, 1 PDF HEAD/GET. If you later inspect N GSC URLs, record N and your date.

Limitations

  • GSC evidence here is a command shape, not a result. A missing token is not proof that a property is unverified.
  • HTML checks read the first response body. Client-rendered bylines are invisible.
  • Selector matches detect markup. They do not verify that a named person wrote the page or holds a credential.
  • QRG is a rater document, not Google's ranking formula. I cite the PDF URL that returned 200. I do not invent a revision date.
  • TrustGrowth's public score is a separate product artifact. See How We Score E-E-A-T from Search Console Data.

Prerequisites

  • A Search Console property you already own or can open as Full or Restricted user.
  • A Google Cloud project with the Search Console API enabled and OAuth 2.0 credentials, only if you run the GSC templates.
  • curl, a shell, and Python 3.9+. The scripts use the standard library.

The TrustGrowth public leaderboard is one way to publish results. Every check below can run without TrustGrowth.

Step 1: Confirm the audit surface with GSC

Primary docs: Search Console API authorization. This step is a template.

export GSC_ACCESS_TOKEN='replace_with_a_short_lived_oauth_token'
curl --fail-with-body -sS \
  -H "Authorization: Bearer ${GSC_ACCESS_TOKEN}" \
  'https://www.googleapis.com/webmasters/v3/sites'

A successful body contains siteEntry rows such as https://example.com/ or sc-domain:example.com, plus permissionLevel. Read the HTTP status and JSON error. Do not infer cause from the status alone.

Endpoint Documented access Useful output sites.list OAuth webmasters.readonly Property URL and permission level URL Inspection urlInspection.index.inspect OAuth webmasters; owner or Full user Index status, crawl time, canonical Search Analytics searchanalytics.query OAuth webmasters.readonly plus property access Clicks, impressions, CTR, position

Restricted users can often read Search Analytics and still fail URL Inspection. That is a permission boundary, not an indexation finding. See the URL Inspection API reference.

Step 2: Pull indexation and crawl signals

Primary docs: Search Analytics query. Still a template. Dates are computed on your machine. I did not inspect 50 URLs.

The property string in the URL path must be fully percent-encoded, including : and /. Slash-only substitution is wrong. I tested both encodings on 2026-08-26:

Input Encoding Result Usable in the path? https://example.com/ replace / with %2F https:%2F%2Fexample.com%2F No. The colon is still raw. https://example.com/ urllib.parse.quote(..., safe="") https%3A%2F%2Fexample.com%2F Yes. This is the encoding to copy.
export GSC_PROPERTY='https://example.com/'
ENCODED_PROPERTY=$(python3 -c "from urllib.parse import quote; import os; print(quote(os.environ['GSC_PROPERTY'], safe=''))")
TODAY=$(date -u +%F)
START=$(date -u -d '28 days ago' +%F 2>/dev/null || date -u -v-28d +%F)
curl --fail-with-body -sS -X POST \
  -H "Authorization: Bearer ${GSC_ACCESS_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d "{\"startDate\":\"${START}\",\"endDate\":\"${TODAY}\",\"dimensions\":[\"page\"],\"rowLimit\":50}" \
  "https://www.googleapis.com/webmasters/v3/sites/${ENCODED_PROPERTY}/searchAnalytics/query"

For each URL you choose, call URL Inspection with the exact verified property URL and the page URL:

export PAGE_URL='https://example.com/pricing'
curl --fail-with-body -sS -X POST \
  -H "Authorization: Bearer ${GSC_ACCESS_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d "{\"inspectionUrl\":\"${PAGE_URL}\",\"siteUrl\":\"${GSC_PROPERTY}\"}" \
  'https://searchconsole.googleapis.com/v1/urlInspection/index:inspect'

Record coverageState, lastCrawlTime, and googleCanonical from the response you receive. If you later treat a lastCrawlTime older than 90 days as a review flag, say that the 90-day cut is your rule. Google does not publish 90 days as an E-E-A-T threshold.

Step 3: Check structured data declarations

This script is executable. It uses only the Python standard library. It walks arrays, nested objects, and @graph. It checks declarations, not credentials.

#!/usr/bin/env python3
import json, re, sys, urllib.request

page_url = sys.argv[1] if len(sys.argv) == 2 else "https://example.com/about"
req = urllib.request.Request(page_url, headers={"User-Agent": "eeat-audit/1.0"})
with urllib.request.urlopen(req, timeout=20) as response:
    status = response.status
    content_type = response.headers.get("content-type", "")
    html = response.read().decode("utf-8", "replace")
if status >= 400:
    raise SystemExit(f"HTTP {status} for {page_url}")
if "text/html" not in content_type:
    raise SystemExit("Expected HTML, received: " + content_type)

types_found = set()
keys_found = set()

def walk(value):
    if isinstance(value, list):
        for item in value:
            walk(item)
    elif isinstance(value, dict):
        node_type = value.get("@type")
        if isinstance(node_type, list):
            types_found.update(str(item) for item in node_type)
        elif node_type:
            types_found.add(str(node_type))
        keys_found.update(value.keys())
        for child in value.values():
            walk(child)

for block in re.findall(
    r'<script[^>]*type=["\']application/ld\+json["\'][^>]*>(.*?)</script>',
    html,
    flags=re.I | re.S,
):
    try:
        walk(json.loads(block))
    except json.JSONDecodeError:
        print("Invalid JSON-LD block skipped", file=sys.stderr)

print(json.dumps({
    "url": page_url,
    "status": status,
    "content_type": content_type,
    "types": sorted(types_found),
    "has_person": "Person" in types_found,
    "has_organization": "Organization" in types_found,
    "has_author_property": "author" in keys_found,
    "has_sameAs_property": "sameAs" in keys_found
}, indent=2))

I ran python3 inspect_jsonld.py https://trustgrowth.ai/about on 2026-08-26. Observed output (stdlib fetch, no JavaScript):

{
  "url": "https://trustgrowth.ai/about",
  "status": 200,
  "content_type": "text/html; charset=utf-8",
  "types": ["AboutPage", "ContactPoint", "EntryPoint", "ImageObject", "Offer", "Organization", "Person", "SearchAction", "SoftwareApplication", "WebPage", "WebSite"],
  "has_person": true,
  "has_organization": true,
  "has_author_property": true,
  "has_sameAs_property": true
}

A pass means the fetched HTML declared Person or Organization and exposed author or sameAs. It does not mean the named person has the claimed qualifications. To add author markup rather than detect it, see How to Add Author Schema Markup to Every Page of Your SaaS Site.

Step 4: Check fetched HTML authorship markers

Schema and visible bylines are different checks. credential_terms_detected is keyword detection, not a verified credential.

#!/usr/bin/env python3
import json, re, sys, urllib.request

page_url = sys.argv[1] if len(sys.argv) == 2 else "https://example.com/blog/example"
req = urllib.request.Request(page_url, headers={"User-Agent": "eeat-audit/1.0"})
with urllib.request.urlopen(req, timeout=20) as response:
    status = response.status
    content_type = response.headers.get("content-type", "")
    html = response.read().decode("utf-8", "replace")
if status >= 400:
    raise SystemExit(f"HTTP {status} for {page_url}")
if "text/html" not in content_type:
    raise SystemExit("Expected HTML, received: " + content_type)

h1_match = re.search(r"<h1[^>]*>(.*?)</h1>", html, flags=re.I | re.S)
h1_text = re.sub(r"<[^>]+>", "", h1_match.group(1)).strip() if h1_match else ""
byline_hit = bool(re.search(
    r'(?:class|rel|itemprop)=["\'][^"\']*(?:byline|author)[^"\']*["\']',
    html,
    flags=re.I,
))
bio_link = bool(re.search(r'rel=["\']author["\'][^>]*href=|href=["\'][^"\']+["\'][^>]*rel=["\']author["\']', html, flags=re.I))
credential_terms_detected = bool(re.search(
    r"\b(PhD|MD|CPA|lawyer|engineer|certified|years of experience)\b",
    h1_text,
    flags=re.I,
))
print(json.dumps({
    "url": page_url,
    "status": status,
    "content_type": content_type,
    "h1": h1_text,
    "has_byline": byline_hit,
    "has_bio_link": bio_link,
    "credential_terms_detected": credential_terms_detected
}, indent=2))

I ran it on 2026-08-26 against the published checker article:

{
  "url": "https://trustgrowth.ai/blog/what-an-e-e-a-t-checker-can-and-cannot-verify-bc8057b6-284c-4360-aef5-1b19957b5111",
  "status": 200,
  "content_type": "text/html; charset=utf-8",
  "h1": "What an EEAT Checker Can and Cannot Verify",
  "has_byline": true,
  "has_bio_link": false,
  "credential_terms_detected": false
}

The same script on https://trustgrowth.ai/about returned has_byline: false with H1 About TrustGrowth. That is a markup miss on the about page, not a judgment of the people named in JSON-LD.

Step 5: Check technical trust markers

I executed these six requests on 2026-08-26 and re-verified the same statuses later the same day:

curl -sSI https://trustgrowth.ai/
for path in /privacy /contact /about /authors /authors/ravi-yadav; do
  curl -sS -o /dev/null -w "https://trustgrowth.ai${path} %{http_code}\n" "https://trustgrowth.ai${path}"
done
URL HTTP status Extra observation https://trustgrowth.ai/ 200 strict-transport-security: max-age=63072000; includeSubDomains https://trustgrowth.ai/privacy 200 https://trustgrowth.ai/contact 200 https://trustgrowth.ai/about 200 https://trustgrowth.ai/authors 404 https://trustgrowth.ai/authors/ravi-yadav 404

A 404 is a remediation item only if the site promises that path. Repeat the same paths on your host. HTTPS protects transport. It does not prove that page claims are true.

Step 6: Check branded search signals

GSC does not expose backlinks. If you have API access, branded-query rows are a visibility proxy only. This command is an unexecuted template.

curl --fail-with-body -sS -X POST \
  -H "Authorization: Bearer ${GSC_ACCESS_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d "{\"startDate\":\"${START}\",\"endDate\":\"${TODAY}\",\"dimensions\":[\"query\"],\"dimensionFilterGroups\":[{\"filters\":[{\"dimension\":\"query\",\"operator\":\"contains\",\"expression\":\"YOUR_BRAND\"}]}],\"rowLimit\":25}" \
  "https://www.googleapis.com/webmasters/v3/sites/${ENCODED_PROPERTY}/searchAnalytics/query"

If you run it, report the date range your shell computed and the clicks, impressions, CTR, and average position from the JSON you received. Zero rows are not proof of zero reputation.

Step 7: Map checks to the QRG

QRG mapping used on 2026-08-26. Source: Search Quality Rater Guidelines (HTTP 200 on 2026-08-26). Download the PDF on your audit date and record that date. Do not invent a version number.

Check Measures Does not measure Manual follow-up Crawl and canonical Retrieval and indexing state Expertise Inspect important pages JSON-LD Declared entities and relationships Credential validity Verify author identity Byline and bio Visible attribution Author competence Read the bio and work HTTPS and policy pages Transport and path availability Honest conduct Read the policy text Branded queries Search visibility proxy Complete reputation Search independent sources

Count raw pass and fail results per URL you inspected. This run's public sample is 6 URLs and 2 HTML parses, not 50 GSC rows. A transparent report can say "4 of 6 public paths returned 200 on 2026-08-26." It should not call that percentage Google's E-E-A-T score.

The four things an E-E-A-T audit cannot judge

  1. Actual subject-matter expertise. An API returns claimed names, text, and markup, not competence.
  2. Real-world reputation outside the indexed web. Offline citations, private referrals, and unlinked mentions are absent from GSC and HTML.
  3. Google's internal weighting. Google publishes no formula that maps these observations to ranking weight.
  4. Post-click trust and satisfaction. GSC reports impressions and clicks, not whether a visitor trusted the page.

Google's current rater framework uses E-E-A-T: Experience, Expertise, Authoritativeness, and Trustworthiness. Adding Experience did not create a public API for any of the four.

Reproduce this yourself

eeat-audit/
├── inspect_jsonld.py
├── inspect_authorship.py
└── commands.sh

Copy the two Python files. Run the public curl loop and the two scripts first. They need no Google account. Add GSC only when you have a token for a property you control. Save raw JSON under a directory named for your run date. This workflow has no TrustGrowth dependency.

FAQ

What is the difference between E-A-T and E-E-A-T?

E-A-T is Expertise, Authoritativeness, and Trustworthiness. Google's current rater framework adds Experience and writes E-E-A-T. This checklist can observe markup and crawl fields either way. It cannot observe experience.

Does author schema prove expertise?

No. JSON-LD can declare a Person and an author property. Expertise is a judgment about the work.

Which GSC permission do I need?

sites.list and Search Analytics are documented against webmasters.readonly plus property access. URL Inspection is documented against webmasters and owner or Full user access. A Restricted user can hit a 403 on inspection while Search Analytics still returns rows.

Can I score trust with this checklist?

No. You can count missing headers, missing paths, missing schema keys, and GSC fields you actually retrieved. You cannot output Google's trust weight. Related product method: How We Score E-E-A-T from Search Console Data. Related scope limits: What an E-E-A-T Checker Can and Cannot Verify.

Why did a GSC curl return 404?

Confirm the API path and send a fully encoded siteUrl in the path (https%3A%2F%2Fexample.com%2F, not https:%2F%2Fexample.com%2F). For URL Inspection, siteUrl in the JSON body stays unencoded and must match the verified property.

Summary

I ran the public half of this E-E-A-T audit checklist on 2026-08-26 against 1 host, 6 URLs, and 2 HTML parses. GSC templates remain unexecuted. The checklist can show missing evidence. It cannot judge expertise, offline reputation, Google's weighting, or post-click satisfaction. Last measured: 2026-08-26.

E-E-A-T Google Search Console SEO audits technical SEO Content Strategy structured data
Share:

Know your site's real SEO score

Free GSC-verified audit, E-E-A-T scoring, and AI-powered content strategy.

Get Started Free

Related Articles