To fact-check a YouTube video with an API, pull the video's metadata with Scavio's YouTube metadata endpoint, extract candidate claims from the title and description with an LLM, then query Google for each claim and check whether reputable results support or contradict it. Scavio returns metadata, not a transcript, so the claim extraction runs on the text you have or on a transcript from your own source. The output is a per-claim verdict (supported, unsupported, or mixed) with source URLs. This flags claims for human review; it is not a truth oracle. The value here is grounding: instead of trusting one model's memory, you anchor every judgement to live Google results that a person can open and read.
Prerequisites
- A Scavio API key (sk_live_...) from your dashboard; new accounts get 50 free credits.
- Python 3.9+ with requests, or Node.js 18+ for the JavaScript version.
- An LLM you can call for claim extraction and support judgement (Claude or similar).
- A YouTube video URL or ID you want to check.
Walkthrough
Step 1: Fetch the video metadata
Send the video URL or ID to the YouTube metadata endpoint. You get back the title, description, channel, and stats such as views, likes, and comment counts. Note that this is metadata only; there is no transcript in the response. The description is the richest field here, since creators often pack it with specific claims, dates, and links you can check. Keep the raw stats too, because a claim that contradicts a video with millions of views is worth surfacing prominently.
import requests
resp = requests.post(
"https://api.scavio.dev/api/v1/youtube/metadata",
headers={
"Authorization": "Bearer sk_live_...",
"Content-Type": "application/json",
},
json={"query": "https://www.youtube.com/watch?v=VIDEO_ID"},
)
data = resp.json()
print(data["title"], data["channel"], data["stats"]["views"])Step 2: Extract candidate claims
Pass the title and description to an LLM and ask it for a short list of checkable factual statements. If you have a transcript from your own source, add it here for fuller coverage; the API will not give you one. Each claim should be one specific assertion you can search for, such as a number, a date, or a named cause and effect. Drop opinions and vague phrasing, because they cannot be grounded against search results. This is the step where the LLM does real work, and where you should review its output before trusting the rest of the pipeline.
# Scavio returns metadata, not a transcript.
# An LLM turns the title + description into checkable claims.
metadata_text = f"{data['title']}\n\n{data['description']}"
claims = extract_claims_with_llm(metadata_text)
# -> ["The battery lasts 40 hours", "It ships in March 2026", ...]
for c in claims:
print("CLAIM:", c)Step 3: Ground each claim in Google results
For every claim, query the Google endpoint with light_request set to false so you get organic results, the knowledge graph, and news results. This costs 2 credits per query at that setting; the default light_request of true costs 1 credit but returns less. Phrase the query as the claim itself, or as the claim turned into a neutral question, so you do not bias the results toward agreement. Reading both organic results and news lets you catch claims that were true once but have since been corrected.
import requests
def ground_claim(claim):
resp = requests.post(
"https://api.scavio.dev/api/v1/google",
headers={
"Authorization": "Bearer sk_live_...",
"Content-Type": "application/json",
},
json={"query": claim, "light_request": False},
)
g = resp.json()
return g["organic"], g.get("knowledge_graph"), g.get("news_results")Step 4: Score the claim and attach sources
Feed the top results back to the LLM and ask it to judge whether they support, contradict, or partially match the claim. Record the verdict, a confidence value, and the source URLs so a human can verify the call. Be conservative: when the results are thin or conflicting, label the claim mixed rather than forcing a yes or no. The confidence score and the attached links are what make this output reviewable, which matters because the model judging support can still be wrong.
def score_claim(claim, organic):
# Feed the top results back to the LLM to judge support.
sources = [{"title": r["title"], "url": r["link"], "snippet": r["snippet"]}
for r in organic[:5]]
verdict = judge_with_llm(claim, sources) # supported | unsupported | mixed
return {
"claim": claim,
"verdict": verdict["label"],
"confidence": verdict["confidence"],
"sources": [s["url"] for s in sources],
}Python Example
import requests
API_KEY = "sk_live_..."
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
BASE = "https://api.scavio.dev/api/v1"
def get_metadata(video_url):
resp = requests.post(
f"{BASE}/youtube/metadata",
headers=HEADERS,
json={"query": video_url},
)
resp.raise_for_status()
return resp.json()
def ground_claim(claim):
resp = requests.post(
f"{BASE}/google",
headers=HEADERS,
json={"query": claim, "light_request": False},
)
resp.raise_for_status()
return resp.json()
def extract_claims_with_llm(text):
# Replace with a real LLM call (Claude, etc.).
# Return a list of short, checkable factual statements.
raise NotImplementedError
def judge_with_llm(claim, sources):
# Replace with a real LLM call.
# Return {"label": "supported|unsupported|mixed", "confidence": 0.0-1.0}
raise NotImplementedError
def fact_check(video_url):
meta = get_metadata(video_url)
print(f"Video: {meta['title']} ({meta['stats']['views']} views)")
text = f"{meta['title']}\n\n{meta['description']}"
claims = extract_claims_with_llm(text)
report = []
for claim in claims:
g = ground_claim(claim)
organic = g.get("organic", [])
sources = [{"title": r["title"], "url": r["link"],
"snippet": r["snippet"]} for r in organic[:5]]
verdict = judge_with_llm(claim, sources)
report.append({
"claim": claim,
"verdict": verdict["label"],
"confidence": verdict["confidence"],
"sources": [s["url"] for s in sources],
})
return report
if __name__ == "__main__":
results = fact_check("https://www.youtube.com/watch?v=VIDEO_ID")
for row in results:
print(f"[{row['verdict']}] {row['claim']}")
for url in row["sources"]:
print(f" {url}")JavaScript Example
const API_KEY = "sk_live_...";
const HEADERS = {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
const BASE = "https://api.scavio.dev/api/v1";
async function getMetadata(videoUrl) {
const resp = await fetch(`${BASE}/youtube/metadata`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({ query: videoUrl }),
});
if (!resp.ok) throw new Error(`metadata ${resp.status}`);
return resp.json();
}
async function groundClaim(claim) {
const resp = await fetch(`${BASE}/google`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({ query: claim, light_request: false }),
});
if (!resp.ok) throw new Error(`google ${resp.status}`);
return resp.json();
}
// Replace these two with real LLM calls (Claude, etc.).
async function extractClaimsWithLLM(text) {
throw new Error("not implemented");
}
async function judgeWithLLM(claim, sources) {
throw new Error("not implemented");
}
async function factCheck(videoUrl) {
const meta = await getMetadata(videoUrl);
console.log(`Video: ${meta.title} (${meta.stats.views} views)`);
const text = `${meta.title}\n\n${meta.description}`;
const claims = await extractClaimsWithLLM(text);
const report = [];
for (const claim of claims) {
const g = await groundClaim(claim);
const organic = g.organic || [];
const sources = organic.slice(0, 5).map((r) => ({
title: r.title,
url: r.link,
snippet: r.snippet,
}));
const verdict = await judgeWithLLM(claim, sources);
report.push({
claim,
verdict: verdict.label,
confidence: verdict.confidence,
sources: sources.map((s) => s.url),
});
}
return report;
}
factCheck("https://www.youtube.com/watch?v=VIDEO_ID").then((results) => {
for (const row of results) {
console.log(`[${row.verdict}] ${row.claim}`);
for (const url of row.sources) console.log(` ${url}`);
}
});Expected Output
Video: The Truth About Solid-State Batteries (842113 views)
[supported] Solid-state cells reach higher energy density
https://en.wikipedia.org/wiki/Solid-state_battery
https://www.nature.com/articles/...
[unsupported] These batteries ship to consumers in 2026
https://www.reuters.com/...
[mixed] Charge time drops to under five minutes
https://ieeexplore.ieee.org/...