
Etsy Seo
- 763 installs
- 581 repo stars
- Updated July 23, 2026
- nexscope-ai/ecommerce-skills
etsy-seo is a Claude Code agent skill that scores and optimizes Etsy listing titles, tags, descriptions, attributes, and images on a 0-100 scale for developers who sell on Etsy and need actionable SEO fixes without an Et
About
etsy-seo is a Nexscope AI agent skill (version 1.0.0) that audits Etsy product listings across five weighted dimensions—title (30%), tags (25%), description (20%), attributes (15%), and images (10%)—and returns a 0-100 SEO score with prioritized fixes. Install via `npx skills add nexscope-ai/eCommerce-Skills --skill etsy-seo -g`, then run the bundled Python `analyzer.py` interactively, in `--demo` mode, or with JSON listing payloads. The skill enforces Etsy tag limits (13 tags at 20 characters each), checks titles against the 140-character cap, scores description openings in the first 160 characters, and proposes long-tail keywords with replacement title and tag copy. Developers reach for etsy-seo when tuning Etsy shop listings for marketplace and external search visibility without connecting an Etsy API. Deliverables include per-dimension score bars, unused tag slot counts, rewritten title suggestions, and a HIGH/MEDIUM/LOW action plan table.
- Title keyword fit
- Tag selection
- Attribute completeness
- Listing copy SEO
- Search rank checks
Etsy Seo by the numbers
- 763 all-time installs (skills.sh)
- +83 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #606 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/nexscope-ai/ecommerce-skills --skill etsy-seoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 763 |
|---|---|
| repo stars | ★ 581 |
| Last updated | July 23, 2026 |
| Repository | nexscope-ai/ecommerce-skills ↗ |
How do you score and optimize Etsy listing SEO?
Optimize Etsy titles, tags, attributes, and listing copy so products rank higher in Etsy and external search surfaces.
Who is it for?
Developers or Etsy sellers who want a structured listing SEO audit and rewrite checklist without wiring the Etsy Seller API.
Skip if: Developers building non-Etsy software or teams that need live Etsy search-volume data and automated listing pushes via API.
When should I use this skill?
A developer asks to audit, score, or rewrite Etsy listing titles, tags, descriptions, attributes, or images for better search ranking.
What you get
0-100 SEO score report, five-dimension breakdowns, rewritten titles, 13-tag suggestions, and a prioritized HIGH/MEDIUM/LOW action plan
- 0-100 SEO score report with five dimension breakdowns
- Rewritten title and tag suggestions plus prioritized action plan
- Long-tail keyword recommendations by listing category
By the numbers
- Scores listings across 5 dimensions on a 0-100 scale with weighted title (30%), tags (25%), description (20%), attribute
- Enforces Etsy tag rules: maximum 13 tags at 20 characters each
- Bundles 1 Python analyzer script (analyzer.py) with interactive, --demo, and JSON input modes
Files
Etsy SEO 🔍
Analyze and optimize Etsy listings for better search visibility.
Installation
npx skills add nexscope-ai/eCommerce-Skills --skill etsy-seo -gFeatures
- SEO Score — 0-100 comprehensive rating
- Title Optimization — Keyword placement, length analysis
- Tag Analysis — 13 tag optimization, long-tail suggestions
- Description Analysis — First 160 chars, keyword density
- Attribute Check — Completeness validation
- Keyword Research — Category-based suggestions
- Action Plan — Prioritized improvement roadmap
SEO Scoring Weights
| Dimension | Weight | Max Score |
|---|---|---|
| Title | 30% | 100 |
| Tags | 25% | 100 |
| Description | 20% | 100 |
| Attributes | 15% | 100 |
| Images | 10% | 100 |
Etsy Tag Rules
| Rule | Specification |
|---|---|
| Quantity | Max 13 tags |
| Length | Max 20 characters each |
| Format | Multi-word phrases allowed |
| Strategy | Long-tail + synonyms + attributes |
Title Best Practices
[Core Keyword] + [Attributes] + [Material/Style] + [Use Case/Occasion]
✅ Good:
"Personalized Name Bracelet, Custom Gold Bracelet for Women, Birthday Gift"
❌ Bad:
"Beautiful Handmade Bracelet"Usage
Interactive Mode
python3 scripts/analyzer.pyWith Listing Data
python3 scripts/analyzer.py '{
"title": "Handmade Silver Ring",
"tags": ["silver ring", "handmade jewelry"],
"description": "Beautiful handmade ring...",
"category": "Jewelry"
}'Demo Mode
python3 scripts/analyzer.py --demoOutput Example
🔍 Etsy SEO Analysis Report
Listing: Personalized Name Bracelet
Category: Jewelry > Bracelets
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 SEO SCORE: 72/100 ⚠️
Title: 85/100 ████████░░
Tags: 65/100 ██████░░░░
Description: 70/100 ███████░░░
Attributes: 80/100 ████████░░
Images: 60/100 ██████░░░░
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📝 TITLE ANALYSIS
Current: "Handmade Silver Bracelet"
Length: 24/140 characters ⚠️ Too short
Issues:
• Missing core keyword at start
• No personalization keywords
• Missing occasion/gift keywords
Suggested:
"Personalized Silver Bracelet, Custom Name Bracelet, Birthday Gift for Her"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🏷️ TAG ANALYSIS
Used: 8/13 tags ⚠️ Add 5 more
Current Tags:
✅ silver bracelet
✅ handmade jewelry
⚠️ bracelet (too generic)
Suggested Tags:
+ personalized bracelet
+ custom name jewelry
+ birthday gift for women
+ anniversary gift
+ minimalist bracelet
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 ACTION PLAN
Priority | Action | Impact
──────────────────────────────────────────────
HIGH | Rewrite title with keywords | +15 score
HIGH | Add 5 more tags | +10 score
MEDIUM | Optimize first 160 chars | +8 score
LOW | Add more product photos | +5 scoreKeyword Research
The tool suggests keywords based on:
- Category trends
- Competitor analysis
- Search volume indicators
- Long-tail variations
Optimization Workflow
Analyze current listing
↓
Score each dimension
↓
Identify gaps
↓
Generate suggestions
↓
Prioritize actions
↓
Track improvements---
Part of [Nexscope AI](https://www.nexscope.ai/?co-from=skill) — AI tools for e-commerce sellers.
#!/usr/bin/env python3
"""
Etsy SEO Analyzer - Core Engine
Etsy SEO Analyze - Core Engine
Features:
- SEO Overall Rating (0-100)
- TitleOptimization Suggestions
- 13 TagRecommended
- DescriptionOptimization Suggestions
- KeywordsResearch
- Competitor SEO Analyze
- Store SEO Optimization
- Attribute fillingCheck
Version: 1.0.0
"""
import json
import re
from dataclasses import dataclass, field
from typing import List, Dict, Optional, Tuple
from enum import Enum
from collections import Counter
import sys
class SEOGrade(Enum):
"""SEO Grade"""
EXCELLENT = "A" # 90-100
GOOD = "B" # 70-89
AVERAGE = "C" # 50-69
POOR = "D" # 30-49
CRITICAL = "F" # 0-29
# ============================================================
# Etsy PopularKeywordsLibrary (By category)
# ============================================================
ETSY_KEYWORDS = {
"jewelry": [
"handmade jewelry", "personalized necklace", "custom bracelet",
"birthstone ring", "minimalist earrings", "gold filled",
"sterling silver", "dainty jewelry", "layered necklace",
"initial jewelry", "gift for her", "bridesmaid gift",
],
"home_decor": [
"wall art", "home decor", "farmhouse decor", "boho decor",
"personalized sign", "custom portrait", "macrame", "plant holder",
"candle holder", "throw pillow", "wall hanging", "housewarming gift",
],
"clothing": [
"vintage clothing", "handmade dress", "boho style", "linen clothing",
"custom t-shirt", "embroidered", "sustainable fashion", "cottagecore",
"oversized sweater", "matching set", "loungewear", "festival outfit",
],
"accessories": [
"leather wallet", "handmade bag", "personalized keychain",
"phone case", "hair accessories", "scrunchie", "tote bag",
"crossbody bag", "beanie", "scarf", "belt", "sunglasses chain",
],
"art": [
"digital download", "printable art", "wall print", "custom portrait",
"pet portrait", "watercolor", "illustration", "line art",
"minimalist art", "abstract art", "nursery art", "office decor",
],
"craft_supplies": [
"beads", "charms", "fabric", "yarn", "patterns", "stickers",
"stamps", "dies", "embellishments", "ribbon", "findings",
],
"wedding": [
"wedding invitation", "bridal jewelry", "bridesmaid gift",
"wedding decor", "cake topper", "guest book", "veil",
"wedding favor", "flower girl", "groomsmen gift", "engagement",
],
"default": [
"handmade", "custom", "personalized", "gift", "unique",
"vintage", "boho", "minimalist", "eco friendly", "sustainable",
],
}
# Long-tailKeywordsTemplate
LONG_TAIL_TEMPLATES = [
"{keyword} for women",
"{keyword} for men",
"{keyword} gift",
"personalized {keyword}",
"custom {keyword}",
"handmade {keyword}",
"{keyword} for mom",
"{keyword} for her",
"{keyword} for him",
"vintage {keyword}",
]
# ============================================================
# Data Structures
# ============================================================
@dataclass
class ListingInfo:
"""ProductInformation"""
title: str = ""
tags: List[str] = field(default_factory=list)
description: str = ""
category: str = "default"
attributes: Dict[str, str] = field(default_factory=dict)
price: float = 0.0
images: int = 0
image_alts: List[str] = field(default_factory=list)
url: str = ""
@dataclass
class ShopInfo:
"""StoreInformation"""
name: str = ""
announcement: str = ""
about: str = ""
policies: str = ""
sections: List[str] = field(default_factory=list)
@dataclass
class SEOScore:
"""SEO Rating"""
total: int = 0
grade: SEOGrade = SEOGrade.CRITICAL
title_score: int = 0
tags_score: int = 0
description_score: int = 0
attributes_score: int = 0
images_score: int = 0
@dataclass
class TitleAnalysis:
"""TitleAnalyze"""
length: int = 0
max_length: int = 140
keyword_count: int = 0
has_main_keyword: bool = False
readability: str = ""
issues: List[str] = field(default_factory=list)
suggestions: List[str] = field(default_factory=list)
optimized_title: str = ""
@dataclass
class TagsAnalysis:
"""TagAnalyze"""
count: int = 0
max_count: int = 13
avg_length: float = 0.0
long_tail_count: int = 0
missing_count: int = 0
issues: List[str] = field(default_factory=list)
suggested_tags: List[str] = field(default_factory=list)
@dataclass
class DescriptionAnalysis:
"""DescriptionAnalyze"""
length: int = 0
first_160_chars: str = ""
keyword_density: float = 0.0
has_call_to_action: bool = False
issues: List[str] = field(default_factory=list)
suggestions: List[str] = field(default_factory=list)
@dataclass
class SEOReport:
"""SEO AnalyzeReport"""
listing: ListingInfo
score: SEOScore
title_analysis: TitleAnalysis
tags_analysis: TagsAnalysis
description_analysis: DescriptionAnalysis
keyword_suggestions: List[str]
competitor_insights: List[str]
action_items: List[Dict]
summary: str
summary_zh: str
# ============================================================
# AnalyzeFunction
# ============================================================
def analyze_title(title: str, category: str) -> Tuple[int, TitleAnalysis]:
"""AnalyzeTitle"""
analysis = TitleAnalysis()
analysis.length = len(title)
issues = []
suggestions = []
score = 100
# DegreeCheck (Etsy Restriction 140 Character)
if analysis.length < 40:
issues.append("Title too short (< 40 chars)")
score -= 20
elif analysis.length > 140:
issues.append("Title exceeds 140 character limit")
score -= 30
elif analysis.length < 80:
suggestions.append("Consider using more characters (ideal: 80-140)")
score -= 5
# KeywordsCheck
category_keywords = ETSY_KEYWORDS.get(category, ETSY_KEYWORDS["default"])
title_lower = title.lower()
found_keywords = [kw for kw in category_keywords if kw.lower() in title_lower]
analysis.keyword_count = len(found_keywords)
analysis.has_main_keyword = analysis.keyword_count > 0
if not analysis.has_main_keyword:
issues.append("No category keywords found in title")
score -= 25
elif analysis.keyword_count < 2:
suggestions.append("Add more relevant keywords")
score -= 10
# CanReadabilityCheck
words = title.split()
if len(words) < 5:
issues.append("Title has too few words")
score -= 15
# Special charactersCheck
if re.search(r'[!@#$%^&*()_+=\[\]{}|\\:";\'<>?/]', title):
suggestions.append("Avoid special characters in title")
score -= 5
# all big writeCheck
if title.isupper():
issues.append("Avoid ALL CAPS in title")
score -= 15
analysis.issues = issues
analysis.suggestions = suggestions
analysis.readability = "Good" if len(issues) == 0 else "Needs improvement"
# GenerateOptimization SuggestionsTitle
if not analysis.has_main_keyword and category_keywords:
base_keyword = category_keywords[0]
analysis.optimized_title = f"{base_keyword.title()}, {title}"[:140]
return max(0, score), analysis
def analyze_tags(tags: List[str], category: str) -> Tuple[int, TagsAnalysis]:
"""AnalyzeTag"""
analysis = TagsAnalysis()
analysis.count = len(tags)
issues = []
score = 100
# QuantityCheck (Etsy Allow 13 )
analysis.missing_count = 13 - analysis.count
if analysis.count < 13:
issues.append(f"Only {analysis.count}/13 tags used")
score -= (13 - analysis.count) * 5
# DegreeAnalyze
if tags:
lengths = [len(tag) for tag in tags]
analysis.avg_length = sum(lengths) / len(lengths)
# Check short Tag
short_tags = [t for t in tags if len(t) < 5]
if short_tags:
issues.append(f"{len(short_tags)} tags are too short")
score -= len(short_tags) * 3
# ChecksuperTag (20 Character limit)
long_tags = [t for t in tags if len(t) > 20]
if long_tags:
issues.append(f"{len(long_tags)} tags exceed 20 characters")
score -= len(long_tags) * 5
# Long-tailKeywordsCheck
analysis.long_tail_count = len([t for t in tags if ' ' in t])
if analysis.long_tail_count < 5:
issues.append("Need more multi-word (long-tail) tags")
score -= 10
# heavy complexCheck
unique_tags = set(t.lower() for t in tags)
if len(unique_tags) < len(tags):
issues.append("Duplicate tags found")
score -= 10
analysis.issues = issues
# GenerateRecommendedTag
category_keywords = ETSY_KEYWORDS.get(category, ETSY_KEYWORDS["default"])
existing_lower = set(t.lower() for t in tags)
suggested = []
for kw in category_keywords:
if kw.lower() not in existing_lower and len(suggested) < analysis.missing_count:
suggested.append(kw)
# AddLong-tailVariant
if suggested and len(suggested) < analysis.missing_count:
base = suggested[0].split()[0] if suggested else category_keywords[0]
for template in LONG_TAIL_TEMPLATES:
tag = template.format(keyword=base)
if len(tag) <= 20 and tag.lower() not in existing_lower:
suggested.append(tag)
if len(suggested) >= analysis.missing_count:
break
analysis.suggested_tags = suggested[:analysis.missing_count]
return max(0, score), analysis
def analyze_description(description: str, category: str) -> Tuple[int, DescriptionAnalysis]:
"""AnalyzeDescription"""
analysis = DescriptionAnalysis()
analysis.length = len(description)
issues = []
suggestions = []
score = 100
# DegreeCheck
if analysis.length < 100:
issues.append("Description too short (< 100 chars)")
score -= 30
elif analysis.length < 300:
suggestions.append("Consider a longer description (300+ chars)")
score -= 10
# before 160 CharacterCheck (SearchEngineSummary)
analysis.first_160_chars = description[:160]
# KeywordsdenseDegree
category_keywords = ETSY_KEYWORDS.get(category, ETSY_KEYWORDS["default"])
desc_lower = description.lower()
keyword_count = sum(1 for kw in category_keywords if kw.lower() in desc_lower)
analysis.keyword_density = keyword_count / max(1, len(description.split())) * 100
if keyword_count == 0:
issues.append("No category keywords in description")
score -= 20
# CTA Check
cta_phrases = ["shop now", "buy now", "order today", "click", "add to cart", "check out", "contact"]
analysis.has_call_to_action = any(cta in desc_lower for cta in cta_phrases)
if not analysis.has_call_to_action:
suggestions.append("Add a call-to-action")
score -= 5
# ParagraphCheck
if '\n' not in description and analysis.length > 200:
suggestions.append("Break into paragraphs for readability")
score -= 5
analysis.issues = issues
analysis.suggestions = suggestions
return max(0, score), analysis
def calculate_seo_score(
title_score: int,
tags_score: int,
desc_score: int,
attr_score: int,
img_score: int
) -> SEOScore:
"""CalculateComprehensive SEO Rating"""
# Weight: Title30%, Tag25%, Description20%, Property15%, Image10%
total = int(
title_score * 0.30 +
tags_score * 0.25 +
desc_score * 0.20 +
attr_score * 0.15 +
img_score * 0.10
)
if total >= 90:
grade = SEOGrade.EXCELLENT
elif total >= 70:
grade = SEOGrade.GOOD
elif total >= 50:
grade = SEOGrade.AVERAGE
elif total >= 30:
grade = SEOGrade.POOR
else:
grade = SEOGrade.CRITICAL
return SEOScore(
total=total,
grade=grade,
title_score=title_score,
tags_score=tags_score,
description_score=desc_score,
attributes_score=attr_score,
images_score=img_score,
)
def generate_action_items(
title_analysis: TitleAnalysis,
tags_analysis: TagsAnalysis,
desc_analysis: DescriptionAnalysis,
score: SEOScore
) -> List[Dict]:
"""GenerateActionRecommendation"""
actions = []
# TitleIssue
for issue in title_analysis.issues:
actions.append({
"category": "Title",
"category_zh": "Title",
"priority": "P1",
"action": f"Fix: {issue}",
"action_zh": f"Fix: {issue}",
})
# TagIssue
if tags_analysis.missing_count > 0:
actions.append({
"category": "Tags",
"category_zh": "Tag",
"priority": "P1",
"action": f"Add {tags_analysis.missing_count} more tags",
"action_zh": f"Add {tags_analysis.missing_count} Tag",
})
for issue in tags_analysis.issues:
if "missing" not in issue.lower():
actions.append({
"category": "Tags",
"category_zh": "Tag",
"priority": "P2",
"action": f"Fix: {issue}",
"action_zh": f"Fix: {issue}",
})
# DescriptionIssue
for issue in desc_analysis.issues:
actions.append({
"category": "Description",
"category_zh": "Description",
"priority": "P2",
"action": f"Fix: {issue}",
"action_zh": f"Fix: {issue}",
})
return actions
def analyze_listing(listing: ListingInfo) -> SEOReport:
"""AnalyzeProduct SEO"""
# Analyze each Part
title_score, title_analysis = analyze_title(listing.title, listing.category)
tags_score, tags_analysis = analyze_tags(listing.tags, listing.category)
desc_score, desc_analysis = analyze_description(listing.description, listing.category)
# PropertyRating (simple)
attr_score = 100 if len(listing.attributes) >= 5 else len(listing.attributes) * 20
# ImageRating
img_score = min(100, listing.images * 10) if listing.images > 0 else 0
# Overall Rating
score = calculate_seo_score(title_score, tags_score, desc_score, attr_score, img_score)
# KeywordsRecommendation
category_keywords = ETSY_KEYWORDS.get(listing.category, ETSY_KEYWORDS["default"])
keyword_suggestions = category_keywords[:10]
# ActionRecommendation
action_items = generate_action_items(title_analysis, tags_analysis, desc_analysis, score)
# Summary
summary = f"SEO Score: {score.total}/100 ({score.grade.value}) | Title: {title_score} | Tags: {tags_score} | Desc: {desc_score}"
summary_zh = f"SEO Rating: {score.total}/100 ({score.grade.value}) | Title: {title_score} | Tag: {tags_score} | Description: {desc_score}"
return SEOReport(
listing=listing,
score=score,
title_analysis=title_analysis,
tags_analysis=tags_analysis,
description_analysis=desc_analysis,
keyword_suggestions=keyword_suggestions,
competitor_insights=[],
action_items=action_items,
summary=summary,
summary_zh=summary_zh,
)
# ============================================================
# OutputFormat
# ============================================================
def format_report(report: SEOReport, lang: str = "en") -> str:
"""FormatReport"""
s = report.score
t = report.title_analysis
tg = report.tags_analysis
d = report.description_analysis
grade_colors = {"A": "🟢", "B": "🔵", "C": "🟡", "D": "🟠", "F": "🔴"}
grade_icon = grade_colors.get(s.grade.value, "⚪")
if lang == "zh":
lines = [
"🏷️ **Etsy SEO AnalyzeReport**",
"",
f"**Product**: {report.listing.title[:50]}...",
f"**Category**: {report.listing.category}",
"",
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
f"## {grade_icon} SEO Overall Rating: {s.total}/100 (Grade {s.grade.value})",
"",
"| dimensionDegree | Score | Weight |",
"|------|------|------|",
f"| Title | {s.title_score}/100 | 30% |",
f"| Tag | {s.tags_score}/100 | 25% |",
f"| Description | {s.description_score}/100 | 20% |",
f"| Property | {s.attributes_score}/100 | 15% |",
f"| Image | {s.images_score}/100 | 10% |",
"",
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
"## 📝 TitleAnalyze",
"",
f"**Degree**: {t.length}/{t.max_length} Character",
f"**Keywords**: {t.keyword_count} ",
f"**CanReadability**: {t.readability}",
"",
]
if t.issues:
lines.append("**Issue:**")
for issue in t.issues:
lines.append(f"- ❌ {issue}")
lines.append("")
if t.suggestions:
lines.append("**Recommendation:**")
for sug in t.suggestions:
lines.append(f"- 💡 {sug}")
lines.append("")
if t.optimized_title:
lines.extend([
"**Optimization after TitleExample:**",
f"```{t.optimized_title}```",
"",
])
lines.extend([
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
"## 🏷️ TagAnalyze",
"",
f"** already Use**: {tg.count}/13 ",
f"**Long-tail Keywords**: {tg.long_tail_count} ",
f"**AverageDegree**: {tg.avg_length:.1f} Character",
"",
])
if tg.issues:
lines.append("**Issue:**")
for issue in tg.issues:
lines.append(f"- ❌ {issue}")
lines.append("")
if tg.suggested_tags:
lines.extend([
"**RecommendedAddTag:**",
"",
])
for i, tag in enumerate(tg.suggested_tags, 1):
lines.append(f"{i}. `{tag}`")
lines.append("")
lines.extend([
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
"## 📄 DescriptionAnalyze",
"",
f"**Degree**: {d.length} Character",
f"**KeywordsdenseDegree**: {d.keyword_density:.1f}%",
f"** has CTA**: {'✅ is ' if d.has_call_to_action else '❌ no'}",
"",
])
if d.issues or d.suggestions:
for issue in d.issues:
lines.append(f"- ❌ {issue}")
for sug in d.suggestions:
lines.append(f"- 💡 {sug}")
lines.append("")
if report.keyword_suggestions:
lines.extend([
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
"## 🔑 RecommendedKeywords",
"",
", ".join([f"`{kw}`" for kw in report.keyword_suggestions[:8]]),
"",
])
if report.action_items:
lines.extend([
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
"## 📋 Action Plan",
"",
])
for action in report.action_items[:5]:
lines.append(f"**[{action['priority']}] [{action['category_zh']}]** {action['action_zh']}")
lines.append("")
lines.extend([
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
report.summary_zh,
])
else:
# English version (similar structure)
lines = [
"🏷️ **Etsy SEO Analysis Report**",
"",
f"**Listing**: {report.listing.title[:50]}...",
f"**Category**: {report.listing.category}",
"",
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
"",
f"## {grade_icon} SEO Score: {s.total}/100 (Grade {s.grade.value})",
"",
"| Dimension | Score | Weight |",
"|-----------|-------|--------|",
f"| Title | {s.title_score}/100 | 30% |",
f"| Tags | {s.tags_score}/100 | 25% |",
f"| Description | {s.description_score}/100 | 20% |",
f"| Attributes | {s.attributes_score}/100 | 15% |",
f"| Images | {s.images_score}/100 | 10% |",
"",
"## 📝 Title Analysis",
f"Length: {t.length}/{t.max_length} | Keywords: {t.keyword_count}",
"",
]
if t.issues:
for issue in t.issues:
lines.append(f"- ❌ {issue}")
if tg.suggested_tags:
lines.extend([
"",
"## 🏷️ Suggested Tags",
", ".join([f"`{tag}`" for tag in tg.suggested_tags]),
])
lines.extend(["", report.summary])
return "\n".join(lines)
# ============================================================
# CLI
# ============================================================
def main():
lang = "zh" if "--zh" in sys.argv else "en"
# DemoData
listing = ListingInfo(
title="Beautiful Handmade Bracelet",
tags=["bracelet", "handmade", "gift", "jewelry", "beaded"],
description="A beautiful handmade bracelet perfect for any occasion. Made with high quality beads.",
category="jewelry",
attributes={"material": "beads", "color": "blue"},
images=5,
)
report = analyze_listing(listing)
print(format_report(report, lang))
if __name__ == "__main__":
main()
Related skills
How it compares
Choose etsy-seo when you need a local, no-API listing audit with numeric scores; pair with dedicated keyword-research skills when you need deeper competitor or trend analysis first.
FAQ
Does etsy-seo require an Etsy API key?
etsy-seo does not require an Etsy API key. The skill analyzes listing text you provide—title, tags, description, attributes, and category—and runs the bundled Python analyzer.py locally or through your agent without Seller API credentials.
What does the etsy-seo score measure?
etsy-seo returns a 0-100 SEO score weighted across five dimensions: title (30%), tags (25%), description (20%), attributes (15%), and images (10%). Each dimension gets its own sub-score plus specific rewrite suggestions and a prioritized action plan.
How do you install the etsy-seo skill?
Install etsy-seo with `npx skills add nexscope-ai/eCommerce-Skills --skill etsy-seo -g`, then invoke it in Claude Code, Cursor, Windsurf, or Codex. Run `python3 scripts/analyzer.py` for interactive mode or pass JSON listing data directly.