{
  "data": {
    "id": "19hYhFgrgWdPSTJvc",
    "actId": "4Y3stChofe6TqJpa9",
    "userId": "NL4ThgItAwaoBlffQ",
    "startedAt": "2026-10-19T06:38:59.154Z",
    "finishedAt": "2026-10-19T06:39:14.170Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 14942,
      "runTimeSecs": 14.942,
      "computeUnits": 0.016602222222222223,
      "imageSizeBytes": 317894656
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "zifX0mkAFMHe8CatY",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"YouTube Shorts Scraper Input\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"required\": [],\n  \"properties\": {\n    \"inputMode\": {\n      \"title\": \"Input Mode\",\n      \"type\": \"string\",\n      \"default\": \"channel\",\n      \"enum\": [\n        \"channel\",\n        \"hashtag\",\n        \"search\",\n        \"direct\",\n        \"sound\"\n      ],\n      \"enumTitles\": [\n        \"Channel Shorts Tab\",\n        \"Hashtag Shorts\",\n        \"Keyword Search\",\n        \"Direct Shorts URLs\",\n        \"Shorts by Sound\"\n      ],\n      \"description\": \"Where to find Shorts. 'channel' reads a channel's Shorts tab, 'hashtag' a hashtag's Shorts feed, 'search' YouTube search results filtered to Shorts, 'direct' specific Shorts URLs or video IDs, 'sound' finds other Shorts that use the same sound as seed Shorts.\"\n    },\n    \"channelUrls\": {\n      \"title\": \"Channel URLs\",\n      \"type\": \"array\",\n      \"description\": \"YouTube channel URLs, handles (@username), or channel IDs (UCxxx). Required when inputMode is 'channel'.\",\n      \"editor\": \"stringList\",\n      \"default\": [\n        \"https://www.youtube.com/@MrBeast\"\n      ],\n      \"prefill\": [\n        \"https://www.youtube.com/@MrBeast\"\n      ],\n      \"items\": {\n        \"type\": \"string\"\n      }\n    },\n    \"hashtags\": {\n      \"title\": \"Hashtags\",\n      \"type\": \"array\",\n      \"editor\": \"stringList\",\n      \"description\": \"Hashtags to scrape Shorts from (e.g. #cooking, sports). Required when inputMode is 'hashtag'. Include or omit the # prefix.\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"prefill\": [\n        \"#shorts\"\n      ]\n    },\n    \"searchQueries\": {\n      \"title\": \"Search Queries\",\n      \"type\": \"array\",\n      \"editor\": \"stringList\",\n      \"description\": \"Keywords to search on YouTube, keeping only Shorts results. Required when inputMode is 'search'.\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"prefill\": [\n        \"cooking shorts\"\n      ]\n    },\n    \"shortUrls\": {\n      \"title\": \"Shorts URLs or IDs\",\n      \"type\": \"array\",\n      \"editor\": \"stringList\",\n      \"description\": \"Shorts URLs, youtu.be links, watch URLs, or 11-character video IDs. Required when inputMode is 'direct'; used as the sound seeds when inputMode is 'sound'.\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"default\": [\n        \"https://www.youtube.com/shorts/pfUG56htjBc\"\n      ],\n      \"prefill\": [\n        \"https://www.youtube.com/shorts/pfUG56htjBc\"\n      ]\n    },\n    \"sortShortsBy\": {\n      \"title\": \"Sort Shorts By\",\n      \"type\": \"string\",\n      \"description\": \"Order of the channel's Shorts tab, matching the buttons YouTube shows: newest first (default), most popular, or oldest first. Falls back to newest when a channel does not offer the chosen order.\",\n      \"editor\": \"select\",\n      \"default\": \"newest\",\n      \"prefill\": \"newest\",\n      \"enum\": [\n        \"newest\",\n        \"popular\",\n        \"oldest\"\n      ],\n      \"enumTitles\": [\n        \"Newest first\",\n        \"Most popular\",\n        \"Oldest first\"\n      ]\n    },\n    \"maxShorts\": {\n      \"title\": \"Max Shorts Per Source\",\n      \"type\": \"integer\",\n      \"description\": \"Maximum number of Shorts to return per channel, hashtag or search query (per run in direct mode).\",\n      \"default\": 3,\n      \"prefill\": 3,\n      \"minimum\": 1,\n      \"maximum\": 10000,\n      \"editor\": \"number\"\n    },\n    \"includeDetails\": {\n      \"title\": \"Include Exact Details\",\n      \"type\": \"boolean\",\n      \"description\": \"Visit each Short's page for exact views, likes, comment count, publish date, duration, category, sound and captions. Turn off for a faster, cheaper listing-only run (views as displayed, no likes or dates).\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"includeMembersOnly\": {\n      \"title\": \"Include Members-Only Shorts\",\n      \"type\": \"boolean\",\n      \"description\": \"Channel mode only. Also collect members-only Shorts when YouTube offers the 'Members only' filter on that channel's Shorts tab (a gradual rollout — the actor retries the tab a few times and notes it in the status message when unavailable). Member rows count toward maxShorts and cost up to two player requests each.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"oldestPostDate\": {\n      \"title\": \"Oldest Post Date\",\n      \"type\": \"string\",\n      \"description\": \"Only include Shorts published on or after this date. Format: YYYY-MM-DD. Leave empty to include all. Works in channel, search and direct modes; hashtag mode needs 'Include Exact Details' because dates come from each Short's page.\",\n      \"editor\": \"textfield\",\n      \"nullable\": true,\n      \"prefill\": \"\"\n    },\n    \"market\": {\n      \"title\": \"Market / Region\",\n      \"type\": \"string\",\n      \"description\": \"YouTube market/region for localized content. Affects the language of metadata returned.\",\n      \"editor\": \"select\",\n      \"default\": \"US\",\n      \"enum\": [\n        \"US\",\n        \"GB\",\n        \"CA\",\n        \"AU\",\n        \"DE\",\n        \"FR\",\n        \"JP\",\n        \"KR\",\n        \"BR\",\n        \"IN\",\n        \"MX\",\n        \"IT\",\n        \"ES\",\n        \"NL\",\n        \"PL\",\n        \"RU\",\n        \"SE\",\n        \"NO\",\n        \"DK\",\n        \"FI\",\n        \"ZA\",\n        \"SG\",\n        \"HK\",\n        \"TW\",\n        \"ID\",\n        \"MY\",\n        \"TH\",\n        \"PH\",\n        \"AR\",\n        \"CO\",\n        \"CL\"\n      ],\n      \"enumTitles\": [\n        \"United States\",\n        \"United Kingdom\",\n        \"Canada\",\n        \"Australia\",\n        \"Germany\",\n        \"France\",\n        \"Japan\",\n        \"South Korea\",\n        \"Brazil\",\n        \"India\",\n        \"Mexico\",\n        \"Italy\",\n        \"Spain\",\n        \"Netherlands\",\n        \"Poland\",\n        \"Russia\",\n        \"Sweden\",\n        \"Norway\",\n        \"Denmark\",\n        \"Finland\",\n        \"South Africa\",\n        \"Singapore\",\n        \"Hong Kong\",\n        \"Taiwan\",\n        \"Indonesia\",\n        \"Malaysia\",\n        \"Thailand\",\n        \"Philippines\",\n        \"Argentina\",\n        \"Colombia\",\n        \"Chile\"\n      ]\n    }\n  }\n}",
    "readme": "# YouTube Shorts Metadata Extractor for Channels & Hashtags\n\nCollect YouTube Shorts from a channel's Shorts tab, a hashtag feed, keyword search results, a list of Shorts links, or a sound — and turn each one into a row with exact view and like counts, comment count, publish date, duration, category, sound title, caption languages and the uploader's channel profile.\n\n## What this actor does\n\n- **Five ways in:** a channel's Shorts tab (`channel`), a hashtag's Shorts feed (`hashtag`), YouTube search filtered to Shorts (`search`), specific Shorts URLs/IDs (`direct`), or other Shorts that use the same sound as seed links (`sound`)\n- **Channel sorting:** newest, most popular or oldest first, matching the buttons YouTube shows on the Shorts tab\n- **Exact per-Short metrics:** exact views, likes, comment count, duration, publish date, upload date, category and sound — pulled from each Short's own page, not the rounded listing values\n- **Captions:** whether captions exist and which language tracks are available\n- **Channel profile on every channel-mode row:** subscribers, description, join date, location, links, avatar, banner, total views, total videos and verification\n- **Deep back-catalogs:** page through a channel's entire Shorts list (up to 10,000 per channel per run)\n- **Fast listing-only mode:** turn `includeDetails` off to skip per-Short pages and return titles, links, thumbnails and displayed view counts quickly\n- **Members-only Shorts (optional):** `includeMembersOnly` also collects a channel's members-only Shorts when YouTube offers that filter on its Shorts tab; when the filter is not rolled out for the channel the run notes it and returns the public listing\n- **Exact numbers only or nothing:** a field YouTube does not expose for a given Short is omitted instead of emitted as `null` or a guess; long-form videos are never mixed in\n\n## Output per Short\n\nOne row per Short. Optional fields are omitted when YouTube does not expose a value.\n\n| Field | Description |\n|-------|-------------|\n| `videoId` | The Short's YouTube video ID |\n| `title` | Short's title |\n| `url` | `https://www.youtube.com/shorts/<videoId>` |\n| `thumbnailUrl` | Vertical thumbnail (9:16) |\n| `viewCount` | Exact view count (or the displayed count when `includeDetails` is off) |\n| `viewCountText` | View count as displayed, e.g. `\"30M views\"` |\n| `likes` | Exact like count |\n| `commentsCount` | Exact comment count |\n| `commentsTurnedOff` | `true` when the uploader disabled comments |\n| `duration` | Duration as `MM:SS` / `HH:MM:SS` |\n| `durationSeconds` | Duration in seconds |\n| `publishDate` | Publish date with timezone, e.g. `\"2026-10-16T09:00:02-07:00\"` |\n| `uploadDate` | Same instant in UTC, e.g. `\"2026-10-16T16:00:02.000Z\"` |\n| `category` | YouTube category, e.g. `\"Entertainment\"` |\n| `soundTitle` | `\"Original Sound\"` or the track name when a licensed sound is used |\n| `captionLanguages` | Caption language codes, e.g. `[\"en\"]` |\n| `subtitles` | `\"available\"` when caption tracks exist |\n| `description` | Short's description text |\n| `hashtags` | Hashtags found in the description (hashtag mode returns the searched tag) |\n| `postedTimeText` | Relative publish label, e.g. `\"2 days ago\"` |\n| `formats` | Available video stream qualities |\n| `isAgeRestricted` / `isMembersOnly` / `isUnlisted` | Present and `true` only when that applies |\n| `channelName`, `channelUrl`, `channelId`, `channelUsername` | Uploader identity (channel mode) |\n| `numberOfSubscribers`, `channelTotalVideos`, `channelTotalViews`, `channelJoinedDate`, `channelLocation`, `channelDescription`, `channelDescriptionLinks`, `channelAvatarUrl`, `channelBannerUrl`, `isChannelVerified` | Channel profile fields (channel mode) |\n| `aboutChannelInfo` | Nested object repeating the channel profile fields |\n| `searchQuery` | The keyword (search mode) |\n| `matchedSoundTitle` | The seed sound a `sound`-mode row was found by |\n| `sourceType` / `source` | Which input produced the row |\n| `order` | Position in the result set, starting at 0 |\n| `scrapedAt` | ISO timestamp of when the row was scraped |\n\n### Error and status rows\n\n- A channel, hashtag or search query that cannot be read produces an error row in the same dataset: `recordType: \"error\"` with `sourceType`, `source`, `errorCode` (`CHANNEL_NOT_FOUND`, `NO_SHORTS`, `HASHTAG_EMPTY`, `SEARCH_EMPTY`, `INVALID_SHORT_URL`) and an `error` message. The run itself still succeeds.\n- When a run finds no Shorts at all, the dataset gets one status row instead: `recordType: \"status\"`, `status: \"no_results\"`, plus the inputs you supplied.\n\n## Input\n\n| Field | Type | Required | Default | Description |\n|-------|------|----------|---------|-------------|\n| `inputMode` | string | No | `\"channel\"` | `channel`, `hashtag`, `search`, `direct` or `sound` |\n| `channelUrls` | string[] | Channel mode | `[\"https://www.youtube.com/@MrBeast\"]` | Channel URLs, `@handles` or channel IDs |\n| `hashtags` | string[] | Hashtag mode | – | Hashtags like `#cooking` (with or without `#`) |\n| `searchQueries` | string[] | Search mode | – | Keywords; results are filtered to Shorts |\n| `shortUrls` | string[] | Direct / sound mode | `[\"https://www.youtube.com/shorts/pfUG56htjBc\"]` | Shorts URLs, youtu.be/watch links, or 11-character IDs; seed links in sound mode |\n| `sortShortsBy` | string | No | `\"newest\"` | Channel Shorts order: `\"newest\"`, `\"popular\"` or `\"oldest\"` |\n| `maxShorts` | integer | No | `3` | Max Shorts per channel, hashtag or query (per run in direct mode), 1–10000 |\n| `includeDetails` | boolean | No | true | Visit each Short's page for exact metrics, dates, category, sound and captions. `false` = fast listing-only rows |\n| `includeMembersOnly` | boolean | No | false | Channel mode: also collect members-only Shorts when YouTube offers the `Members only` filter (rollout-dependent; reads each member Short's own page) |\n| `oldestPostDate` | string | No | – | Only Shorts published on/after this date (`YYYY-MM-DD`) |\n| `market` | string | No | `\"US\"` | Region whose YouTube settings are used (31 countries), which sets the language of returned metadata |\n\n### Supported inputs per mode\n\n- `channel` — `@MrBeast`, `https://www.youtube.com/@MrBeast`, `https://youtube.com/channel/UCX6OQ3DkcsbYNE6H8uQQuVA`, a bare `UC…` ID, or a name\n- `hashtag` — `#cooking`, `cooking`\n- `search` — any keyword phrase, e.g. `\"cooking shorts\"`\n- `direct` — `https://www.youtube.com/shorts/<id>`, `https://youtu.be/<id>`, `watch?v=<id>`, or the bare 11-character ID\n- `sound` — the same forms; each link's sound is looked up and its other Shorts are returned\n\n### Example: a channel's most popular Shorts\n\n```json\n{\n  \"inputMode\": \"channel\",\n  \"channelUrls\": [\"https://www.youtube.com/@MrBeast\"],\n  \"sortShortsBy\": \"popular\",\n  \"maxShorts\": 50\n}\n```\n\n### Example: keyword search\n\n```json\n{\n  \"inputMode\": \"search\",\n  \"searchQueries\": [\"cooking shorts\", \"home workout\"],\n  \"maxShorts\": 100\n}\n```\n\n### Example: hashtag feed\n\n```json\n{\n  \"inputMode\": \"hashtag\",\n  \"hashtags\": [\"#cooking\"],\n  \"maxShorts\": 100\n}\n```\n\n### Example: specific Shorts, fast listing only\n\n```json\n{\n  \"inputMode\": \"direct\",\n  \"shortUrls\": [\"https://www.youtube.com/shorts/pfUG56htjBc\"],\n  \"includeDetails\": false\n}\n```\n\n## Use cases\n\n- **Benchmark a creator's Shorts** — exact views, likes and comments for their entire Shorts back-catalog, sorted by newest or most popular\n- **Trend and hashtag research** — see what creators and videos are active under a hashtag right now, then drill into exact engagement\n- **Keyword discovery** — find Shorts around a topic with YouTube search and rank them by exact performance\n- **Sound tracking** — group Shorts by the sound they use (`Original Sound` vs licensed tracks)\n- **Content cadence monitoring** — schedule a run with `oldestPostDate` to collect only recent uploads\n- **Data enrichment** — paste a list of Shorts links and receive clean rows with full metadata for each\n\n## FAQ\n\n**Do I need a YouTube account, API key or proxies?** No. The actor reads YouTube's public pages and picks its network route automatically; there is nothing to configure.\n\n**Why do some rows miss `likes` or `commentsCount`?** YouTube hides those counters for some Shorts, or the page was briefly gated; the actor retries and falls back to its secondary source, and simply omits what is genuinely unavailable rather than guessing.\n\n**What is the difference between `viewCount` and `viewCountText`?** With `includeDetails` on, `viewCount` is the exact number from the Short's page and `viewCountText` is YouTube's display string (e.g. `\"30M views\"`). With `includeDetails` off, only the displayed value is available.\n\n**How deep can a channel's Shorts history go?** To the end of the channel's public Shorts list, up to 10,000 rows per channel per run. `maxShorts` caps each source.\n\n**How deep can a hashtag go?** YouTube's hashtag Shorts feed exposes one page (roughly 36–37 Shorts) and rejects further pages. The actor fills the rest of `maxShorts` from YouTube search results for `\"#tag shorts\"`, which keeps returning Shorts that carry the tag.\n\n**What does sound mode return?** For each seed link you provide, the actor lists other Shorts that use the same sound (8–15 per seed) and tags them with `matchedSoundTitle`. The seed itself is not included; a seed whose sound has no feed yields a `SOUND_UNAVAILABLE` error row.\n\n**Can I get members-only Shorts?** Yes, when YouTube offers the `Members only` filter on that channel's Shorts tab — the filter is a gradual rollout, so the actor retries the tab a few times and, when it is not offered, says so in the status message and returns the public listing. Members-only Shorts count toward `maxShorts` and cost up to two player requests each; they carry `isMembersOnly: true`. YouTube sometimes serves the filter with an empty listing on datacenter networks — the actor then says so and returns the public listing.\n\n**Which sort orders are supported?** The three YouTube offers on the Shorts tab: newest, most popular and oldest. If a channel does not show those buttons, the run falls back to newest order and says so in the status message.\n\n**Are view counts live?** They are exact at the moment of scraping. YouTube updates counters continuously, so repeated runs can show slightly different numbers.\n\n**Is this affiliated with YouTube?** No. This is an independent tool and is not affiliated with, endorsed by, or sponsored by YouTube or Google.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "youtube-shorts-metadata-extractor-for-channels",
      "title": "YouTube Shorts Metadata Extractor for Channels & Hashtags",
      "description": "Pull Shorts from a channel, hashtag, keyword search, specific links or a sound — exact views, likes, comment counts, ISO dates, category, sound and full channel detail. Sort by latest, popular or oldest.",
      "version": "1.0",
      "buildTag": "latest",
      "defaultMemoryMbytes": 1024,
      "minMemoryMbytes": 1024,
      "environmentVariables": {},
      "dockerfile": "../Dockerfile",
      "input": {
        "title": "YouTube Shorts Scraper Input",
        "type": "object",
        "schemaVersion": 1,
        "required": [],
        "properties": {
          "inputMode": {
            "title": "Input Mode",
            "type": "string",
            "default": "channel",
            "enum": [
              "channel",
              "hashtag",
              "search",
              "direct",
              "sound"
            ],
            "enumTitles": [
              "Channel Shorts Tab",
              "Hashtag Shorts",
              "Keyword Search",
              "Direct Shorts URLs",
              "Shorts by Sound"
            ],
            "description": "Where to find Shorts. 'channel' reads a channel's Shorts tab, 'hashtag' a hashtag's Shorts feed, 'search' YouTube search results filtered to Shorts, 'direct' specific Shorts URLs or video IDs, 'sound' finds other Shorts that use the same sound as seed Shorts."
          },
          "channelUrls": {
            "title": "Channel URLs",
            "type": "array",
            "description": "YouTube channel URLs, handles (@username), or channel IDs (UCxxx). Required when inputMode is 'channel'.",
            "editor": "stringList",
            "default": [
              "https://www.youtube.com/@MrBeast"
            ],
            "prefill": [
              "https://www.youtube.com/@MrBeast"
            ],
            "items": {
              "type": "string"
            }
          },
          "hashtags": {
            "title": "Hashtags",
            "type": "array",
            "editor": "stringList",
            "description": "Hashtags to scrape Shorts from (e.g. #cooking, sports). Required when inputMode is 'hashtag'. Include or omit the # prefix.",
            "items": {
              "type": "string"
            },
            "prefill": [
              "#shorts"
            ]
          },
          "searchQueries": {
            "title": "Search Queries",
            "type": "array",
            "editor": "stringList",
            "description": "Keywords to search on YouTube, keeping only Shorts results. Required when inputMode is 'search'.",
            "items": {
              "type": "string"
            },
            "prefill": [
              "cooking shorts"
            ]
          },
          "shortUrls": {
            "title": "Shorts URLs or IDs",
            "type": "array",
            "editor": "stringList",
            "description": "Shorts URLs, youtu.be links, watch URLs, or 11-character video IDs. Required when inputMode is 'direct'; used as the sound seeds when inputMode is 'sound'.",
            "items": {
              "type": "string"
            },
            "default": [
              "https://www.youtube.com/shorts/pfUG56htjBc"
            ],
            "prefill": [
              "https://www.youtube.com/shorts/pfUG56htjBc"
            ]
          },
          "sortShortsBy": {
            "title": "Sort Shorts By",
            "type": "string",
            "description": "Order of the channel's Shorts tab, matching the buttons YouTube shows: newest first (default), most popular, or oldest first. Falls back to newest when a channel does not offer the chosen order.",
            "editor": "select",
            "default": "newest",
            "prefill": "newest",
            "enum": [
              "newest",
              "popular",
              "oldest"
            ],
            "enumTitles": [
              "Newest first",
              "Most popular",
              "Oldest first"
            ]
          },
          "maxShorts": {
            "title": "Max Shorts Per Source",
            "type": "integer",
            "description": "Maximum number of Shorts to return per channel, hashtag or search query (per run in direct mode).",
            "default": 3,
            "prefill": 3,
            "minimum": 1,
            "maximum": 10000,
            "editor": "number"
          },
          "includeDetails": {
            "title": "Include Exact Details",
            "type": "boolean",
            "description": "Visit each Short's page for exact views, likes, comment count, publish date, duration, category, sound and captions. Turn off for a faster, cheaper listing-only run (views as displayed, no likes or dates).",
            "default": true,
            "prefill": true
          },
          "includeMembersOnly": {
            "title": "Include Members-Only Shorts",
            "type": "boolean",
            "description": "Channel mode only. Also collect members-only Shorts when YouTube offers the 'Members only' filter on that channel's Shorts tab (a gradual rollout — the actor retries the tab a few times and notes it in the status message when unavailable). Member rows count toward maxShorts and cost up to two player requests each.",
            "default": false,
            "prefill": false
          },
          "oldestPostDate": {
            "title": "Oldest Post Date",
            "type": "string",
            "description": "Only include Shorts published on or after this date. Format: YYYY-MM-DD. Leave empty to include all. Works in channel, search and direct modes; hashtag mode needs 'Include Exact Details' because dates come from each Short's page.",
            "editor": "textfield",
            "nullable": true,
            "prefill": ""
          },
          "market": {
            "title": "Market / Region",
            "type": "string",
            "description": "YouTube market/region for localized content. Affects the language of metadata returned.",
            "editor": "select",
            "default": "US",
            "enum": [
              "US",
              "GB",
              "CA",
              "AU",
              "DE",
              "FR",
              "JP",
              "KR",
              "BR",
              "IN",
              "MX",
              "IT",
              "ES",
              "NL",
              "PL",
              "RU",
              "SE",
              "NO",
              "DK",
              "FI",
              "ZA",
              "SG",
              "HK",
              "TW",
              "ID",
              "MY",
              "TH",
              "PH",
              "AR",
              "CO",
              "CL"
            ],
            "enumTitles": [
              "United States",
              "United Kingdom",
              "Canada",
              "Australia",
              "Germany",
              "France",
              "Japan",
              "South Korea",
              "Brazil",
              "India",
              "Mexico",
              "Italy",
              "Spain",
              "Netherlands",
              "Poland",
              "Russia",
              "Sweden",
              "Norway",
              "Denmark",
              "Finland",
              "South Africa",
              "Singapore",
              "Hong Kong",
              "Taiwan",
              "Indonesia",
              "Malaysia",
              "Thailand",
              "Philippines",
              "Argentina",
              "Colombia",
              "Chile"
            ]
          }
        }
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "YouTube Shorts Metadata Extractor for Channels & Hashtags Output",
        "description": "YouTube Shorts with exact view, like, comment, date, duration, category and sound metadata, scraped by channel, hashtag, keyword search or direct URL.",
        "properties": {
          "shorts": {
            "type": "string",
            "title": "YouTube Shorts Metadata Extractor dataset",
            "description": "Dataset containing all scraped Shorts with exact engagement metrics, dates and channel metadata.",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "views": {
            "overview": {
              "title": "Overview",
              "transformation": {
                "fields": [
                  "title",
                  "type",
                  "videoId",
                  "viewCount",
                  "likes",
                  "duration",
                  "uploadDate",
                  "url",
                  "hashtags",
                  "soundTitle",
                  "channelName",
                  "channelId",
                  "scrapedAt"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "title": {
                    "label": "Title",
                    "format": "text"
                  },
                  "type": {
                    "label": "Type",
                    "format": "text"
                  },
                  "videoId": {
                    "label": "Video ID",
                    "format": "text"
                  },
                  "viewCount": {
                    "label": "Views",
                    "format": "number"
                  },
                  "likes": {
                    "label": "Likes",
                    "format": "number"
                  },
                  "duration": {
                    "label": "Duration",
                    "format": "text"
                  },
                  "uploadDate": {
                    "label": "Uploaded",
                    "format": "text"
                  },
                  "url": {
                    "label": "URL",
                    "format": "link"
                  },
                  "hashtags": {
                    "label": "Hashtags",
                    "format": "array"
                  },
                  "soundTitle": {
                    "label": "Sound",
                    "format": "text"
                  },
                  "channelName": {
                    "label": "Channel",
                    "format": "text"
                  },
                  "channelId": {
                    "label": "Channel ID",
                    "format": "text"
                  },
                  "scrapedAt": {
                    "label": "Scraped At",
                    "format": "text"
                  }
                }
              }
            },
            "detailed": {
              "title": "Detailed",
              "transformation": {
                "fields": [
                  "title",
                  "type",
                  "videoId",
                  "url",
                  "thumbnailUrl",
                  "viewCount",
                  "viewCountText",
                  "likes",
                  "commentsCount",
                  "duration",
                  "durationSeconds",
                  "publishDate",
                  "uploadDate",
                  "category",
                  "soundTitle",
                  "matchedSoundTitle",
                  "captionLanguages",
                  "hashtags",
                  "channelName",
                  "channelUrl",
                  "channelId",
                  "channelUsername",
                  "channelDescription",
                  "channelJoinedDate",
                  "channelLocation",
                  "channelAvatarUrl",
                  "channelBannerUrl",
                  "channelTotalVideos",
                  "channelTotalViews",
                  "numberOfSubscribers",
                  "isChannelVerified",
                  "isMembersOnly",
                  "inputChannelUrl",
                  "sourceType",
                  "source",
                  "order",
                  "commentsTurnedOff",
                  "scrapedAt"
                ]
              },
              "display": {
                "component": "table",
                "properties": {
                  "title": {
                    "label": "Title",
                    "format": "text"
                  },
                  "type": {
                    "label": "Type",
                    "format": "text"
                  },
                  "videoId": {
                    "label": "Video ID",
                    "format": "text"
                  },
                  "url": {
                    "label": "URL",
                    "format": "link"
                  },
                  "thumbnailUrl": {
                    "label": "Thumbnail",
                    "format": "link"
                  },
                  "viewCount": {
                    "label": "Views",
                    "format": "number"
                  },
                  "viewCountText": {
                    "label": "Views (text)",
                    "format": "text"
                  },
                  "likes": {
                    "label": "Likes",
                    "format": "number"
                  },
                  "commentsCount": {
                    "label": "Comments",
                    "format": "number"
                  },
                  "duration": {
                    "label": "Duration",
                    "format": "text"
                  },
                  "durationSeconds": {
                    "label": "Duration (sec)",
                    "format": "number"
                  },
                  "publishDate": {
                    "label": "Published",
                    "format": "text"
                  },
                  "uploadDate": {
                    "label": "Uploaded (UTC)",
                    "format": "text"
                  },
                  "category": {
                    "label": "Category",
                    "format": "text"
                  },
                  "soundTitle": {
                    "label": "Sound",
                    "format": "text"
                  },
                  "captionLanguages": {
                    "label": "Captions",
                    "format": "array"
                  },
                  "hashtags": {
                    "label": "Hashtags",
                    "format": "array"
                  },
                  "channelName": {
                    "label": "Channel",
                    "format": "text"
                  },
                  "channelUrl": {
                    "label": "Channel URL",
                    "format": "link"
                  },
                  "channelId": {
                    "label": "Channel ID",
                    "format": "text"
                  },
                  "channelUsername": {
                    "label": "Username",
                    "format": "text"
                  },
                  "channelDescription": {
                    "label": "Channel Desc",
                    "format": "text"
                  },
                  "channelJoinedDate": {
                    "label": "Joined",
                    "format": "text"
                  },
                  "channelLocation": {
                    "label": "Channel Location",
                    "format": "text"
                  },
                  "channelAvatarUrl": {
                    "label": "Avatar",
                    "format": "link"
                  },
                  "channelBannerUrl": {
                    "label": "Banner",
                    "format": "link"
                  },
                  "channelTotalVideos": {
                    "label": "Total Videos",
                    "format": "number"
                  },
                  "channelTotalViews": {
                    "label": "Total Views",
                    "format": "number"
                  },
                  "numberOfSubscribers": {
                    "label": "Subscribers",
                    "format": "number"
                  },
                  "isChannelVerified": {
                    "label": "Verified",
                    "format": "boolean"
                  },
                  "inputChannelUrl": {
                    "label": "Input URL",
                    "format": "text"
                  },
                  "sourceType": {
                    "label": "Source Type",
                    "format": "text"
                  },
                  "source": {
                    "label": "Source",
                    "format": "text"
                  },
                  "order": {
                    "label": "Order",
                    "format": "number"
                  },
                  "commentsTurnedOff": {
                    "label": "Comments Off",
                    "format": "boolean"
                  },
                  "scrapedAt": {
                    "label": "Scraped At",
                    "format": "text"
                  },
                  "matchedSoundTitle": {
                    "label": "Matched Sound",
                    "format": "text"
                  },
                  "isMembersOnly": {
                    "label": "Members Only",
                    "format": "boolean"
                  }
                }
              }
            }
          }
        }
      },
      "readme": "# YouTube Shorts Metadata Extractor for Channels & Hashtags\n\nCollect YouTube Shorts from a channel's Shorts tab, a hashtag feed, keyword search results, a list of Shorts links, or a sound — and turn each one into a row with exact view and like counts, comment count, publish date, duration, category, sound title, caption languages and the uploader's channel profile.\n\n## What this actor does\n\n- **Five ways in:** a channel's Shorts tab (`channel`), a hashtag's Shorts feed (`hashtag`), YouTube search filtered to Shorts (`search`), specific Shorts URLs/IDs (`direct`), or other Shorts that use the same sound as seed links (`sound`)\n- **Channel sorting:** newest, most popular or oldest first, matching the buttons YouTube shows on the Shorts tab\n- **Exact per-Short metrics:** exact views, likes, comment count, duration, publish date, upload date, category and sound — pulled from each Short's own page, not the rounded listing values\n- **Captions:** whether captions exist and which language tracks are available\n- **Channel profile on every channel-mode row:** subscribers, description, join date, location, links, avatar, banner, total views, total videos and verification\n- **Deep back-catalogs:** page through a channel's entire Shorts list (up to 10,000 per channel per run)\n- **Fast listing-only mode:** turn `includeDetails` off to skip per-Short pages and return titles, links, thumbnails and displayed view counts quickly\n- **Members-only Shorts (optional):** `includeMembersOnly` also collects a channel's members-only Shorts when YouTube offers that filter on its Shorts tab; when the filter is not rolled out for the channel the run notes it and returns the public listing\n- **Exact numbers only or nothing:** a field YouTube does not expose for a given Short is omitted instead of emitted as `null` or a guess; long-form videos are never mixed in\n\n## Output per Short\n\nOne row per Short. Optional fields are omitted when YouTube does not expose a value.\n\n| Field | Description |\n|-------|-------------|\n| `videoId` | The Short's YouTube video ID |\n| `title` | Short's title |\n| `url` | `https://www.youtube.com/shorts/<videoId>` |\n| `thumbnailUrl` | Vertical thumbnail (9:16) |\n| `viewCount` | Exact view count (or the displayed count when `includeDetails` is off) |\n| `viewCountText` | View count as displayed, e.g. `\"30M views\"` |\n| `likes` | Exact like count |\n| `commentsCount` | Exact comment count |\n| `commentsTurnedOff` | `true` when the uploader disabled comments |\n| `duration` | Duration as `MM:SS` / `HH:MM:SS` |\n| `durationSeconds` | Duration in seconds |\n| `publishDate` | Publish date with timezone, e.g. `\"2026-10-16T09:00:02-07:00\"` |\n| `uploadDate` | Same instant in UTC, e.g. `\"2026-10-16T16:00:02.000Z\"` |\n| `category` | YouTube category, e.g. `\"Entertainment\"` |\n| `soundTitle` | `\"Original Sound\"` or the track name when a licensed sound is used |\n| `captionLanguages` | Caption language codes, e.g. `[\"en\"]` |\n| `subtitles` | `\"available\"` when caption tracks exist |\n| `description` | Short's description text |\n| `hashtags` | Hashtags found in the description (hashtag mode returns the searched tag) |\n| `postedTimeText` | Relative publish label, e.g. `\"2 days ago\"` |\n| `formats` | Available video stream qualities |\n| `isAgeRestricted` / `isMembersOnly` / `isUnlisted` | Present and `true` only when that applies |\n| `channelName`, `channelUrl`, `channelId`, `channelUsername` | Uploader identity (channel mode) |\n| `numberOfSubscribers`, `channelTotalVideos`, `channelTotalViews`, `channelJoinedDate`, `channelLocation`, `channelDescription`, `channelDescriptionLinks`, `channelAvatarUrl`, `channelBannerUrl`, `isChannelVerified` | Channel profile fields (channel mode) |\n| `aboutChannelInfo` | Nested object repeating the channel profile fields |\n| `searchQuery` | The keyword (search mode) |\n| `matchedSoundTitle` | The seed sound a `sound`-mode row was found by |\n| `sourceType` / `source` | Which input produced the row |\n| `order` | Position in the result set, starting at 0 |\n| `scrapedAt` | ISO timestamp of when the row was scraped |\n\n### Error and status rows\n\n- A channel, hashtag or search query that cannot be read produces an error row in the same dataset: `recordType: \"error\"` with `sourceType`, `source`, `errorCode` (`CHANNEL_NOT_FOUND`, `NO_SHORTS`, `HASHTAG_EMPTY`, `SEARCH_EMPTY`, `INVALID_SHORT_URL`) and an `error` message. The run itself still succeeds.\n- When a run finds no Shorts at all, the dataset gets one status row instead: `recordType: \"status\"`, `status: \"no_results\"`, plus the inputs you supplied.\n\n## Input\n\n| Field | Type | Required | Default | Description |\n|-------|------|----------|---------|-------------|\n| `inputMode` | string | No | `\"channel\"` | `channel`, `hashtag`, `search`, `direct` or `sound` |\n| `channelUrls` | string[] | Channel mode | `[\"https://www.youtube.com/@MrBeast\"]` | Channel URLs, `@handles` or channel IDs |\n| `hashtags` | string[] | Hashtag mode | – | Hashtags like `#cooking` (with or without `#`) |\n| `searchQueries` | string[] | Search mode | – | Keywords; results are filtered to Shorts |\n| `shortUrls` | string[] | Direct / sound mode | `[\"https://www.youtube.com/shorts/pfUG56htjBc\"]` | Shorts URLs, youtu.be/watch links, or 11-character IDs; seed links in sound mode |\n| `sortShortsBy` | string | No | `\"newest\"` | Channel Shorts order: `\"newest\"`, `\"popular\"` or `\"oldest\"` |\n| `maxShorts` | integer | No | `3` | Max Shorts per channel, hashtag or query (per run in direct mode), 1–10000 |\n| `includeDetails` | boolean | No | true | Visit each Short's page for exact metrics, dates, category, sound and captions. `false` = fast listing-only rows |\n| `includeMembersOnly` | boolean | No | false | Channel mode: also collect members-only Shorts when YouTube offers the `Members only` filter (rollout-dependent; reads each member Short's own page) |\n| `oldestPostDate` | string | No | – | Only Shorts published on/after this date (`YYYY-MM-DD`) |\n| `market` | string | No | `\"US\"` | Region whose YouTube settings are used (31 countries), which sets the language of returned metadata |\n\n### Supported inputs per mode\n\n- `channel` — `@MrBeast`, `https://www.youtube.com/@MrBeast`, `https://youtube.com/channel/UCX6OQ3DkcsbYNE6H8uQQuVA`, a bare `UC…` ID, or a name\n- `hashtag` — `#cooking`, `cooking`\n- `search` — any keyword phrase, e.g. `\"cooking shorts\"`\n- `direct` — `https://www.youtube.com/shorts/<id>`, `https://youtu.be/<id>`, `watch?v=<id>`, or the bare 11-character ID\n- `sound` — the same forms; each link's sound is looked up and its other Shorts are returned\n\n### Example: a channel's most popular Shorts\n\n```json\n{\n  \"inputMode\": \"channel\",\n  \"channelUrls\": [\"https://www.youtube.com/@MrBeast\"],\n  \"sortShortsBy\": \"popular\",\n  \"maxShorts\": 50\n}\n```\n\n### Example: keyword search\n\n```json\n{\n  \"inputMode\": \"search\",\n  \"searchQueries\": [\"cooking shorts\", \"home workout\"],\n  \"maxShorts\": 100\n}\n```\n\n### Example: hashtag feed\n\n```json\n{\n  \"inputMode\": \"hashtag\",\n  \"hashtags\": [\"#cooking\"],\n  \"maxShorts\": 100\n}\n```\n\n### Example: specific Shorts, fast listing only\n\n```json\n{\n  \"inputMode\": \"direct\",\n  \"shortUrls\": [\"https://www.youtube.com/shorts/pfUG56htjBc\"],\n  \"includeDetails\": false\n}\n```\n\n## Use cases\n\n- **Benchmark a creator's Shorts** — exact views, likes and comments for their entire Shorts back-catalog, sorted by newest or most popular\n- **Trend and hashtag research** — see what creators and videos are active under a hashtag right now, then drill into exact engagement\n- **Keyword discovery** — find Shorts around a topic with YouTube search and rank them by exact performance\n- **Sound tracking** — group Shorts by the sound they use (`Original Sound` vs licensed tracks)\n- **Content cadence monitoring** — schedule a run with `oldestPostDate` to collect only recent uploads\n- **Data enrichment** — paste a list of Shorts links and receive clean rows with full metadata for each\n\n## FAQ\n\n**Do I need a YouTube account, API key or proxies?** No. The actor reads YouTube's public pages and picks its network route automatically; there is nothing to configure.\n\n**Why do some rows miss `likes` or `commentsCount`?** YouTube hides those counters for some Shorts, or the page was briefly gated; the actor retries and falls back to its secondary source, and simply omits what is genuinely unavailable rather than guessing.\n\n**What is the difference between `viewCount` and `viewCountText`?** With `includeDetails` on, `viewCount` is the exact number from the Short's page and `viewCountText` is YouTube's display string (e.g. `\"30M views\"`). With `includeDetails` off, only the displayed value is available.\n\n**How deep can a channel's Shorts history go?** To the end of the channel's public Shorts list, up to 10,000 rows per channel per run. `maxShorts` caps each source.\n\n**How deep can a hashtag go?** YouTube's hashtag Shorts feed exposes one page (roughly 36–37 Shorts) and rejects further pages. The actor fills the rest of `maxShorts` from YouTube search results for `\"#tag shorts\"`, which keeps returning Shorts that carry the tag.\n\n**What does sound mode return?** For each seed link you provide, the actor lists other Shorts that use the same sound (8–15 per seed) and tags them with `matchedSoundTitle`. The seed itself is not included; a seed whose sound has no feed yields a `SOUND_UNAVAILABLE` error row.\n\n**Can I get members-only Shorts?** Yes, when YouTube offers the `Members only` filter on that channel's Shorts tab — the filter is a gradual rollout, so the actor retries the tab a few times and, when it is not offered, says so in the status message and returns the public listing. Members-only Shorts count toward `maxShorts` and cost up to two player requests each; they carry `isMembersOnly: true`. YouTube sometimes serves the filter with an empty listing on datacenter networks — the actor then says so and returns the public listing.\n\n**Which sort orders are supported?** The three YouTube offers on the Shorts tab: newest, most popular and oldest. If a channel does not show those buttons, the run falls back to newest order and says so in the status message.\n\n**Are view counts live?** They are exact at the moment of scraping. YouTube updates counters continuously, so repeated runs can show slightly different numbers.\n\n**Is this affiliated with YouTube?** No. This is an independent tool and is not affiliated with, endorsed by, or sponsored by YouTube or Google.\n"
    },
    "buildNumber": "1.0.2",
    "imageDigest": "66dc77676a4e906813860899d61134d6503427a94de5e115b23f92d5122397be",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.0"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.016602222222222223
    }
  }
}