Nodeshub is a powerful API that delivers structured data from Google search results. Instead of complicated scraping, you get ready-to-use JSON data in seconds. Perfect for SEO analysis, competitor monitoring, market research, and marketing automation.
The fastest way to try Nodeshub — no setup required. You get 100 free crawls instantly.
Important — save your API key!
Use your API key to make requests directly from the terminal. Replace YOUR_API_KEY with your actual key.
curl -X GET "https://api.nodeshub.io/v1/search" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "keyword=pizza warszawa" \
-d "gl=pl" \
-d "hl=pl" \
-d "device=desktop"Parameters explained:
keyword — the search query you want to analyzegl — country code for Google results (e.g. "us", "pl", "de")hl — language code for the interface (e.g. "en", "pl", "de")device — "desktop" or "mobile"The response is a JSON object containing organic results, SERP features, rankings, snippets, and more. See the Response Structure section for full details.
Connect Nodeshub to no-code platforms like n8n, Make.com, or Zapier using a simple HTTP request node with your API key. Perfect for scheduled SERP monitoring, automated reports, and competitive analysis.
For step-by-step setup instructions for each platform, see the No-Code Automations section below.
Try it out. No strings attached.
Free/100 tokens
Perfect for small projects and testing
$19/5000 tokens
Great for regular use and medium-scale projects
$99/40 000 tokens
Ideal for large-scale analysis and high-volume usage
$299/175 000 tokens
Use Nodeshub directly inside Claude Desktop with the Model Context Protocol (MCP). Search Google, analyze SERPs, and get structured data without leaving your conversation.
Visit nodeshub.io and copy your API key from the playground. You get 100 free crawls instantly.
Open the configuration file for your OS:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Add this to your config file:
{
"mcpServers": {
"nodeshub": {
"command": "npx",
"args": ["-y", "nodeshub-mcp"],
"env": {
"NODESHUB_API_KEY": "your-api-key-here"
}
}
}
}Restart Claude Desktop and start using Nodeshub in your conversations. Try asking:
Connect Nodeshub to your favorite automation tools. Schedule SERP checks, build reports, and monitor competitors — without writing a single line of code.
Open-source workflow automation. Add an HTTP Request node, point it at Nodeshub, and connect to 400+ integrations.
https://api.nodeshub.io/v1/searchAuthorization: Bearer YOUR_API_KEYkeyword, hl, gl, deviceVisual automation platform. Use the HTTP module to call Nodeshub and route SERP data to any app.
https://api.nodeshub.io/v1/search?keyword=YOUR_KEYWORD&hl=en&gl=usAuthorization: Bearer YOUR_API_KEYConnect Nodeshub to 6,000+ apps. Use Webhooks by Zapier to call the API and trigger downstream actions.
https://api.nodeshub.io/v1/search?keyword=YOUR_KEYWORD&hl=en&gl=usAuthorization | Bearer YOUR_API_KEY| Code | Meaning | Solution |
|---|---|---|
400 | Bad Request | Check query parameters (keyword, gl, hl) |
401 | Unauthorized | Check Authorization: Bearer YOUR_API_KEY |
429 | Too Many Requests | Slow down request rate or upgrade plan |
500 | Internal Server Error | Try again later |
Endpoint: https://api.nodeshub.io/v1/search
Request Structure:
GET https://api.nodeshub.io/v1/search?keyword=pizza+warszawa&hl=pl&gl=pl&device=desktop
Authorization: Bearer YOUR_API_KEY| Parameter | Type | Description | Example |
|---|---|---|---|
keyword | String | Search phrase | "best+programming+courses" |
| Parameter | Type | Description | Example Values |
|---|---|---|---|
hl | String | Interface language | "en", "pl", "de", "fr", "es" |
gl | String | Geographic location (country) | "us", "pl", "de", "uk", "fr" |
device | String | Device type | "desktop", "mobile" |
num | Integer | Number of results Currently unavailable | 10, 20, 30, 50, 100 |
Important Note: The num parameter is temporarily unavailable due to Google API changes. All queries currently return the default number of results (around 10).
Top level: data, totalResponseTime. Snippets live under data.results.snippets (not snippets_data). Organic results use pos, url, domain, description.
{
"data": {
"success": true,
"search_engine": "google",
"location": "pl",
"language": "pl",
"timestamp": "2026-02-26T12:00:00Z",
"results": {
"query": "pizza warszawa",
"organic_results": [
{
"pos": 1,
"global_pos": 2,
"title": "Best pizza in Warsaw - 24/7 Delivery",
"url": "https://pizzawarszawa.pl",
"domain": "pizzawarszawa.pl",
"description": "Best pizza in Warsaw with home delivery..."
}
],
"snippets": {
"people_also_ask": { "questions": [], "rank_absolute": 3 },
"related_searches": { "queries": [], "rank_absolute": 12 },
"ai_overview": {},
"local_pack": [],
"ads": []
},
"snippets_found": []
}
},
"totalResponseTime": 1240
}Use response.data.results.snippets.people_also_ask, response.data.results.snippets.related_searches, etc. Metadata is in data (location, language, timestamp).
Snippets are SERP elements returned under data.results.snippets. Which keys are present depends on the query. You can check data.results.snippets_found (array of names) to see which snippets were found for your request.
An AI-generated summary by Google that provides a synthesized answer to a user's query.
Business Use Cases:
JSON Preview
{
"ai_overview": {
"has_listen_button": false,
"rank_absolute": 1,
"sources": [
{
"display_url": "Facebook Business",
"rank_inner": 1,
"snippet": "Facebook advertising helps businesses...",
"title": "Facebook Advertising Guide",
"url": "https://www.facebook.com/business/ads"
}
],
"status": "success",
"text": "Facebook advertising allows businesses to target specific..."
}
}Paid Google Advertisements that appear at the top of search results.
Business Applications:
JSON Preview
{
"ads": [
{
"description": "Order The New Galaxy S25 Ultra...",
"display_url": "https://www.samsung.com",
"rank_absolute": 1,
"rating": {
"reviews": null,
"stars": null
},
"sitelinks": [
{ "title": "Shop Now", "url": "https://..." }
]
}
]
}A dynamic section showing related questions that users frequently ask.
Business Use Cases:
JSON Preview
{
"people_also_ask": {
"questions": [
{ "rank_inner": 1, "text": "How do Facebook ads work?", "expanded": false },
{ "rank_inner": 2, "text": "What is the best audience for ads?", "expanded": false }
],
"rank_absolute": 3
}
}A Google feature that provides quick, structured information about entities like people, places, or things.
Business Use Cases:
JSON Preview
{
"knowledge_graph": {
"title": "OpenAI",
"subtitle": "Artificial intelligence company",
"description": "OpenAI is an American AI research organization...",
"image_url": "https://...",
"attributes": {
"Founded": "December 11, 2015",
"Headquarters": "San Francisco, California",
"CEO": "Sam Altman"
},
"rank_absolute": 1
}
}A map-based snippet showing local businesses related to a search query. Access via data.results.snippets.local_pack.
Business Use Cases:
JSON Preview
{
"local_pack": [
{
"title": "Best Pizza Warsaw",
"address": "ul. Marszalkowska 12, Warsaw",
"phone": "+48 22 123 4567",
"rating": 4.5,
"reviews_count": 120,
"website": "https://bestpizzawarsaw.pl",
"open_now": true,
"rank_inner": 1
}
]
}Quick answers to popular questions related to the query. Access via data.results.snippets.things_to_know.
JSON Preview
{
"things_to_know": {
"items": [
{
"title": "Types of pizza ovens",
"snippet": "The main types include wood-fired, gas, and electric...",
"url": "https://...",
"rank_inner": 1
}
],
"rank_absolute": 5
}
}Posts and insights from Reddit, YouTube, Quora, etc. Access via data.results.snippets.perspectives.
JSON Preview
{
"perspectives": [
{
"source": "Reddit",
"author": "u/pizzalover",
"title": "Best pizza spots in Warsaw - my top picks",
"snippet": "After trying 50+ places, here are my favorites...",
"url": "https://reddit.com/r/warsaw/...",
"votes": 245,
"date": "3 weeks ago",
"rank_inner": 1
}
]
}Chips under the search bar for quick narrowing (e.g. "Under $500", "On sale"). Use for long-tail keyword research via data.results.snippets.refine_chips.
JSON Preview
{
"refine_chips": [
{ "label": "On sale", "url": "https://google.com/search?q=...&tbs=..." },
{ "label": "Under $500", "url": "https://google.com/search?q=..." },
{ "label": "Free shipping", "url": "https://google.com/search?q=..." },
{ "label": "4+ stars", "url": "https://google.com/search?q=..." }
]
}Recipe cards with ingredients, time, rating. Access via data.results.snippets.recipes_results. Ideal for food blogs and recipe SEO.
JSON Preview
{
"recipes_results": [
{
"title": "Classic Margherita Pizza",
"source": "Bon Appetit",
"rating": 4.8,
"reviews_count": 340,
"total_time": "45 min",
"ingredients": ["flour", "mozzarella", "tomato sauce", "basil"],
"thumbnail_url": "https://...",
"url": "https://...",
"rank_inner": 1
}
]
}Highlighted answer box at the top of search results with extracted content. Access via data.results.snippets.featured_snippets.
JSON Preview
{
"featured_snippets": [
{
"type": "paragraph",
"title": "What is SEO?",
"description": "Search engine optimization is the process of...",
"url": "https://...",
"display_url": "example.com › seo-guide",
"rank_absolute": 1,
"rank_inner": 1
}
]
}Recent news articles related to the search query. Access via data.results.snippets.top_stories.
JSON Preview
{
"top_stories": [
{
"rank_absolute": 6,
"stories": [
{
"title": "Here's how the new Samsung Galaxy S26 compares...",
"source": "The Verge",
"url": "https://www.theverge.com/...",
"published": "19 hours ago",
"is_live": false,
"rank_inner": 1
}
]
}
]
}Video results carousel from YouTube and other sources. Access via data.results.snippets.videos_pack.
JSON Preview
{
"videos_pack": [
{
"rank_absolute": 3,
"title": "Videos",
"videos": [
{
"title": "Python Full Course for Beginners",
"channel": "Programming with Mosh",
"duration": "6:14:07",
"source": "YouTube",
"published": "Feb 18, 2019",
"url": "https://www.youtube.com/watch?v=...",
"rank_inner": 1
}
]
}
]
}Product carousels with prices, ratings, and merchant info. Access via data.results.snippets.popular_products.
JSON Preview
{
"popular_products": [
{
"products": [
{
"title": "Nike Air Max SC Men's",
"price": 94.99,
"merchant": "DICK'S Sporting Goods",
"delivery_info": "Free delivery",
"discount_percent": null,
"rating": { "stars": 4.6, "reviews": 5100 },
"rank_inner": 1
}
]
}
]
}Image results carousel embedded in search results. Access via data.results.snippets.images_pack.
JSON Preview
{
"images_pack": [
{
"images": [
{
"title": "Nike Air Max 90 By You Custom Men's Shoes",
"alt_text": "Nike Air Max 90 By You Custom Men's Shoes",
"source": "Nike",
"url": "https://www.nike.com/...",
"rank_inner": 1
}
]
}
]
}Forum posts and community discussions from Reddit, Quora, and other platforms. Access via data.results.snippets.discussions_and_forums.
JSON Preview
{
"discussions_and_forums": [
{
"answer_count_text": "80+ comments",
"details": "r/samsung · 80+ comments · 1 month ago",
"rank_inner": 1
}
]
}Academic papers from Google Scholar embedded in search results. Access via data.results.snippets.scholarly_articles.
JSON Preview
{
"scholarly_articles": [
{
"title": "A review of the final US Climate Change...",
"citation_text": "National Research Council",
"cited_by_text": "Cited by 1381",
"url": "https://scholar.google.com/...",
"rank_absolute": 13
}
]
}Detailed product panel on the right side of results with description, filters, and merchant offers. Access via data.results.snippets.product_info_right.
JSON Preview
{
"product_info_right": {
"description": "Discover the Samsung Galaxy S25 5G...",
"filters": ["Color", "Storage"],
"offers": [
{
"seller": "AT&T",
"price": "$15.99",
"condition": "New",
"url": "https://www.att.com/..."
},
{
"seller": "Samsung",
"price": "$799.99",
"condition": "New",
"url": "https://www.samsung.com/..."
}
]
}
}Related product suggestions shown for product queries. Access via data.results.snippets.people_also_search_products.
JSON Preview
{
"people_also_search_products": [
{
"products": [
{
"name": "Samsung Galaxy S25+",
"image_url": "https://...",
"position": 1,
"url": "https://google.com/search?q=..."
}
]
}
]
}Local store listings with availability for product queries. Access via data.results.snippets.stores_nearby.
JSON Preview
{
"stores_nearby": [
{
"store_name": "Best Buy",
"address": "123 Main St",
"availability": "In stock",
"price": "$799.99"
}
]
}Organic results are in data.results.organic_results. Each item has pos, global_pos, title, url, domain, description.
// Track your website position for target keywords
function trackPosition(targetDomain, searchResults) {
const organicResults = searchResults.data.results.organic_results;
const position = organicResults.findIndex(result =>
result.url.includes(targetDomain)
) + 1;
if (position > 0) {
console.log(`Your domain ranks at position ${position}`);
// Analyze competitors above you
const betterResults = organicResults.slice(0, position - 1);
console.log('Competitors ranking higher:');
betterResults.forEach((result, index) => {
console.log(`${index + 1}. ${result.title} - ${result.url}`);
});
return position;
} else {
console.log('Your domain not found in top results');
return null;
}
}
// Usage example
const myPosition = trackPosition('mywebsite.com', searchResponse);// Analyze SERP features for keyword difficulty assessment
function analyzeSerpFeatures(searchResponse) {
const features = searchResponse.data.results.snippets_found || [];
console.log('SERP Features detected:', features);
// Calculate keyword difficulty based on features
let difficulty = 'Easy';
if (features.includes('knowledge_graph')) {
difficulty = 'Hard';
console.log('Knowledge Graph detected - High competition');
}
if (features.includes('ai_overview')) {
difficulty = 'Very Hard';
console.log('AI Overview present - Very high competition');
}
if (features.includes('videos_pack')) {
console.log('Video results present - Consider video content');
}
if (features.includes('popular_products')) {
console.log('Shopping results - Commercial intent keyword');
}
return {
difficulty,
features,
recommendations: generateRecommendations(features)
};
}
function generateRecommendations(features) {
const recommendations = [];
if (features.includes('people_also_ask')) {
recommendations.push('Create FAQ content addressing PAA questions');
}
if (features.includes('videos_pack')) {
recommendations.push('Develop video content strategy');
}
if (features.includes('related_searches')) {
recommendations.push('Target related search terms for long-tail SEO');
}
return recommendations;
}// Comprehensive competitor analysis
function analyzeCompetitors(searchResults) {
const organicResults = searchResults.data.results.organic_results;
// Domain frequency analysis
const domainStats = {};
organicResults.forEach((result, index) => {
const domain = result.domain;
if (!domainStats[domain]) {
domainStats[domain] = {
positions: [],
titles: [],
descriptions: [],
averagePosition: 0
};
}
domainStats[domain].positions.push(index + 1);
domainStats[domain].titles.push(result.title);
domainStats[domain].descriptions.push(result.description);
});
// Calculate average positions
Object.keys(domainStats).forEach(domain => {
const positions = domainStats[domain].positions;
domainStats[domain].averagePosition =
positions.reduce((a, b) => a + b, 0) / positions.length;
});
// Sort by average position (best first)
const sortedCompetitors = Object.entries(domainStats)
.sort(([, a], [, b]) => a.averagePosition - b.averagePosition);
console.log('Top competitors:');
sortedCompetitors.slice(0, 5).forEach(([domain, stats], index) => {
console.log(`${index + 1}. ${domain}`);
console.log(` Average position: ${stats.averagePosition.toFixed(1)}`);
console.log(` Appears in positions: ${stats.positions.join(', ')}`);
console.log(` Sample titles: ${stats.titles.slice(0, 2).join(' | ')}`);
});
return sortedCompetitors;
}
// Usage
const competitors = analyzeCompetitors(searchResponse);curl -X GET "https://api.nodeshub.io/v1/search" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "keyword=pizza warszawa" \
-d "gl=pl" \
-d "hl=pl" \
-d "device=desktop"const API_KEY = process.env.NODESHUB_API_KEY;
const response = await fetch(
'https://api.nodeshub.io/v1/search?keyword=pizza+warszawa&hl=pl&gl=pl',
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const data = await response.json();
const organic = data?.data?.results?.organic_results ?? [];
const related = data?.data?.results?.snippets?.related_searches?.queries ?? [];import os
import requests
url = "https://api.nodeshub.io/v1/search"
headers = {"Authorization": f"Bearer {os.environ.get('NODESHUB_API_KEY')}"}
params = {"keyword": "pizza warszawa", "gl": "pl", "hl": "pl"}
response = requests.get(url, headers=headers, params=params)
data = response.json()
organic = data.get("data", {}).get("results", {}).get("organic_results", [])
related = (data.get("data", {}).get("results", {}).get("snippets", {}) or {}).get("related_searches", {}).get("queries", [])$url = 'https://api.nodeshub.io/v1/search?' . http_build_query([
'keyword' => 'pizza warszawa', 'gl' => 'pl', 'hl' => 'pl'
]);
$ctx = stream_context_create([
'http' => [
'header' => 'Authorization: Bearer ' . getenv('NODESHUB_API_KEY')
]
]);
$data = json_decode(file_get_contents($url, false, $ctx), true);
$organic = $data['data']['results']['organic_results'] ?? [];
$related = $data['data']['results']['snippets']['related_searches']['queries'] ?? [];Classify search intent for any keyword in real time. Determine whether a query is informational, navigational, commercial, or transactional — and use that signal to prioritize content strategy.
The Query Fan-out endpoint takes a single seed keyword and expands it into a diverse set of related search queries. It analyzes real-time SERP data to generate keyword variations with different intents, specificity levels, and angles — perfect for content planning, keyword research, and building comprehensive topic clusters.
AI-powered keyword expansion
Unlike simple keyword suggestions, Query Fan-out uses AI to understand search intent and generate semantically diverse variations — including comparative queries, follow-up questions, entity expansions, and implicit user needs that traditional tools miss.
Endpoint: https://api.nodeshub.io/v1/query-fanout
Method: GET
Request Structure:
GET https://api.nodeshub.io/v1/query-fanout?keyword=seo+tools&hl=en&mode=standard&add_questions=true&add_topic_leaders=false&include_reasoning=false
Authorization: Bearer YOUR_API_KEY| Parameter | Type | Description | Example |
|---|---|---|---|
keyword | String | Seed keyword to expand | "seo+tools" |
hl | String | Language code for results | "en", "pl", "de" |
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | String | "standard" | "standard" (7.5 tokens) or "reasoning" (30 tokens) — reasoning mode produces more variants with explanations |
add_questions | Boolean | true | Include question-style keyword variants in results |
add_topic_leaders | Boolean | false | Enrich results with top-ranking page titles from SERP |
include_reasoning | Boolean | false | Include AI reasoning for each variant (only works in reasoning mode) |
Token cost: Standard mode costs 7.5 tokens per query. Reasoning mode costs 30 tokens per query but generates more diverse variants with detailed explanations.
{
"seed_keyword": "Pressure washer",
"generated_variants": [
{
"keyword": "best pressure washers",
"type": "related",
"confidence": 0.9
},
{
"keyword": "electric pressure washer",
"type": "specification",
"confidence": 0.85
},
{
"keyword": "pressure washer vs power washer",
"type": "comparative",
"confidence": 0.7
}
// ... more variants
],
"total_generated": 15,
"metadata": {
"generation_time_ms": 10959,
"mode": "standard",
"serp_queries_executed": 1,
"unique_questions_found": 0,
"generated_variants_count": 15
},
"top_titles": [
"Pressure Washers",
"Pressure Washers & Accessories",
"Commercial & Industrial Pressure Washers"
]
}In reasoning mode, each variant includes a reasoning field explaining why it was generated:
{
"seed_keyword": "SEO tools",
"generated_variants": [
{
"keyword": "free SEO tools for website analysis",
"type": "specification",
"confidence": 0.95,
"reasoning": "Combines the 'free' aspect highlighted in SERP titles with a specific use case ('website analysis'), making the query more precise."
},
{
"keyword": "SEO software comparison",
"type": "comparative",
"confidence": 0.9,
"reasoning": "Indicates a user in the decision-making phase, looking to compare different SEO solutions."
},
{
"keyword": "best SEO tools 2026",
"type": "recent",
"confidence": 0.9,
"reasoning": "Incorporates the current year to find the most up-to-date recommendations."
}
// ... more variants (typically 15-20)
],
"total_generated": 20,
"metadata": {
"generation_time_ms": 17114,
"mode": "reasoning",
"serp_queries_executed": 1,
"unique_questions_found": 0,
"generated_variants_count": 20
},
"top_titles": [
"Free SEO Tools by Moz | Try Premium SEO Checkers for Free",
"100% Free SEO Tools - SmallSEOTools.com",
"The 11 best SEO tools"
]
}| Field | Type | Description |
|---|---|---|
seed_keyword | String | The original keyword you submitted |
generated_variants | Array | List of expanded keyword objects |
generated_variants[].keyword | String | The generated keyword variation |
generated_variants[].type | String | Classification of the variant (see Variant Types below) |
generated_variants[].confidence | Number | Relevance score from 0.0 to 1.0 |
generated_variants[].reasoning | String | AI explanation for the variant (reasoning mode only) |
total_generated | Number | Total number of variants generated |
metadata | Object | Processing metadata (timing, mode, counts) |
top_titles | Array | Top-ranking page titles from SERP used for context |
Each generated keyword is classified into one of the following types, helping you understand the search intent and use it strategically:
relatedSemantically related queries that share the same topic space.
Example: "pressure washer" → "pressure washer accessories"
specificationMore specific versions of the seed keyword — narrowed by feature, use case, or type.
Example: "pressure washer" → "electric pressure washer"
implicitUnstated but likely user needs inferred from the seed keyword.
Example: "pressure washer" → "pressure washer for home use"
comparativeQueries comparing alternatives, often used in decision-making.
Example: "pressure washer" → "pressure washer vs power washer"
follow_upQuestions users typically ask after the initial search.
Example: "pressure washer" → "how to use a pressure washer"
reformulationRephrased versions of the same intent using different wording.
Example: "SEO tools" → "best free SEO checkers"
entity_expandedExpanded around specific entities (brands, products) found in SERP.
Example: "SEO tools" → "alternatives to Moz SEO tools"
entailmentLogically implied topics that follow from the seed keyword.
Example: "pressure washer" → "pressure washer maintenance"
generalizationBroader versions of the query, widening the topic scope.
Example: "seo tools" → "what is an seo tool"
personalizedLocation-aware or user-context queries.
Example: "cat" → "cats for adoption near me"
recentTime-sensitive queries incorporating recency.
Example: "SEO tools" → "best SEO tools 2026"
clarificationDisambiguating queries when the keyword has multiple meanings.
Example: "jaguar" → "jaguar car" / "jaguar animal"
curl --request GET \
--url 'https://api.nodeshub.io/v1/query-fanout?keyword=seo+tools&hl=en&mode=standard&add_questions=true&add_topic_leaders=false&include_reasoning=false' \
--header 'Authorization: Bearer YOUR_API_KEY'const params = new URLSearchParams({
keyword: 'seo tools',
hl: 'en',
mode: 'standard', // or 'reasoning'
add_questions: 'true',
add_topic_leaders: 'false',
include_reasoning: 'false'
});
const res = await fetch(
`https://api.nodeshub.io/v1/query-fanout?${params}`,
{ headers: { Authorization: 'Bearer YOUR_API_KEY' } }
);
const data = await res.json();
// Access generated variants
data.generated_variants.forEach(v => {
console.log(`[${v.type}] ${v.keyword} (confidence: ${v.confidence})`);
});import requests
params = {
'keyword': 'seo tools',
'hl': 'en',
'mode': 'reasoning',
'add_questions': 'true',
'add_topic_leaders': 'true',
'include_reasoning': 'true'
}
resp = requests.get(
'https://api.nodeshub.io/v1/query-fanout',
params=params,
headers={'Authorization': 'Bearer YOUR_API_KEY'},
timeout=30
)
data = resp.json()
for variant in data['generated_variants']:
print(f"[{variant['type']}] {variant['keyword']}")
if 'reasoning' in variant:
print(f" → {variant['reasoning']}")
print(f" Confidence: {variant['confidence']}")Use variant types to build structured topic clusters around a pillar keyword:
function buildContentCluster(fanoutResponse) {
const cluster = {
pillar: fanoutResponse.seed_keyword,
supporting: [],
faq: [],
comparisons: []
};
for (const v of fanoutResponse.generated_variants) {
if (v.type === 'specification' || v.type === 'related') {
cluster.supporting.push(v.keyword);
} else if (v.type === 'follow_up' || v.type === 'implicit') {
cluster.faq.push(v.keyword);
} else if (v.type === 'comparative') {
cluster.comparisons.push(v.keyword);
}
}
return cluster;
}Sort variants by confidence to prioritize the most relevant keywords:
function prioritizeKeywords(fanoutResponse, minConfidence = 0.8) {
return fanoutResponse.generated_variants
.filter(v => v.confidence >= minConfidence)
.sort((a, b) => b.confidence - a.confidence)
.map(v => ({
keyword: v.keyword,
type: v.type,
confidence: v.confidence
}));
}Fan out a keyword, then fetch SERP data for each variant:
async function deepResearch(keyword, apiKey) {
// Step 1: Expand the keyword
const fanout = await fetch(
`https://api.nodeshub.io/v1/query-fanout?keyword=${encodeURIComponent(keyword)}&hl=en&mode=standard&add_questions=true&add_topic_leaders=false&include_reasoning=false`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
).then(r => r.json());
// Step 2: Get SERP data for top variants
const topVariants = fanout.generated_variants
.filter(v => v.confidence >= 0.85)
.slice(0, 5);
const results = [];
for (const variant of topVariants) {
const serp = await fetch(
`https://api.nodeshub.io/v1/search?keyword=${encodeURIComponent(variant.keyword)}&gl=us&hl=en`,
{ headers: { Authorization: `Bearer ${apiKey}` } }
).then(r => r.json());
results.push({
keyword: variant.keyword,
type: variant.type,
serp_data: serp
});
}
return results;
}An open-source toolkit of ready-made SEO skills for Claude Code, Gemini CLI, or any AI CLI. Each skill works on two layers:
SERP data is fetched live from Google (not from a historical database), so results reflect the current state of search. The skills follow the open Agent Skills standard, making them portable across compatible tools.
This repository is a promotional package created by Senuto to demonstrate the capabilities of the NodesHub platform. It is a collection of instructions (SKILL.md files), Python scripts, and configuration templates that extend Claude Code with SEO-related functionality.
It is not a standalone application. Here is how it works:
/nod-serp-analysis "keyword" — Claude Code reads the included instructions and performs the analysis.For Business Users Only
This repository and the NodesHub platform it connects to are intended exclusively for professional and business use. Access to NodesHub is governed solely by the NodesHub Terms of Service, available at nodeshub.io.
Open a terminal, VS Code, Cursor, or any editor with an integrated terminal.
git clone https://github.com/Senuto/nodeshub-seo-skillscd nodeshub-seo-skillsImportant: always launch your AI CLI from inside this folder. Skills are discovered from .claude/skills/ relative to your working directory.
claudeWorks with Claude Code, Gemini CLI, Cursor, Aider, and any AI CLI that supports slash commands.
/guideType /guide to get an overview of all available skills and agents, and to check the connection status of all APIs (NodesHub, OpenRouter, Genuino, Jina, GSC, GA4).
Connect NodesHub and any optional services using dedicated skills:
/connect-nodeshub — required, provides live SERP data/connect-openrouter — optional, for clustering and content briefs/connect-genuino — optional, for AI Score and humanization/connect-gsc — optional, Google Search Console/connect-ga4 — optional, Google Analytics 4Everything is ready. Start running skills like /nod-serp-analysis, /nod-keyword-research, and more.
/connect-nodeshub and enter your key.python3 .claude/skills/nod-nodeshub-api/scripts/save_key.py YOUR_KEYexport NODESHUB_API_KEY=YOUR_KEYVerification:
python3 .claude/skills/nod-nodeshub-api/scripts/check_setup.pypython3 .claude/skills/nod-nodeshub-api/scripts/save_openrouter_key.py YOUR_KEY/connect-openrouter in Claude Code for a guided setup./connect-genuino in Claude Code for a guided setup.Type /connect-gsc in Claude Code — it will walk you through the OAuth setup step by step.
What it unlocks: pulls real search queries, impressions, clicks, and average positions from your GSC property. Skills can then cross-reference SERP data with your actual performance.
Type /connect-ga4 in Claude Code — it will walk you through the OAuth setup step by step.
What it unlocks: pulls pageviews, sessions, traffic sources, and events from your GA4 property. Useful for correlating keyword rankings with actual traffic and conversions.
Each NodesHub API call costs tokens. New users receive 100 free tokens upon registration. Genuino features are billed separately in Genuino credits.
Check your balance:
python3 .claude/skills/nod-nodeshub-api/scripts/balance.py| Operation | Cost |
|---|---|
| Fetch SERP (1 keyword) | 1 token |
| Query Fan-out standard (1 keyword) | 7.5 tokens |
| Query Fan-out reasoning (1 keyword) | 30 tokens |
| Intent Classifier | 2 tokens per keyword |
| Content Brief / Audit | ~8.5 tokens |
| AI Score (Genuino) | 1 Genuino credit (base) + optional 2 for guidelines + 2 for humanization prompt |
| Check balance | 0 |
| List countries/languages | 0 |
/nod-serp-analysisFetches and analyzes the top 10 Google results for a given keyword. Before running, the skill asks for keyword, market (gl/hl), and device — desktop and mobile SERPs can differ significantly.
What you get:
Cost: 1 token per keyword.
When to use: When you want to see who's ranking for a keyword, which SERP features appear, and what the competitive landscape looks like.
# Claude Code
/nod-serp-analysis
> Analyze the SERP for "website SEO" gl=us hl=en device=desktop
# Terminal
python3 .claude/skills/nod-nodeshub-api/scripts/serpdata.py "website SEO" --gl us --hl en --device desktop/nod-keyword-researchExpands a single seed keyword into a full list of related phrases, questions, long-tail variants, and topic clusters.
Two modes:
A) Iterative Research (recommended) — fetches the SERP for the seed, extracts PAA questions and related searches, then repeats for discovered phrases.
| Preset | Loops | Cost | Keywords |
|---|---|---|---|
| conservative | 3 | ~22 tokens | 50–150 |
| standard | 5 | ~72 tokens | 150–400 |
| aggressive | 15 | ~262 tokens | 400–1,000+ |
| beast | 30 | ~637 tokens | 1,000–3,000+ |
B) Simple Fan-out — a single API call returning related phrases, questions, and variants. Faster, but shallower. Cost: 7.5 tokens (standard) / 30 (reasoning).
Output: A CSV file saved to output/data/keywords/{keyword_slug}/ with columns: keyword, source, type, discovered_in_loop, serp_overlap.
/nod-serp-clustersGroups a list of keywords based on the similarity of their Google results. If two keywords share similar top-10 results, they end up in the same cluster — meaning they can be covered on one page.
Two clustering methods:
Cluster levels: --levels 1 (single layer, default), --levels 2 (broad + detailed), --levels 3 (broad + medium + detailed).
Reports: --report html (interactive D3.js dendrogram) or --report md (Markdown).
Requires: NodesHub key + OpenRouter key.
/nod-intent-classifierClassifies search intent based on SERP signals — not on the phrase itself, but on what Google actually shows.
Four categories:
Cost: 1 token per keyword. Each keyword receives a percentage confidence score.
/nod-paa-minerExtracts "People Also Ask" questions from Google for a list of keywords, deduplicates them, and optionally clusters them by topic using an LLM.
Output saved to: output/data/paa/{slug}_{date}.json — automatically after each run.
Cost: 1 token per keyword + optional OpenRouter for clustering. Keywords are fetched concurrently for faster results. When to use: When building a FAQ section, looking for questions to answer in your content, or planning an article's structure.
/nod-featured-snippet-hunterFinds Featured Snippet (position zero) opportunities for your domain.
Classifies opportunities as:
Cost: 1 token per keyword.
/nod-rank-trackerChecks where your domain ranks for given keywords. Saves snapshots and compares changes over time.
Output: A table with positions, changes (up/down), and the URLs that are ranking.
Data saved to: output/data/rank-history/{domain}/{YYYY-MM-DD}.json
Performance: Keywords are fetched concurrently (up to 5 in parallel) — ~2× faster on large lists.
Cost: 1 token per keyword. Recommended frequency: once a week.
/nod-competitor-trackerShows who is ranking for your target keywords. Displays domain frequency in the top 10, average positions, and a keyword × domain matrix.
Key difference vs. Rank Tracker: Rank Tracker monitors your domain. Competitor Tracker shows everyone ranking (or specific domains via --watch).
Data saved to: output/data/competitor-tracking/{YYYY-MM-DD}.json
Performance: Keywords are fetched concurrently (up to 5 in parallel).
Cost: 1 token per keyword.
/nod-visibility-monitorCalculates an SEO visibility score for a domain based on weighted positions. Position #1 = 10 pts, #2 = 9, ..., #8–10 = 2, outside top 10 = 0. The score is expressed as a percentage of the maximum possible visibility.
You can benchmark against competitors — add competitor domains to see who has greater visibility across a given keyword set.
Data saved to: output/data/visibility/{domain}/{YYYY-MM-DD}.json
Performance: Keywords are fetched concurrently (up to 5 in parallel).
Cost: 1 token per keyword. Useful for reporting.
/nod-content-briefGenerates a complete content brief for writing an article. Combines SERP data with keyword expansion to produce a comprehensive plan.
The brief includes:
Output saved to: output/data/briefs/{slug}.md — automatically after each run.
Cost: ~8.5 tokens (standard) / ~31 (reasoning) per brief.
/nod-content-auditorAudits an existing page against current Google results and identifies what's missing compared to what's ranking.
Pass --raw to get clean JSON output on stdout (progress messages go to stderr).
Cost: ~8.5 tokens (standard) / ~31 (reasoning).
/ai-scoreAnalyzes text for the probability of being AI-generated using the Genuino API.
What you get:
Supports three input methods: text files, inline text (minimum 200 words), and URLs (fetched via Jina Reader).
Cost: 1 Genuino credit per analysis. +2 credits for guidelines. +2 credits for humanization prompt. Requires: Genuino API key. Jina API key optional (for URL analysis).
# Claude Code
/ai-score
> Check this article: article.txt
# Terminal
python3 .claude/skills/ai-score/scripts/analyze.py --file article.txt --guidelinesAgents are orchestrators that compose multiple skills into end-to-end pipelines. Unlike skills (single-purpose tools), agents run multi-step workflows with user interaction between steps. Each agent saves progress per step and can be resumed from any point.
/topic-plannerOrchestrates the entire pipeline from a seed keyword to finished content briefs. Combines three steps into a single run with progress reporting between stages:
Resumable — each step saves its results to output/data/topics/[slug]/. You can stop and pick up where you left off.
Cost: ~25 tokens (keywords) + ~1 token/keyword (clustering) + OpenRouter (briefs). Requires: NodesHub key + OpenRouter key. Jina is optional.
/keyword-to-publishRuns the full pipeline from a seed keyword to a publish-ready article:
Results saved to: output/data/articles/{slug}/ — one subfolder per article with keywords, brief, and final article.
Applies brand context from docs/ automatically if filled in (voice, tone, audiences).
Cost: ~40–80 NodesHub tokens + Genuino credits. Requires: NodesHub key + Genuino key.
/content-humanizerIteratively rewrites AI-generated text until it passes AI detection:
Cost: 1–3 Genuino credits per iteration. Requires: Genuino key.
/nod-keyword-research — collect keywords (standard preset)./nod-serp-clusters — group keywords into clusters./nod-content-brief — create a brief for each cluster./topic-planner — provide a seed keyword; the agent handles everything.
/nod-content-auditor — identify what's missing vs. the competition./nod-rank-tracker — monitor ranking changes after publishing./nod-competitor-tracker — see who ranks for your keywords./nod-visibility-monitor — compare your visibility against competitors./nod-serp-analysis — dive deeper into specific phrases./nod-serp-analysis — check who ranks and what the SERP looks like./nod-intent-classifier — determine which content type fits the intent./nod-featured-snippet-hunter — find snippet opportunities./nod-paa-miner — collect PAA questions for a FAQ section./keyword-to-publish — provide a seed keyword; the agent handles research, writing, AI check, and audit.
/ai-score — check the current AI probability./content-humanizer — if the score is too high, the agent rewrites flagged sections automatically.Most skills accept the same parameters:
| Parameter | Description | Example |
|---|---|---|
--gl | Country code (Google geolocation) | pl, us, de, uk |
--hl | Language code (Google host language) | pl, en, de |
--device | Device type | desktop, mobile |
--file | File with keywords (one per line) | keywords.txt |
--raw | Return raw JSON | — |
--compare | Compare with previous snapshot | — |
List supported country and language codes:
python3 .claude/skills/nod-nodeshub-api/scripts/params.py countries
python3 .claude/skills/nod-nodeshub-api/scripts/params.py languagesSkills that support HTML output generate interactive reports saved to output/reports/. Each skill lets you choose: create a new report or append a section to an existing one. To brand them:
assets/branding/.--report html.output/reports/ — open it in a browser or send directly to a client.To combine multiple skills into one report, use append_section(report_path, section_html) — each skill run adds a new section without overwriting previous ones.
All skill output is saved under output/ inside the repo folder. The entire folder is gitignored:
output/data/rank-history/{domain}/{YYYY-MM-DD}.json — Rank Tracker snapshotsoutput/data/competitor-tracking/{YYYY-MM-DD}.json — Competitor Tracker snapshotsoutput/data/visibility/{domain}/{YYYY-MM-DD}.json — Visibility Monitor snapshotsoutput/data/topics/{slug}/ — Topic Planner resultsoutput/data/articles/{slug}/ — Keyword to Publish resultsoutput/data/keywords/{slug}/ — Keyword Research outputoutput/data/briefs/{slug}.md — Content Brief auto-savesoutput/data/paa/{slug}_{date}.json — PAA Miner resultsoutput/data/serp-cache/ — SERP cache shared across all skillsoutput/reports/ — HTML and Markdown reportsData accumulates between sessions — you can compare runs, review history, and resume agents from any step.
/docs folderThe /docs folder contains editable Markdown templates for your brand context. Claude Code reads these files automatically at the start of every session, so all generated content (briefs, audits, articles) is aligned with your brand from the start.
audiences.md — your target segments and personasbrand-guidelines.md — logo, colors, typography, componentscompetitors.md — direct/indirect competitors, positioningproduct.md — overview, key features, value proposition, pricingproof-points.md — statistics, case studies, testimonialsvoice-tone.md — brand voice, tone guidelines, do's and don'tsFill these in once and every skill output will automatically reflect your brand.
The repo ships with .claude/settings.local.json.example. After cloning, rename it manually:
mv .claude/settings.local.json.example .claude/settings.local.jsonOpen the file and fill in your keys:
{
"env": {
"NODESHUB_API_KEY": "",
"OPENROUTER_API_KEY": "",
"GENUINO_API_KEY": "",
"JINA_API_KEY": ""
}
}Store API keys here, not in the chat
Never paste API keys directly into the conversation — they may end up in conversation history. .claude/settings.local.json is gitignored and will never be committed to the repository.
SERP results are cached locally to avoid redundant API calls and save tokens. The cache is shared across all skills — if you run two different skills on the same keyword list, only the first skill fetches from the API. Cache entries expire automatically after 24 hours and are stored in output/data/serp-cache/{gl}-{hl}/{keyword}.json.
To change the TTL, edit DEFAULT_TTL_HOURS in:
.claude/skills/nod-nodeshub-api/scripts/serp_cache.pyTo force a fresh fetch immediately, pass --no-cache to any skill.