DEV Community

Eze
Eze

Posted on

Exploring BoTTube's Agent Video API With Python: Pagination, Stats and HATEOAS

BoTTube is an AI-native video platform where autonomous agents publish content and earn on hardware-verified infrastructure — part of the RustChain DePIN ecosystem. Its public JSON API is a nice case study in practical API integration patterns: cursor-free pagination, category filtering, and HATEOAS discovery links. This tutorial walks through all three in Python, with every output captured from a live run.

First contact: the stats endpoint

Before listing anything, get the lay of the land:

import json, urllib.request

BASE = "https://bottube.ai"
UA = {"User-Agent": "my-integration/0.1"}

def get(path):
    req = urllib.request.Request(BASE + path, headers=UA)
    with urllib.request.urlopen(req, timeout=20) as r:
        return json.loads(r.read().decode())

s = get("/api/stats")
print(s["agents"], "agents,", s["humans"], "humans")
for a in s["top_agents"][:3]:
    print(f"{a['display_name']}: {a['total_views']:,} views")
Enter fullscreen mode Exit fullscreen mode

Actual output:

1415 agents, 135 humans
Sophia Elya: 78,274 views
The Daily Byte: 53,097 views
SkyWatch AI: 41,859 views
Enter fullscreen mode Exit fullscreen mode

Two things worth noticing. The platform is overwhelmingly agentic — about 10.5 agents per human — and the stats endpoint already hands you a leaderboard, so you don't need to compute rankings yourself.

Filtering and paginating videos

/api/videos supports category filters and page-based pagination:

v = get("/api/videos?category=education&per_page=3")
print(f"total={v['total']} pages={v['pages']}")
for vid in v["videos"]:
    print(vid["title"][:60], "by", vid["agent_name"])
Enter fullscreen mode Exit fullscreen mode

Real output:

total=2916 pages=972
RustChain Operator Minute 2026-08-23: Old Computers Still Co by keon446b032231
RustChain Operator Minute 2026-08-22: One Machine One Signal by keon446b032231
RustChain Operator Minute 2026-08-21: Public Receipts Matter by keon446b032231
Enter fullscreen mode Exit fullscreen mode

With per_page=3 there are 972 pages. Page-based pagination has a classic pitfall: if new items are inserted while you iterate, you can see duplicates across pages. For a one-shot snapshot that's fine; for continuous crawling, deduplicate on the video ID.

Letting the server tell you where to go next

The nicest design decision in this API: /api/agents returns HATEOAS-style _links, so your client never hardcodes URL structure:

a = get("/api/agents?limit=2")
links = a["_links"]
print(list(links.keys()))
# ['discover', 'next', 'register', 'self']

nxt = links["next"]
href = nxt["href"] if isinstance(nxt, dict) else nxt

full = href if href.startswith("http") else BASE + href
req = urllib.request.Request(full, headers=UA)
with urllib.request.urlopen(req, timeout=20) as r:
    page2 = json.loads(r.read().decode())
print("page", page2["page"], "->", len(page2["agents"]), "agents")
# page 2 -> 2 agents
Enter fullscreen mode Exit fullscreen mode

If the API changes its query-string format tomorrow, a link-following client keeps working while a hardcoded-URL client breaks. When an API offers discovery links, use them.

A minimal resilient client

Putting the pieces together:

class BotTubeClient:
    def __init__(self, base="https://bottube.ai"):
        self.base = base
        self.ua = {"User-Agent": "bottube-client/0.1"}

    def _get(self, url):
        req = urllib.request.Request(url, headers=self.ua)
        with urllib.request.urlopen(req, timeout=20) as r:
            return json.loads(r.read().decode())

    def stats(self):
        return self._get(f"{self.base}/api/stats")

    def videos(self, **params):
        qs = "&".join(f"{k}={v}" for k, v in params.items())
        return self._get(f"{self.base}/api/videos?{qs}")

    def follow(self, link):
        # Follow a HATEOAS link whether it is absolute or relative.
        href = link["href"] if isinstance(link, dict) else link
        return self._get(href if href.startswith("http") else self.base + href)

c = BotTubeClient()
print(c.stats()["agents"])          # 1415
top_education = c.videos(category="music", per_page=5)
Enter fullscreen mode Exit fullscreen mode

One etiquette note: set a descriptive User-Agent. Public community APIs like this one can't distinguish polite integrations from scrapers otherwise.

What makes this API interesting beyond CRUD

The agent/human ratio isn't just trivia — if you're building analytics for multi-agent platforms, BoTTube's split of attribution (agent_id, is_human) plus view counts gives you a real dataset for studying agent-generated media economics. And since the platform pays creators through the RustChain ecosystem, view statistics tie directly to an actual value flow rather than being vanity metrics.

Everything above ran live against production at publication time — outputs are verbatim. The API requires no authentication for reads, so you can reproduce every snippet as-is.

Top comments (0)