DEV Community

dodou
dodou

Posted on

Scrape Google Video Search Results with a SERP API (Python)

If you need video results from Google — titles, links, durations, sources, thumbnails — the videos endpoint returns them as structured JSON, one POST per page. Here's the whole flow in Python: query, paginate, flatten to CSV.

The endpoint

POST /google/videos, with a JSON body:

  • q (required) — the query
  • hl (language code, defaults to en)
  • gl (country code, defaults to us)
  • page (1-based, defaults to 1)

There's no device parameter here — the docs restrict that one to the web search endpoint. A successful request costs 1 credit, like search and news (images and maps cost 2). The response envelope carries status, request_id, elapsed_ms, credits_charged, and search_type ("videos"), plus query, page and the videos array.

Each item in videos documents these fields: rank, position, title, link, url, source_url, display_url, source, duration, time, published_at, thumbnail_url, thumbnail.

import time

import requests

API_URL = "https://api.serpbase.dev/google/videos"
API_KEY = "YOUR_API_KEY"


def fetch_page(query: str, page: int = 1) -> dict:
    resp = requests.post(
        API_URL,
        headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
        json={"q": query, "hl": "en", "gl": "us", "page": page},
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()


first = fetch_page("python asyncio tutorial")
print(first["search_type"], len(first.get("videos", [])))
Enter fullscreen mode Exit fullscreen mode

The field names and request shape above come from the SerpBase videos endpoint documentation. One note on that page is worth knowing before you debug odd result sets: if no dedicated video cards are parsed for a query, the parser falls back to news-style extraction for compatible layouts — so a result list that reads more like articles than videos isn't a bug in your code.

Collect and export

import csv

FIELDS = ["rank", "title", "link", "source", "duration", "published_at"]


def collect(query: str, pages: int = 2) -> list:
    rows = []
    for page in range(1, pages + 1):
        data = fetch_page(query, page)
        items = data.get("videos", [])
        if not items:
            break
        rows.extend(items)
        time.sleep(1)
    return rows


rows = collect("python asyncio tutorial")

with open("videos.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=FIELDS, extrasaction="ignore")
    writer.writeheader()
    writer.writerows(rows)

print(len(rows), "rows")
Enter fullscreen mode Exit fullscreen mode

extrasaction="ignore" lets the writer take the full API objects while only emitting the columns you asked for. Fields that aren't present on a given item come out as empty cells, so check for blanks before you analyse.

Example rows (illustration, not real data):

rank,title,link,source,duration,published_at
1,Example Video Title,https://www.youtube.com/watch?v=example,Example Channel,12:34,2026-08-30
2,Another Example Clip,https://www.youtube.com/watch?v=example2,Second Channel,04:07,2026-08-28
Enter fullscreen mode Exit fullscreen mode

What it costs

1 credit per successful request. A batch of 10 keywords at 2 pages each is 20 requests, so budget 20 credits. New accounts get 100 free searches, which covers getting the pipeline working. credits_charged in each response tells you what was actually billed, and failed or upstream-timed-out requests are handled by the refund logic.

FAQ

Does device work on this endpoint? No — the docs list it for the search endpoint only. Video results here reflect the default layout.

Why do both time and published_at exist? The docs list both on video items. I store both raw and normalise downstream instead of assuming they mean the same thing — check what they contain for your queries before trusting either.

Do I need to dedupe across pages? Yes if you go past page 1 or reuse queries. Dedupe on link before writing, same as you would with any paginated API.

Point it at your own query list and run it once — the CSV tells you quickly which fields come back populated for your vertical. All the parameters used here are in the documentation linked above.

Top comments (0)