Building media processing tools made us dig into how platforms like TikTok structure their video delivery pipelines. The answer was more interesting than we expected and changed how we think about video infrastructure entirely.
When we started building internal media processing tools at Execross, one of the things we kept running into was how differently various platforms structure their video delivery pipelines. Most people think of video delivery as a simple thing: there is a file somewhere, you download it, done. The reality for large-scale short-form video platforms is considerably more interesting than that.
This post is a writeup of what we learned about video delivery infrastructure while building our own tooling, and why understanding it at a technical level matters if you are working on anything in the media or content space.
The basics of video delivery at scale
A naive video hosting system stores a video file and serves it when requested. This works fine at small scale. At the scale of a platform serving billions of videos to hundreds of millions of users daily, it falls apart almost immediately.
The problems are predictable once you think about them. Storage costs grow linearly with content volume. Bandwidth costs are enormous at scale. Latency for users far from storage servers is unacceptable. Transcoding the various formats users upload into web-friendly versions requires significant compute. Every major video platform has had to solve all of these problems, and the solutions they have landed on share some common architectural patterns.
Transcoding is the first layer. When a video is uploaded, it gets transcoded into multiple quality tiers and formats. A single uploaded video becomes many derived files: different resolutions, different codecs, different bitrates optimized for different network conditions. The platform serves different versions to different clients depending on device capability, network speed, and user settings.
CDN distribution is the second layer. Transcoded files get pushed to edge nodes around the world so users can fetch video data from servers geographically close to them. This is why video loads quickly regardless of where the platform's origin storage is located.
Signed URL access control is the third layer. Because video files are expensive assets, platforms use time-limited signed URLs to control access. A URL to a video file is not permanent. It expires, typically within hours or sometimes days. This prevents hotlinking to video assets directly, forces all traffic through the platform's access control layer, and allows the platform to revoke access when needed.
How short-form platforms handle this differently
Short-form video platforms have specific constraints that push their architecture in particular directions. Videos are short, which means they need to start playing almost instantly. Users scroll through feeds at high speed, which means the platform needs to pre-buffer upcoming videos before the user gets to them. The content is extremely social, which means the same video might get watched millions of times in a short window.
These constraints push toward aggressive pre-buffering strategies, multiple parallel CDN configurations, and some interesting approaches to URL structure and video metadata.
One pattern that is consistent across short-form platforms is that the video metadata API response contains multiple URL options for the same video. There will typically be a standard delivery URL and one or more alternative URLs optimized for different use cases: lower latency delivery, download-optimized delivery, lower quality for bandwidth-constrained connections, and so on. The client application chooses which URL to use based on its current state and the user's settings.
This matters for anyone building media processing tools because it means the URL you see when you inspect a video element in the browser might not be the same as what is available through the metadata API. The browser might be streaming from a different CDN endpoint than what the API returns.
Watermarking at the delivery layer
Watermarking is an interesting case study in how platforms balance user experience with attribution and rights management.
The naive approach to watermarking is to burn it into the video during transcoding. This is robust from a protection standpoint but inflexible. You cannot remove a burned-in watermark without re-transcoding the entire video, and you cannot show different watermarks in different contexts.
A more sophisticated approach is to apply watermarks at the delivery layer rather than the encoding layer. The base video file stored on disk is clean. The watermark gets composited on top either during delivery processing or on the client device itself. This allows the platform to show different overlays for different contexts: a creator username for social sharing, a platform logo for embedded players, no watermark when the creator themselves downloads their own content.
This delivery-layer watermarking approach is why the same underlying video asset can appear with or without a watermark depending on how and where it is accessed. The watermark is not a property of the stored video file but of the delivery context.
Understanding this distinction is important when building any kind of media processing tool. The file you are working with and the visual output a user sees can be meaningfully different things.
URL expiry and caching strategy
One of the most common mistakes when first building media processing pipelines is treating video URLs as permanent. They are not. Platform CDN URLs for video content have TTLs that vary by platform but are typically somewhere between one hour and 24 hours.
If you cache a raw video URL and try to serve it 12 hours later, it will likely return a 403. Building correct TTL handling into your caching layer from the start saves a lot of debugging time later.
The right approach is to cache the video metadata (which tends to be stable for much longer) and generate fresh delivery URLs when you actually need to serve or download the video content. This two-level caching strategy keeps things both fast and correct.
`python
import time
from dataclasses import dataclass
from typing import Optional
@dataclass
class VideoMetadata:
video_id: str
title: str
duration_seconds: float
width: int
height: int
@dataclass
class DeliveryUrl:
url: str
fetched_at: float
ttl_seconds: float = 3600.0 # conservative default
@property
def is_expired(self) -> bool:
age = time.time() - self.fetched_at
# Treat as expired at 80% of TTL to avoid serving stale URLs
return age > (self.ttl_seconds * 0.8)
class VideoCache:
def __init__(self):
self._metadata: dict[str, VideoMetadata] = {}
self._delivery_urls: dict[str, DeliveryUrl] = {}
def get_metadata(self, video_id: str) -> Optional[VideoMetadata]:
return self._metadata.get(video_id)
def set_metadata(self, video_id: str, metadata: VideoMetadata) -> None:
self._metadata[video_id] = metadata
def get_delivery_url(self, video_id: str) -> Optional[str]:
entry = self._delivery_urls.get(video_id)
if entry is None or entry.is_expired:
return None
return entry.url
def set_delivery_url(self, video_id: str, url: str, ttl: float = 3600.0) -> None:
self._delivery_urls[video_id] = DeliveryUrl(
url=url,
fetched_at=time.time(),
ttl_seconds=ttl,
)
`
Rate limiting as a first-class concern
Rate limiting is real for any tool that makes frequent API calls to external platforms. Most platforms implement limits at multiple layers simultaneously: per IP address, per session or authentication token, and sometimes per account. Building rate limiting in from the beginning is much easier than retrofitting it later.
The token bucket algorithm is well suited for this. You maintain a bucket of tokens that refills at a constant rate up to a maximum capacity. Each API call consumes one token. If the bucket is empty, the call waits until a token is available. This smooths out burst traffic and keeps your request rate consistently below the limit.
`python
import asyncio
import time
class TokenBucket:
def __init__(self, rate: float, capacity: int):
self.rate = rate # tokens per second
self.capacity = capacity # max tokens
self.tokens = float(capacity)
self.last_refill = time.monotonic()
async def acquire(self) -> None:
while True:
now = time.monotonic()
elapsed = now - self.last_refill
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_refill = now
if self.tokens >= 1.0:
self.tokens -= 1.0
return
wait = (1.0 - self.tokens) / self.rate
await asyncio.sleep(wait)
`
We set our token rate to around 70 to 80 percent of the actual API limit. This headroom means we almost never hit a rate limit response in practice, which keeps the pipeline fast and avoids the retry overhead that rate limit responses introduce.
The multiple-URL problem in practice
When a metadata API response contains multiple URLs for the same video, you need a strategy for which one to use. For our use case we ended up with a simple priority order based on what we needed.
For high-quality archiving we want the highest resolution URL. For generating preview thumbnails we want the lowest quality URL to minimize bandwidth. For anything that needs to be processed in real time we want the URL with the lowest expected latency from our server's location.
`python
from enum import Enum
from dataclasses import dataclass
class UrlPurpose(Enum):
ARCHIVE = "archive" # highest quality
PREVIEW = "preview" # lowest quality, fastest
PROCESSING = "processing" # balanced
@dataclass
class VideoUrl:
url: str
quality: str # e.g. "1080p", "720p", "360p"
bitrate_kbps: int
def select_url(urls: list[VideoUrl], purpose: UrlPurpose) -> VideoUrl:
if not urls:
raise ValueError("No URLs provided")
sorted_by_quality = sorted(urls, key=lambda u: u.bitrate_kbps, reverse=True)
if purpose == UrlPurpose.ARCHIVE:
return sorted_by_quality[0]
elif purpose == UrlPurpose.PREVIEW:
return sorted_by_quality[-1]
else:
# Middle quality for general processing
mid = len(sorted_by_quality) // 2
return sorted_by_quality[mid]
`
What we ended up building
Our internal media processing service ended up being a relatively thin layer that sits between our applications and the external platforms we work with. It handles authentication state, rate limiting, metadata caching with correct TTL handling, delivery URL freshness, and output normalization into our internal data model.
The normalization layer turned out to be more valuable than we expected. Different platforms return video metadata in wildly different schemas. Having a single internal representation for a video regardless of its source made all our downstream code simpler and easier to test. A video is a video regardless of where it came from.
The whole thing runs on a single modest VPS and handles our internal processing volume comfortably. For most small-to-medium tool builders, a well-designed simple service beats a complex distributed system every time. The complexity can always be added later if the scale actually demands it.
Things we wish we had done from the start
Proper URL expiry handling. We got bitten by stale URLs serving 403s in production before we built the TTL tracking described above. It is a subtle class of bug because it only shows up for content that was fetched more than an hour ago, which means it can sit undetected in development.
More conservative rate limits. We started too aggressive and had to dial things back after hitting limits in production. Starting at 50% of the actual limit and working up is less exciting but much more stable.
Structured logging from day one. When something goes wrong in a media pipeline, you want to know exactly which video ID failed, which URL was attempted, what the response status was, and how many retries happened. Adding this after the fact is painful.