I Checked Video Search API Dates. Here’s What They Can Tell You.

By Anurag Pathak, Founder of Serpent API·

To assess video search freshness, keep the returned publication date, the time your query first observed a link and the search window as separate facts. Serpent documents publishedTime as a calendar date or null. That field cannot show how many minutes after upload a monitor found the video.

I founded Serpent API, so this is an affiliated review of its fields. On October 5, 2026, I checked the Videos API contract and YouTube Search documentation. The sample values are illustrative; I did not measure upload-to-search delay or recall. For cross-site identity handling, see the video inventory workflow.

Set a repeatable freshness question

A defensible question is “Which video links did this query first return today?” It is different from “Which videos were uploaded today?” Choose freshness=h, d, 7d, m or y according to your review cadence. The time alias supports day, week, month and year; if both are supplied, documented freshness wins. Keep engine, country, language and num stable between runs.

Clock or signalWhat it saysSafe statement
publishedTimeDate shown with search result, or null“Result reports a publication date of …”
Local first_seenWhen this monitor first recorded the key“First observed in our sampled search at …”
Source-page timestampPlatform or publisher timestamp after review“The source page reports …” with a dated citation
Missing next runNo row in that search sample“Not returned in this sample,” never “deleted”

Persist first-seen and last-seen video keys

The script below reads a JSON state file, samples one documented rolling window and updates its observed keys. It keeps null dates and emits a short-sample flag. For more than one worker, use a database with atomic upserts. This is contract-derived example code; no live result from this query was captured for the article.

Install requests with python -m pip install requests and set SERPENT_API_KEY before running this illustrative script.

import json, os
from datetime import datetime, timezone
from pathlib import Path
from urllib.parse import urlsplit, parse_qs, parse_qsl, urlencode, urlunsplit
import requests

def item_key(raw):
    try:
        u = urlsplit(raw)
        if u.scheme not in ("http", "https") or not u.hostname: return None
        host = u.hostname.lower().removeprefix("www.")
        vid = (parse_qs(u.query).get("v", [None])[0]
               if host == "youtube.com" and u.path.rstrip("/") == "/watch" else None)
        if host == "youtu.be": vid = u.path.strip("/").split("/")[0]
        if vid: return "youtube:" + vid
        query = [(k, v) for k, v in parse_qsl(u.query, keep_blank_values=True)
                 if not k.lower().startswith("utm_") and k.lower() not in {"fbclid", "gclid"}]
        return urlunsplit(("https", host, u.path.rstrip("/") or "/",
                           urlencode(sorted(query)), ""))
    except ValueError:
        return None

STATE = Path("video-observations.json")
params = {"q": "urban garden tutorial", "engine": "ddg", "country": "us",
          "freshness": "7d", "num": 25, "format": "full"}
state = json.loads(STATE.read_text()) if STATE.exists() else {"controls": params, "items": {}, "runs": []}
if (state.get("controls") != params or not isinstance(state.get("items"), dict)
        or not isinstance(state.get("runs"), list)):
    raise ValueError("Use a separate state file for each query definition")
items = state["items"]
now = datetime.now(timezone.utc).isoformat()
r = requests.get("https://apiserpent.com/api/videos", params=params,
    headers={"X-API-Key": os.environ["SERPENT_API_KEY"]}, timeout=60)
r.raise_for_status()
data = r.json()
if data.get("success") is not True: raise ValueError("No usable video answer")
rows = data.get("results", {}).get("videos")
if not isinstance(rows, list): raise ValueError("Missing video list")
new, exceptions = [], []
for position, row in enumerate(rows, 1):
    key = item_key(row.get("url")) if isinstance(row, dict) and isinstance(row.get("url"), str) else None
    if key is None:
        exceptions.append({"position": position, "reason": "no usable URL"})
        continue
    if key not in items:
        items[key] = {"first_seen": now, "published_date": row.get("publishedTime"),
                      "observed_urls": []}
        new.append({"item_key": key, "watch_url": row["url"], **items[key]})
    if row["url"] not in items[key]["observed_urls"]:
        items[key]["observed_urls"].append(row["url"])
    items[key]["last_seen"] = now
delivery = data.get("delivery") or {}
requested, returned = delivery.get("requested"), delivery.get("returned")
sample_short = (returned < requested if isinstance(returned, (int, float))
                and isinstance(requested, (int, float)) else None)
if (data.get("meta") or {}).get("partialResults"): sample_short = True
run = {"observed_at": now, "requested": requested, "returned": returned,
       "sample_short": sample_short, "new_keys": len(new), "exceptions": exceptions}
state["runs"].append(run)
STATE.write_text(json.dumps(state, indent=2, sort_keys=True))
print(json.dumps({"request": params, "newly_observed": new,
                  "run": run, "sample_short": sample_short}, indent=2))

Illustrative record: {"first_seen":"2026-10-05T09:00:00+00:00","published_date":"2026-10-04","last_seen":"2026-10-05T09:00:00+00:00"}. It shows how yesterday’s publication date can be first observed today. It is not a measured delay: the source may provide only a date, and the search sample may not have included the link earlier.

Compare repeat samples without false disappearance alerts

  1. Save all query controls and returned keys for each run. Use the same controls when comparing two runs.
  2. Inspect delivery and meta.partialResults on a short response. Keep all rows returned, but mark the snapshot incomplete.
  3. For a newly observed or missing key, open the watch URL. If the task is YouTube-only, use YouTube’s official resources for platform-specific timestamps and status.

Choose a tool and budget in the right units

OptionFreshness viewPublished unitFit
Serpent VideosRolling search-result window and date-only fieldDefault $0.10/1,000 callsCross-site discovery with your own observation clock.
YouTube Data APIYouTube resource search and platform metadataProject quota; each method has a documented quota costYouTube-only audit that needs its official identifiers and timestamps.
Publisher feedsPublisher-defined release timestampsProvider-specificKnown catalog with direct publication context.

Two sampled queries every six hours for 30 days are 240 Serpent Videos calls. At the Default rate of $0.10/1,000, listed usage is $0.024, before storage and verification. Do not compare this dollar amount directly with YouTube quota units; estimate the actual calls and plan limits for each workflow. Recheck current pricing and quota pages when implementing.

Handle the four ambiguous changes between runs

Compare the same query controls and normalize video identity before assigning a change label. A row can enter the window because ranking changed, leave because it moved below num, or be absent because delivery was short. None of those events is a measured upload or deletion. Keep a run ledger with scheduled time, actual observation time, requested and returned rows, the selected rolling window and any partial-result flag.

Two-run patternSafe labelNext check
New key, publication date presentFirst observed now; source reports that dateOpen the source if exact upload timing matters.
New key, date nullFirst observed now; publication date unknownDo not substitute the crawl time.
Previously seen key missing from a full sampleNot returned in this windowCheck the watch page before changing availability status.
Previously seen key missing from a short sampleComparison inconclusiveKeep the last known observation and review another complete run.

A practical report can say, “Of the links this fixed query returned at 09:00 UTC, six were not in our previous complete sample.” Its denominator is those two search samples, not all videos uploaded that day. If you need an alert on uploads within one hour, first obtain a source timestamp precise enough for that rule, then measure first-seen delay on a dated, manually checked set. A date-only field cannot validate an hourly service target.

What would make a strong freshness claim?

Create a dated validation set of source-confirmed upload times, run the same search on a fixed schedule, and compare actual first-seen times with those confirmed times. Record sample size, query, engine, country and short-delivery events. Without that test, report only observed search results and date-level publication data.

For a broader starting point, see the Video Search API overview; the video competitor monitoring guide covers a related task.

Keep a video freshness log

Store publication date and first-seen time separately, then review repeat samples and source pages for timing-sensitive claims.

Get an API key

Try the playground · Read the API reference

FAQ

Does publishedTime tell me the exact upload time?

No. Serpent documents YYYY-MM-DD or null. Check the source page or a platform-specific API for a finer timestamp when your decision needs one.

Can I filter videos by an exact from and to timestamp?

The documented Videos endpoint offers rolling freshness values and a time alias for day, week, month and year. It does not document arbitrary timestamp bounds.

Does a missing result mean a video was deleted?

No. Search ranking and result windows change, and a response may be short. Open the watch URL or check the platform API before calling it removed.

Should I use the YouTube Data API for YouTube-only freshness?

Often yes. Its official search and video resources provide YouTube-specific metadata under a quota model. Use it when you need the platform contract or finer timing than a cross-site search date.

Related Posts