Video Search API Tutorial: A Live-Tested Guide (2026)
A video search API returns ranked video results — titles, URLs, durations, view counts, thumbnails and embed links — as JSON from a single GET request. No player scraping, no per-platform integrations. This tutorial walks through the Serpent API video search endpoint engine by engine, and every request in it was actually run against the live API on July 18, 2026 — the outputs, timings and field tables below are the real ones, including the places where engines behave differently.
That last part matters, because the response you get depends heavily on which engine you pick, and most write-ups gloss over it. We'll cover the five supported engines, the standardised field set every one of them returns, the filters that demonstrably work, a tested Python client, and where Google's dedicated video engine differs from the video carousel inside its web results.
What a video search API returns
Search engines maintain dedicated video indexes — aggregating YouTube, Vimeo, Dailymotion, TikTok and thousands of publisher sites — and a video search API exposes that index as structured data. One query fans out across every platform the engine indexes, which is the practical difference from integrating platform APIs one by one: you get cross-platform coverage and SERP-style ranking for free. For YouTube-only depth (channel stats, per-video analytics), a dedicated YouTube API is the better tool; the two are complementary.
Serpent API's endpoint is GET /api/videos. It takes q (required), engine (ddg — the default — plus google, yahoo, bing and brave), country, num (1–100), plus the filter parameters covered below. Full parameter reference in the API docs.
Setup and first request
Create a free account — 10 free searches, no card — copy your key, and you can be looking at JSON in under a minute:
curl -H "X-API-Key: YOUR_API_KEY" \
"https://apiserpent.com/api/videos?q=guitar+lesson&engine=google&country=us"
Here is the first row of a live engine=google run, exactly as the API returned it (the description is trimmed for print):
{
"position": 1,
"title": "Beginner Acoustic Lesson 1 - Your Very First Guitar Lesson (E Minor + Asus2)",
"url": "https://www.youtube.com/watch?v=HNSaXAe8tyg",
"duration": "16:20",
"source": "www.youtube.com",
"views": 14400000,
"thumbnail": "https://i.ytimg.com/vi/HNSaXAe8tyg/hqdefault.jpg",
"description": "More Guitar Resources: 🎵 Get my FREE Guitar Guide PDF—packed with chords, scales, triads, arpeggios, and more: https://www.martymusic.com...",
"publisher": "Marty Music",
"embedUrl": null,
"publishedTime": "2016-06-13"
}
Eleven fields, the same eleven on every engine, in that order — a value the result did not carry comes back as null rather than as a missing key, so one parser handles them all. That is the row above in miniature: most of what a video-intent dashboard wants is already parsed (a YYYY-MM-DD publish date, an integer views you can sort on, the uploader in publisher, the video's own YouTube thumbnail), and the one thing this result did not carry, embedUrl, is null rather than absent — so the field is safe to read on every row and simply falls back to building your own player URL from url. If you're publishing the results, the fields map cleanly onto schema.org's VideoObject vocabulary.
The response fields, engine by engine
The field set is standardised across every engine: the same eleven fields, in the same order, whichever engine you pass. That is the part worth knowing before you write a single struct or model class — you write one model, not one per engine.
| Field | Type | Notes |
|---|---|---|
position | integer | 1-based rank in the list returned. Never skips a number. |
title | string | null | Video title. |
url | string | null | Link to watch the video. |
duration | string | null | Runtime as displayed, e.g. "10:01". |
source | string | null | Host the video is served from, e.g. "www.youtube.com". |
views | number | null | A number you can sort on — 4894, not "4.9K views". |
thumbnail | string | null | Thumbnail URL. For a YouTube video it is that video's own YouTube thumbnail, on every engine. |
description | string | null | Description text shown with the video, where the result carries one. |
publisher | string | null | Uploader or channel name — never the platform word. |
embedUrl | string | null | Embeddable player URL. |
publishedTime | string | null | Publication date as YYYY-MM-DD. |
Every one of those keys is always present. A value the result did not carry comes back as null, never as a missing key, so a strict decoder — as Go's is, something our Golang tutorial deals with at length — has one shape to bind against on every engine. What genuinely varies is coverage and count, not shape: not every result carries a description, an embed URL or a view count, and the number of videos a single call returns differs by engine. Measured at num=100: google ~60, yahoo ~60, bing ~59, ddg ~59, brave ~50. num is a ceiling, not a promise — no engine has been measured delivering a full 100 videos in one call, and a short answer says so in its delivery block.
If you integrated before September 2026, two field names changed. Read views in place of viewCount, thumbnail in place of thumbnailUrl, description in place of snippet, and publishedTime in place of date; provider, channel and displayedUrl are gone, with source carrying the host and publisher the uploader. views is now a number rather than a display string, so if you were stripping the text and parsing it yourself, delete that step.
A tested Python client
Standard library only — a video search API is a plain HTTPS GET, so urllib covers it. This script was run on July 18, 2026; the view-count field has since been renamed views, and the listing below reads the current name:
import os, json, urllib.parse, urllib.request
API_KEY = os.environ["SERPENT_API_KEY"]
BASE = "https://apiserpent.com/api/videos"
def video_search(query, **params):
params["q"] = query
req = urllib.request.Request(
BASE + "?" + urllib.parse.urlencode(params),
headers={"X-API-Key": API_KEY},
)
with urllib.request.urlopen(req, timeout=30) as resp:
return json.load(resp)
data = video_search("how to make sourdough bread", engine="ddg", num=5)
print(f"{data['meta']['totalVideos']} videos in {data['meta']['elapsed']}")
for v in data["results"]["videos"]:
views = v.get("views")
views = f"{views:,} views" if isinstance(views, int) else "views n/a"
print(f"{v['position']}. [{v['duration']}] {v['title'][:60]} — {views}")
Real output from that run:
5 videos in 10122ms
1. [16:09] How To Make Sourdough Bread Masterclass — 16,317,362 views
2. [14:08] YOUR FIRST SOURDOUGH (Sourdough Bread For Complete Beginners — 4,526,808 views
3. [9:25] The Ultimate Homemade Sourdough Bread — 3,728,473 views
4. [16:50] Amazing Sourdough Bread Recipe — 3,509,308 views
5. [11:53] Bake the Perfect Sourdough Bread: A Step-by-Step Guide — 3,021,501 views
Note the .get("views") defensive read — every key is always present, but its value can be null, so write your client against the field table above and it survives an engine switch. Timing honesty: this run took 10.1s while the earlier one took 3.8s. Live searches vary; if you're batching thousands of queries, size a worker pool to your account's documented allocations (the rate-limit comparison has cross-provider numbers, and the scale guide covers the client engineering).
Rank tracking? Use format=simple
If you only care about ranking order — say you're tracking where your videos place for a keyword — pass format=simple and the response collapses to a flat array of four fields. Live output with engine=bing&num=5:
[
{ "position": 1, "title": "All React Hooks Explained in 2 Hours | Complete React Hooks Tutorial with Example",
"url": "https://www.youtube.com/watch?v=6wf5dIrryoQ", "duration": "17:47" },
{ "position": 2, "title": "Basic React Hooks Crash Course (useState, useEffect, useMemo, useCallback) | WebDevSimplified",
"url": "https://www.youtube.com/watch?v=qRkSWHbGkEU", "duration": "40:34" },
...
]
Smaller payloads, trivial parsing, and the same flat shape across all five SERP endpoints — the pattern our SERP API uses for web results too. (position is an integer here and in format=full alike, on every engine.)
Filters that actually work
Filter support is where video APIs most often overpromise, so we tested rather than transcribed. A live run of duration=short&freshness=m on the default engine returned 60 results whose first eight durations were 1:05, 0:33, 0:11, 0:09, 0:08, 0:43, 0:59, 4:26 — overwhelmingly sub-minute clips — and whose publish timestamps all fell within the previous month (July 6, June 20, June 24…). The filters are engine-side, so they shape which videos are retrieved, not just a post-hoc trim; treat the boundaries as the engine's judgment (that one 4:26 still counted as “short”) rather than hard cutoffs.
duration—short,medium,longfreshness—h,d,7d/w,m,y(ddg);time=day|week|month|yearon yahoo/bingsafe—off,moderate,strictresolution—high/standard(ddg) or360p–1080p(yahoo/bing)country— 2-letter code, e.g.us,gb,de
Production notes
Three behaviors to build around, all observed rather than theorized. First, result counts float: we asked for the default and got 56 on one engine, 60 on another — read meta.totalVideos instead of assuming, and treat a thin result set for an obscure query as normal engine behavior. Second, timings float too (3.8s and 10.1s for the same shape of request in our runs), so give clients a 30-second timeout and log meta.elapsed if you care about latency trends. Third, if you push past your account's request allocations you'll see a 429 — the platform's automated protection against overload — with a Retry-After header that tells your client exactly how long to pause. Honor it and back off with jitter; the retry pattern in our Go client tutorial ports to any language in a few lines.
Google: the dedicated engine, and the inline carousel
engine=google is served on the dedicated endpoint, alongside ddg, yahoo, bing and brave. It returns the same eleven fields in the same order as every other engine, and at num=100 it sits in the same band as the rest — about 60 (see the API docs). Pass an engine value that isn't one of the five and you get a 400 that names them:
{ "success": false,
"error": "Invalid engine. Use google, yahoo, bing, ddg, or brave." }
There is a second, genuinely different thing worth knowing about. Google's web results carry video carousels of their own, and that is not the same view as a dedicated video search: it is the video block as a web searcher sees it, ranked beside organic results, People Also Ask and AI Overviews. If that is what you are measuring, run a deep web search and read results.videos from the response — the Google SERP API returns it inline. Use the dedicated endpoint for cross-platform discovery and full per-video metadata; use the inline carousel when the question is what appears on a web SERP. (And remember that all live SERPs shuffle between runs — here's why — so track medians, not single samples.)
What it costs
Video search is one of Serpent API's cheapest verticals: from $0.60 per 1,000 searches on the entry tier down to $0.54 and then $0.42 per 1,000 at the highest tier — the numbers above come from the live pricing page today, and the video search API page keeps the current tier table. At $0.42/1K, monitoring 1,000 keywords daily for a month costs about $12.60. Comparing providers for a bigger stack? The interactive cost calculator models real monthly bills across ten providers, the cheapest SERP API breakdown covers the web-search side, and the 2026 SERP API comparison ranks the field overall.
What people build with it
Content-gap research: query your target keywords, aggregate publisher and views, and you know which topics have demand but weak coverage. Competitive monitoring: schedule daily format=simple runs and diff the positions — the same pattern as web rank tracking, at video-vertical prices. Editorial embeds: embedUrl plus thumbnail gives a CMS everything it needs to attach relevant video to articles automatically. Trend detection: freshness=d plus views surfaces what took off in the last 24 hours for any topic you care about.
FAQ
Is there a free video search API?
Yes — video search is included in Serpent API's free tier: 10 searches on signup, no card, same endpoint and JSON as paid plans.
Which engines are supported?
ddg (the default), google, yahoo, bing and brave — all five on the same endpoint, returning the same eleven fields. Google's web results also carry inline video carousels, which a deep web search exposes under results.videos; that is a different view from a dedicated video search rather than a substitute for it.
How many results can I get per call?
num accepts 1–100, but it is a ceiling rather than a promise. Measured at num=100: google ~60, yahoo ~60, bing ~59, ddg ~59, brave ~50. This tutorial's own July runs served 56 (ddg) and 60 (yahoo) for single queries. Always read meta.totalVideos for the count actually returned.
Can I filter by video length or upload date?
Yes — duration and freshness (plus safe, resolution, and yahoo/bing's time). Our live duration=short&freshness=m test returned mostly sub-minute videos published within the prior month.
How fast are responses?
Seconds, not milliseconds — 3.8 to 10.1s across the runs recorded for this tutorial. Batch work should lean on concurrency within your account's documented allocations rather than raw per-call speed.
Search videos from your code today
Every request in this tutorial ran against the live API before publishing. Start with 10 free searches — no card — and video queries from $0.42 per 1,000 at scale.
Start Free — 10 Searches, No CardExplore: Video Search API · API Docs · Pricing



