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
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 propertysiteEntry 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-levelcurl -o /dev/null -wfor/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/aboutand 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, orurlInspection.index.inspect. Those blocks are copy-paste templates withexample.comand 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.
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:
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 sourcesCount 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
- Actual subject-matter expertise. An API returns claimed names, text, and markup, not competence.
- Real-world reputation outside the indexed web. Offline citations, private referrals, and unlinked mentions are absent from GSC and HTML.
- Google's internal weighting. Google publishes no formula that maps these observations to ranking weight.
- 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.
Know your site's real SEO score
Free GSC-verified audit, E-E-A-T scoring, and AI-powered content strategy.
Get Started Free