Skill: bowen-dotcom/aisa-youtube-skill (version 1.0.1)

--- name: openclaw-youtube description: "YouTube SERP Scout for agents. Search top-ranking videos, channels, and trends for content research and competitor tracking." homepage: https://openclaw.ai metadata: {"openclaw":{"emoji":"📺","requires":{"bins":["curl","python3"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY"}} --- # OpenClaw YouTube 📺 **YouTube SERP Scout for autonomous agents. Powered by AIsa.** One API key. Rank discovery. Content research. Competitor tracking. ## 🔥 What Can You Do? ### Content Research ``` "Find top-ranking videos about 'AI agents tutorial' to see what's working" ``` ### Competitor Tracking ``` "Search for videos from competitor channels about 'machine learning'" ``` ### Trend Discovery ``` "What are the top YouTube videos about 'GPT-5' right now?" ``` ### Topic Analysis ``` "Find popular videos on 'autonomous driving' to understand audience interest" ``` ### Channel Discovery ``` "Search for channels creating content about 'crypto trading'" ``` ## Quick Start ```bash export AISA_API_KEY="your-key" ``` --- ## Core Capabilities ### Basic YouTube Search ```bash # Search for videos curl "https://api.aisa.one/apis/v1/youtube/search?engine=youtube&q=AI+agents+tutorial" \ -H "Authorization: Bearer $AISA_API_KEY" ``` ### Search with Country Filter ```bash # Search in specific country (US) curl "https://api.aisa.one/apis/v1/youtube/search?engine=youtube&q=machine+learning&gl=us" \ -H "Authorization: Bearer $AISA_API_KEY" # Search in Japan curl "https://api.aisa.one/apis/v1/youtube/search?engine=youtube&q=AI&gl=jp&hl=ja" \ -H "Authorization: Bearer $AISA_API_KEY" ``` ### Search with Language Filter ```bash # Search with interface language curl "https://api.aisa.one/apis/v1/youtube/search?engine=youtube&q=python+tutorial&hl=en" \ -H "Authorization: Bearer $AISA_API_KEY" # Chinese interface curl "https://api.aisa.one/apis/v1/youtube/search?engine=youtube&q=编程教程&hl=zh-CN&gl=cn" \ -H "Authorization: Bearer $AISA_API_KEY" ``` ### Pagination with Filter Token ```bash # Use sp parameter for pagination or advanced filters curl "https://api.aisa.one/apis/v1/youtube/search?engine=youtube&q=AI&sp=<filter_token>" \ -H "Authorization: Bearer $AISA_API_KEY" ``` --- ## Python Client ```bash # Basic search python3 {baseDir}/scripts/youtube_client.py search --query "AI agents tutorial" # Search with country python3 {baseDir}/scripts/youtube_client.py search --query "machine learning" --country us # Search with language python3 {baseDir}/scripts/youtube_client.py search --query "python tutorial" --lang en # Full options python3 {baseDir}/scripts/youtube_client.py search --query "GPT-5 news" --country us --lang en # Competitor research python3 {baseDir}/scripts/youtube_client.py search --query "OpenAI tutorial" # Trend discovery python3 {baseDir}/scripts/youtube_client.py search --query "AI trends 2025" ``` --- ## Use Cases ### 1. Content Gap Analysis Find what content is ranking well to identify gaps in your strategy: ```python # Search for top videos in your niche results = client.search("AI automation tutorial") # Analyze titles, views, and channels to find opportunities ``` ### 2. Competitor Monitoring Track what competitors are publishing: ```python # Search for competitor brand + topic results = client.search("OpenAI GPT tutorial") # Monitor ranking changes over time ``` ### 3. Keyword Research Discover what topics are trending: ```python # Search broad topics to see what's popular results = client.search("artificial intelligence 2025") # Extract common keywords from top-ranking titles ``` ### 4. Audience Research Understand what your target audience watches: ```python # Search in specific regions results = client.search("coding tutorial", country="jp", lang="ja") # Analyze regional content preferences ``` ### 5. SEO Analysis Analyze how videos rank for specific keywords: ```python # Track ranking positions for target keywords keywords = ["AI tutorial", "machine learning basics", "Python AI"] for kw in keywords: results = client.search(kw) # Record top 10 videos and their channels ``` --- ## API Endpoint Reference | Endpoint | Method | Description | |----------|--------|-------------| | `/youtube/search` | GET | Search YouTube SERP | ## Request Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | engine | string | Yes | Must be `youtube` | | q | string | Yes | Search query | | gl | string | No | Country code (e.g., `us`, `jp`, `uk`, `cn`) | | hl | string | No | Interface language (e.g., `en`, `ja`, `zh-CN`) | | sp | string | No | YouTube filter token for pagination/filters | ## Response Format ```json { "search_metadata": { "id": "search_id", "status": "Success", "created_at": "2025-01-15T12:00:00Z", "request_time_taken": 1.23, "total_time_taken": 1.45 }, "search_results": [ { "video_id": "abc123xyz", "title": "Complete AI Agents Tutorial 2025", "link": "https://www.youtube.com/watch?v=abc123xyz", "channel_name": "AI Academy", "channel_link": "https://www.youtube.com/@aiacademy", "description": "Learn how to build AI agents from scratch...", "views": "125K views", "published_date": "2 weeks ago", "duration": "45:30", "thumbnail": "https://i.ytimg.com/vi/abc123xyz/hqdefault.jpg" } ] } ``` --- ## Country Codes (gl) | Code | Country | |------|---------| | us | United States | | uk | United Kingdom | | jp | Japan | | cn | China | | de | Germany | | fr | France | | kr | South Korea | | in | India | | br | Brazil | | au | Australia | ## Language Codes (hl) | Code | Language | |------|----------| | en | English | | ja | Japanese | | zh-CN | Chinese (Simplified) | | zh-TW | Chinese (Traditional) | | ko | Korean | | de | German | | fr | French | | es | Spanish | | pt | Portuguese | | ru | Russian | --- ## Pricing | API | Cost | |-----|------| | YouTube search | ~$0.002 | Every response includes `usage.cost` and `usage.credits_remaining`. --- ## Get Started 1. Sign up at [aisa.one](https://aisa.one) 2. Get your API key 3. Add credits (pay-as-you-go) 4. Set environment variable: `export AISA_API_KEY="your-key"` ## Full API Reference See [API Reference](https://docs.aisa.one/reference/) for complete endpoint documentation.

path: README.md
content: # OpenClaw YouTube 📺

YouTube SERP Scout for autonomous agents. Search top-ranking videos, channels, and trends.

## Features

- **SERP Search**: Find top-ranking videos for any query
- **Country/Language Filters**: Target specific regions
- **Competitor Research**: Track competitor content
- **Trend Discovery**: Find what's popular now

## Quick Start

```bash
export AISA_API_KEY="your-key"

# Basic search
python scripts/youtube_client.py search --query "AI agents tutorial"

# Search with country filter
python scripts/youtube_client.py search --query "machine learning" --country us

# Find top videos
python scripts/youtube_client.py top-videos --query "GPT-5" --count 10

# Competitor research
python scripts/youtube_client.py competitor --name "OpenAI" --topic "tutorial"
```

## Use Cases

1. **Content Research** - Find what's ranking to plan your content
2. **Competitor Tracking** - Monitor competitor YouTube presence
3. **Trend Discovery** - Identify trending topics
4. **Keyword Research** - Discover popular search terms
5. **Audience Research** - Understand regional preferences

## Documentation

See [SKILL.md](SKILL.md) for complete API documentation.


path: scripts/youtube_client.py
content: #!/usr/bin/env python3
"""
OpenClaw YouTube - AIsa API Client
YouTube SERP Scout for content research, competitor tracking, and trend discovery.

Usage:
    python youtube_client.py search --query <query> [--country <code>] [--lang <code>]
    python youtube_client.py search --query "AI agents tutorial" --country us --lang en
"""

import argparse
import json
import os
import sys
import urllib.request
import urllib.parse
import urllib.error
from typing import Any, Dict, List, Optional


class YouTubeClient:
    """OpenClaw YouTube - YouTube SERP Scout API Client."""
    
    BASE_URL = "https://api.aisa.one/apis/v1"
    
    def __init__(self, [REDACTED_SECRET] = None):
        """Initialize the client with an API key."""
        self.[REDACTED_SECRET] or os.environ.get("AISA_API_KEY")
        if not self.api_key:
            raise ValueError(
                "AISA_API_KEY is required. Set it via environment variable or pass to constructor."
            )
    
    def _request(
        self, 
        method: str, 
        endpoint: str, 
        params: Optional[Dict[str, Any]] = None,
        data: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Make an HTTP request to the AIsa API."""
        url = f"{self.BASE_URL}{endpoint}"
        
        if params:
            query_string = urllib.parse.urlencode(
                {k: v for k, v in params.items() if v is not None}
            )
            url = f"{url}?{query_string}"
        
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
            "User-Agent": "OpenClaw-YouTube/1.0",
            "Accept": "application/json"
        }
        
        request_data = None
        if data:
            request_data = json.dumps(data).encode("utf-8")
        
        if method == "POST" and request_data is None:
            request_data = b"{}"
        
        req = urllib.request.Request(url, data=request_data, headers=headers, method=method)
        
        try:
            with urllib.request.urlopen(req, timeout=60) as response:
                return json.loads(response.read().decode("utf-8"))
        except urllib.error.HTTPError as e:
            error_body = e.read().decode("utf-8")
            try:
                return json.loads(error_body)
            except json.JSONDecodeError:
                return {"success": False, "error": {"code": str(e.code), "message": error_body}}
        except urllib.error.URLError as e:
            return {"success": False, "error": {"code": "NETWORK_ERROR", "message": str(e.reason)}}
    
    # ==================== YouTube Search API ====================
    
    def search(
        self, 
        query: str, 
        country: Optional[str] = None,
        language: Optional[str] = None,
        filter_token: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Search YouTube SERP.
        
        Args:
            query: Search query string
            country: Country code (e.g., 'us', 'jp', 'uk')
            language: Interface language (e.g., 'en', 'ja', 'zh-CN')
            filter_token: YouTube filter token for pagination or advanced filters
            
        Returns:
            Search results with video information
        """
        params = {
            "engine": "youtube",
            "q": query
        }
        
        if country:
            params["gl"] = country.lower()
        if language:
            params["hl"] = language
        if filter_token:
            params["sp"] = filter_token
            
        return self._request("GET", "/youtube/search", params=params)
    
    def search_videos(
        self, 
        query: str, 
        country: Optional[str] = None,
        language: Optional[str] = None,
        max_results: int = 10
    ) -> List[Dict[str, Any]]:
        """
        Search and return video list with simplified format.
        
        Args:
            query: Search query string
            country: Country code
            language: Interface language
            max_results: Maximum number of results to return
            
        Returns:
            List of video dictionaries
        """
        result = self.search(query, country, language)
        
        videos = []
        # Handle different response formats
        search_results = result.get("videos", result.get("search_results", result.get("video_results", [])))
        
        for item in search_results[:max_results]:
            # Handle nested channel object
            channel_info = item.get("channel", {})
            channel_name = item.get("channel_name") or channel_info.get("title") or channel_info.get("name")
            
            video = {
                "video_id": item.get("id", item.get("video_id")),
                "title": item.get("title"),
                "link": item.get("link"),
                "channel": channel_name,
                "views": item.get("views"),
                "duration": item.get("length", item.get("duration")),
                "published": item.get("published_time", item.get("published_date", item.get("published")))
            }
            videos.append(video)
            
        return videos
    
    def find_top_videos(
        self, 
        query: str, 
        count: int = 5,
        country: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Find top-ranking videos for a query.
        
        Args:
            query: Search query
            count: Number of top videos to return
            country: Country code
            
        Returns:
            Dictionary with query and top videos
        """
        videos = self.search_videos(query, country=country, max_results=count)
        
        return {
            "query": query,
            "country": country or "global",
            "top_videos": videos,
            "total_found": len(videos)
        }
    
    def competitor_research(
        self, 
        competitor_name: str, 
        topic: Optional[str] = None,
        country: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Research competitor's YouTube presence.
        
        Args:
            competitor_name: Competitor brand/channel name
            topic: Optional topic to filter
            country: Country code
            
        Returns:
            Competitor research results
        """
        query = competitor_name
        if topic:
            query = f"{competitor_name} {topic}"
            
        result = self.search(query, country=country)
        
        # Extract unique channels from results
        channels = {}
        videos = result.get("videos", result.get("search_results", result.get("video_results", [])))
        
        for item in videos:
            channel_info = item.get("channel", {})
            channel_name = item.get("channel_name") or channel_info.get("title") or channel_info.get("name") or "Unknown"
            if channel_name not in channels:
                channels[channel_name] = {
                    "name": channel_name,
                    "link": item.get("channel_link", item.get("channel", {}).get("link")),
                    "video_count": 0
                }
            channels[channel_name]["video_count"] += 1
        
        return {
            "competitor": competitor_name,
            "topic": topic,
            "total_videos_found": len(videos),
            "channels_found": list(channels.values()),
            "top_videos": videos[:5]
        }


def main():
    """Main CLI entry point."""
    parser = argparse.ArgumentParser(
        description="OpenClaw YouTube - YouTube SERP Scout",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
    %(prog)s search --query "AI agents tutorial"
    %(prog)s search --query "machine learning" --country us
    %(prog)s search --query "python tutorial" --lang en
    %(prog)s search --query "GPT-5 news" --country us --lang en
        """
    )
    
    subparsers = parser.add_subparsers(dest="command", help="Command")
    
    # search
    search_parser = subparsers.add_parser("search", help="Search YouTube")
    search_parser.add_argument("--query", "-q", required=True, help="Search query")
    search_parser.add_argument("--country", "-c", help="Country code (e.g., us, jp, uk)")
    search_parser.add_argument("--lang", "-l", help="Interface language (e.g., en, ja, zh-CN)")
    search_parser.add_argument("--filter", "-f", help="YouTube filter token")
    
    # top-videos
    top_parser = subparsers.add_parser("top-videos", help="Find top-ranking videos")
    top_parser.add_argument("--query", "-q", required=True, help="Search query")
    top_parser.add_argument("--count", "-n", type=int, default=5, help="Number of videos")
    top_parser.add_argument("--country", "-c", help="Country code")
    
    # competitor
    comp_parser = subparsers.add_parser("competitor", help="Competitor research")
    comp_parser.add_argument("--name", "-n", required=True, help="Competitor name")
    comp_parser.add_argument("--topic", "-t", help="Topic filter")
    comp_parser.add_argument("--country", "-c", help="Country code")
    
    args = parser.parse_args()
    
    if not args.command:
        parser.print_help()
        sys.exit(1)
    
    try:
        client = YouTubeClient()
    except ValueError as e:
        print(json.dumps({"success": False, "error": {"code": "AUTH_ERROR", "message": str(e)}}))
        sys.exit(1)
    
    result = None
    
    if args.command == "search":
        result = client.search(
            args.query,
            country=args.country,
            language=args.lang,
            filter_token=args.filter
        )
    elif args.command == "top-videos":
        result = client.find_top_videos(
            args.query,
            count=args.count,
            country=args.country
        )
    elif args.command == "competitor":
        result = client.competitor_research(
            args.name,
            topic=args.topic,
            country=args.country
        )
    
    if result:
        output = json.dumps(result, indent=2, ensure_ascii=False)
        try:
            print(output)
        except UnicodeEncodeError:
            print(json.dumps(result, indent=2, ensure_ascii=True))
        sys.exit(0 if result.get("success", True) != False else 1)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)