Search YouTube videos with channel, views, length and upload date.
Searches YouTube like the search box: title, channel, view count, length in seconds, how long ago it was published, live flag, thumbnail and link. Sort by relevance, upload date, view count or rating. Shorts shelves, ads and channel/playlist cards are left out; results in the user's own browser follow their YouTube settings.
- Site
- YouTube, United States
- Version
- v2 of 2
- Runs, last 7 days
- No reports yet
- sha256
- c0ab2ac6d76b…
Run it with your agent
after installing WieldAsk in plain words. Your agent finds this recipe and calls it:
“Search YouTube videos with channel, views, length and upload date.”
Read-only: it changes nothing on the site. No login needed.
{ "tool": "run", "arguments": { "recipe_id": "youtube.com/search-videos", "params": { "limit": 10, "query": "lasagne recipe" } } }
Requests
sent from your browser, in order- 1POST/youtubei/v1/search?prettyPrintYouTube's own search API (InnerTube), the call the results page makes; read-only POST. The client version comes from the page's ytcfg. The one-item foreach maps sort to YouTube's filter code (videos only, plus the sort).
Parameters
from your agentReturns
to your agentWhere it can send
- www.youtube.com
Anything else is blocked by the extension.
Reads from your session
- client_version from dom
Used only in requests to the addresses on the left. Never sent to wield.run or your agent.
Checks
a run passes when- length(output.videos) > `5`
- length(output.videos[?starts_with(url, 'https://www.youtube.com/watch?v=')]) == length(output.videos)
- length(output.videos[?views != `null`]) > `3`
- length(output.videos[?duration_s > `0`]) > `3`
- output.videos[0].channel != `null`
Raw recipe
Served as canonical JSON from /api/v1/recipes/youtube.com/search-videos, so its sha256 matches the ETag and the index.
curl -s https://wield.run/api/v1/recipes/youtube.com/search-videos | shasum -a 256 # c0ab2ac6d76b37992f77f8cf3f1767ca92b268426a02b73188f889da9edfbbbb
{ "auth": { "derive": [ { "from": "dom", "name": "client_version", "regex": "\"INNERTUBE_CLIENT_VERSION\":\"([0-9.]+)\"", "selector": "script:not([src])" } ] }, "description": "Searches YouTube like the search box: title, channel, view count, length in seconds, how long ago it was published, live flag, thumbnail and link. Sort by relevance, upload date, view count or rating. Shorts shelves, ads and channel/playlist cards are left out; results in the user's own browser follow their YouTube settings.", "effect": "read", "id": "youtube.com/search-videos", "keywords": [ "youtube", "video", "videos", "suchen", "anleitung", "tutorial", "how to", "music", "review", "watch", "channel", "rezept video" ], "output": { "schema": { "properties": { "estimated_results": { "type": [ "integer", "null" ] }, "query": { "type": "string" }, "videos": { "items": { "properties": { "channel": { "type": [ "string", "null" ] }, "channel_url": { "type": [ "string", "null" ] }, "duration_s": { "type": [ "integer", "null" ] }, "duration_text": { "type": [ "string", "null" ] }, "id": { "type": "string" }, "live": { "type": "boolean" }, "published": { "description": "As YouTube says it, e.g. '1 month ago'", "type": [ "string", "null" ] }, "thumbnail": { "type": [ "string", "null" ] }, "title": { "type": "string" }, "url": { "type": "string" }, "views": { "type": [ "integer", "null" ] } }, "type": "object" }, "type": "array" } }, "type": "object" }, "value": "let $lim = params.limit || `10` in let $all = map(&(let $v = @, $d = @.lengthText.simpleText, $vc = @.viewCountText.simpleText || (@.viewCountText.runs[0].text) in {id: $v.videoId, title: join('', $v.title.runs[].text), channel: $v.ownerText.runs[0].text || `null`, channel_url: ($v.ownerText.runs[0].navigationEndpoint.browseEndpoint.canonicalBaseUrl && join('', ['https://www.youtube.com', $v.ownerText.runs[0].navigationEndpoint.browseEndpoint.canonicalBaseUrl])) || `null`, views: ($vc && to_number(replace(replace(replace(replace($vc, ',', ''), ' views', ''), ' view', ''), ' watching', ''))) || ($vc == 'No views' && `0`) || `null`, duration_s: ($d && (let $p = split($d, ':') in ((length($p) == `3` && ((to_number(trim_left($p[0], '0')) || `0`) * `3600` + (to_number(trim_left($p[1], '0')) || `0`) * `60` + (to_number(trim_left($p[2], '0')) || `0`))) || ((to_number(trim_left($p[0], '0')) || `0`) * `60` + (to_number(trim_left($p[1], '0')) || `0`))))) || `null`, duration_text: $d || `null`, published: $v.publishedTimeText.simpleText || `null`, live: length(($v.badges || `[]`)[?metadataBadgeRenderer.style == 'BADGE_STYLE_TYPE_LIVE_NOW']) > `0`, thumbnail: ($v.thumbnail.thumbnails[-1].url) || `null`, url: join('', ['https://www.youtube.com/watch?v=', $v.videoId])}), (steps.search[0].videos || `[]`)[?videoId]) in {query: params.query, estimated_results: steps.search[0].estimated && to_number(steps.search[0].estimated) || `null`, videos: ($lim == `5` && $all[:5]) || ($lim == `20` && $all[:20]) || $all[:10]}" }, "params": { "additionalProperties": false, "properties": { "country": { "default": "US", "description": "Region for results (ISO country code)", "pattern": "^[A-Z]{2}$", "type": "string" }, "limit": { "default": 10, "enum": [ 5, 10, 20 ], "type": "integer" }, "query": { "description": "What to search for, e.g. 'lasagne recipe', 'how to replace a bike chain'", "maxLength": 150, "minLength": 1, "type": "string" }, "sort": { "default": "relevance", "enum": [ "relevance", "upload_date", "view_count", "rating" ], "type": "string" } }, "required": [ "query" ], "type": "object" }, "provenance": { "method": "browser-inspect", "notes": "The results page loads the first page server-side and calls POST /youtubei/v1/search for more; the same API answers the first page too. Body: context.client {clientName WEB, clientVersion from ytcfg INNERTUBE_CLIENT_VERSION in the page's inline script, hl, gl}, query, params (filter code: EgIQAQ== videos; CAISAhAB / CAMSAhAB / CAESAhAB = videos sorted by upload date / view count / rating). Videos are videoRenderer items inside itemSectionRenderer; shorts come as gridShelfViewModel shelves and are left out. hl is fixed to en so view counts and durations parse. v2 (2026-11-07 22:38): read_only_post on 1 step only; requests and output unchanged.", "recorded_at": "2026-11-07T18:50:00+01:00", "recorded_by": "C4" }, "requires_login": false, "schema": "https://wield.run/schema/recipe/v1.json", "site": { "country": "US", "id": "youtube.com", "language": "en", "name": "YouTube", "origin": "https://www.youtube.com", "origins": [ "https://www.youtube.com" ] }, "steps": [ { "description": "YouTube's own search API (InnerTube), the call the results page makes; read-only POST. The client version comes from the page's ytcfg. The one-item foreach maps sort to YouTube's filter code (videos only, plus the sort).", "expect": { "assert": "body.contents != `null`", "content_type": "json", "status": [ 200 ] }, "extract": { "json": { "estimated": "estimatedResults", "videos": "contents.twoColumnSearchResultsRenderer.primaryContents.sectionListRenderer.contents[].itemSectionRenderer.contents[][].videoRenderer" } }, "foreach": { "as": "f", "in": "[{sp: (params.sort == 'upload_date' && 'CAISAhAB') || (params.sort == 'view_count' && 'CAMSAhAB') || (params.sort == 'rating' && 'CAESAhAB') || 'EgIQAQ=='}]", "max": 1 }, "id": "search", "read_only_post": true, "request": { "body": { "type": "json", "value": { "context": { "client": { "clientName": "WEB", "clientVersion": "{{vars.client_version}}", "gl": "{{params.country}}", "hl": "en" } }, "params": "{{f.sp}}", "query": "{{params.query}}" } }, "headers": { "Content-Type": "application/json", "X-Youtube-Client-Name": "1", "X-Youtube-Client-Version": "{{vars.client_version}}" }, "method": "POST", "query": { "prettyPrint": "false" }, "url": "/youtubei/v1/search" } } ], "task": "Search YouTube videos with channel, views, length and upload date", "tos": { "notes": "YouTube's terms restrict automated access; the official YouTube Data API is the sanctioned route for bulk use. This recipe makes one search request per call in the user's own browser, as the user would.", "reviewed": false }, "verify": { "checks": [ "length(output.videos) > `5`", "length(output.videos[?starts_with(url, 'https://www.youtube.com/watch?v=')]) == length(output.videos)", "length(output.videos[?views != `null`]) > `3`", "length(output.videos[?duration_s > `0`]) > `3`", "output.videos[0].channel != `null`" ], "example_params": { "limit": 10, "query": "lasagne recipe" }, "max_ms": 4000 }, "version": 2 }