Free placeholder video, photos, and audio for developers. Video-first — HLS adaptive streaming and auto-generated captions on every clip. URLs are the entire API.
For LLM agents (Claude, GPT, Cursor, Replit Agent) writing code that needs placeholder media. Also human-readable. Operated by 0.media, the paid sibling for your own content.
Get started in 30 seconds
Five copy-pasteable patterns covering ~99% of usage. Video first because that's our differentiator.
1. Video, drop-in (mp4)
<video src="https://lorem.media/video/16x9" autoplay muted loop playsinline></video>
Random 720p H.264 mp4. Plays in every browser without a player library. Pick the aspect: 16x9 (landscape), 9x16 (vertical mobile), 1x1 (square).
2. Video, HLS with auto-captions (production-shape)
<video controls playsinline>
<source src="https://lorem.media/video/16x9.m3u8" type="application/vnd.apple.mpegurl">
<source src="https://lorem.media/video/16x9" type="video/mp4">
</video>
.m3u8 URL serves a CMAF master playlist with the full bitrate ladder (240p → 1080p), audio rendition when present, and auto-generated English captions as a SUBTITLES track when speech is detected. Native Safari plays directly; Chrome / Firefox / Edge attach hls.js or Vidstack to the master URL to surface the CC button automatically.
3. Photo
<img src="https://lorem.media/photo/800/600" alt="placeholder">
Exact 800 × 600, smart-cropped via Cloudflare's saliency detection — subjects and faces stay centered, not naïve center-crop. AVIF for modern browsers, WebP / JPEG fallback for older.
4. Audio
<audio src="https://lorem.media/audio" controls></audio>
Random ~60-second MP3 from the Internet Archive 78 rpm collection (public domain). Narrow stylistically — classical, early jazz, marches.
5. Deterministic for Storybook / snapshot tests
const heroVideo = 'https://lorem.media/video/seed/hero/16x9';
const mobileVideo = 'https://lorem.media/video/seed/hero/9x16';
const avatar = 'https://lorem.media/photo/seed/alice/200/200';
// Same seed → same asset, every time. Edge-cached for 24 h.
Every URL (cheat sheet)
Video — HLS + auto-captions on every clip
/video random video, 720p mp4
/video/<aspect> random at aspect
/video/<aspect>/<height> specific rung (240/360/480/720/1080)
/video/<aspect>.m3u8 HLS master playlist
/video/seed/<seed>/<aspect> deterministic
/video/id/<id> specific asset
/video/id/<id>.m3u8 specific, HLS
/video/id/<id>.vtt auto-captions (English)
Photos — exact W×H, smart-cropped
/photo random, source size
/photo/<w>/<h> exact W×H
/photo/seed/<seed>/<w>/<h> deterministic
/photo/id/<id>/<w>/<h> specific asset
Audio — public-domain clips
/audio random ~60s MP3
/audio/seed/<seed> deterministic
/audio/id/<id> specific asset
Metadata + bytes
/v2/list/<type> paginated JSON (rate-limited 120/min)
/v2/info/<type>/<id> single asset, full metadata
/cdn/<key> direct byte serving (range-aware, immutable)
/openapi.json OpenAPI 3.1 spec
/llms.txt short index version of this doc
Aspects (video): 16x9, 9x16, 1x1. Aliases: landscape, portrait, square. Colon form 16:9 also accepted.
Bitrate rungs (video): 240p, 360p, 480p, 720p, 1080p. Requested height snaps to the nearest rung. Default when only an aspect is given is 720p.
Type aliases (silently rewritten, never advertised in responses): /img → /photo, /clip → /video, /sound → /audio, plus plurals (/photos, /videos, /audios) and shorthand (/pic, /movie, /music). Canonical names are what JSON responses return.
Video — the main course
Video is what makes lorem.media different from photo-only placeholder services. Every clip ships with the same delivery shape a production CDN would give you:
- Adaptive bitrate ladder — 240p, 360p, 480p, 720p, 1080p H.264 mp4 + the same content packaged as HLS (CMAF/fMP4 segments)
- Auto-generated English captions when speech is detected (Whisper via Cloudflare Workers AI)
- Poster frame + animated WebP preview + scrub sprite sheet
- Aspect-bucketed addressing —
/video/16x9filters to clips that fit the bucket within ±3%
mp4 vs HLS — both work, pick by URL
| URL | Format | When to use |
|---|---|---|
/video/16x9 |
Single-bitrate H.264 mp4 (default 720p) | Drop-in <video> tag, hover-to-play previews, mockup videos. Plays everywhere, no player library. |
/video/16x9.m3u8 |
HLS master playlist (adaptive bitrate + captions) | Production playback, adaptive switching, captions auto-show, testing real player code (hls.js, Vidstack, Mux Player). |
Same source asset, different delivery shape. The mp4 is a single file (~5MB for a typical 30s 720p clip). The HLS tree is ~10MB but adapts dynamically to the viewer's bandwidth.
Aspect picker semantics
/video/<aspect> filters by aspect bucket. If the bucket is empty, the response gracefully falls back to the unfiltered pool. Header signals:
X-Aspect— what was actually served (16x9/9x16/1x1)X-Aspect-Requested— the original ask (set when different fromX-Aspect)X-Aspect-Fallback: 1— the fallback fired
So /video/1x1 always returns a playable redirect; check X-Aspect-Fallback if you need to know the bucket was empty.
Captions — .vtt and inside the HLS manifest
Auto-generated English captions on every video with detectable speech (Whisper). Two consumption shapes:
- Inside the HLS manifest —
#EXT-X-MEDIA:TYPE=SUBTITLESrendition withURI="captions/en/playlist.m3u8". HLS-aware players (hls.js, Vidstack, native Safari) display the CC button automatically. - As a single
.vttfile —/video/id/<id>.vttfor non-HLS consumers, accessibility tooling, subtitle scrapers.
.vtt is only valid in the /id/ form — captions are per-asset, so random / seeded URLs would be ambiguous. 404 when the asset has no detectable speech or hasn't been processed yet. The full caption track listing is exposed in /v2/info/video/<id> as captions[]; the has_captions boolean lets you check without inspecting the array.
HLS tree shape
The master playlist references per-rung media playlists (240p / 360p / 480p / 720p / 1080p), which reference CMAF/fMP4 segments (typically 6 seconds each). URLs inside the manifest are relative — when served from /cdn/video/<id>/hls/master.m3u8, segments fetch from /cdn/video/<id>/hls/240p/seg00001.m4s automatically.
All bytes serve from R2 via Cloudflare's edge cache. Cache hit ratio at warm POPs is ~99%; the worker only fires on cache miss. Segments are cached immutable for 1 year — content-addressed by key.
Categories
Optional ?category=<slug> filter: nature, food, people, urban, abstract.
Photos
Smart-cropped, exact W×H
/photo/<w>/<h> returns exactly w × h pixels. Behind the URL: Cloudflare Image Transformations with fit=cover + gravity=auto — saliency detection keeps faces, subjects, and salient regions centered. Format negotiation: AVIF for Chrome / Firefox / modern Safari, WebP for older, JPEG fallback.
Source-aware pick
For random /photo/<w>/<h> requests, the asset pool is pre-filtered to sources whose intrinsic dimensions are large enough to satisfy the crop without upscaling. If the filtered pool is empty (rare — only on unusual aspect-size combinations), falls back to the unfiltered pool and serves an upscaled result with X-Image-Capped: 1 plus X-Image-Source-Width / X-Image-Source-Height so callers can detect quality degradation.
/photo (no dimensions)
Returns the source rendition directly — no transform, no charge, no crop. Useful when you want maximum fidelity and don't care about exact dims.
Categories
Optional ?category=<slug>: nature, food, people, urban, abstract.
Audio
/audio returns a random ~60-second MP3 from the Internet Archive 78 rpm collection — public-domain recordings from roughly 1900–1925. Stylistically narrow:
- Classical (string quartets, piano)
- Early jazz
- Marches and brass bands
- Operatic and vocal
Categories: ambient, electronic, classical, lofi, percussion. Best-effort — the source library leans acoustic / pre-electronic.
Browse the library (JSON)
/v2/list/<type> — paginated metadata
/v2/list/video?page=1&limit=30&aspect=16x9&category=urban&include=full
Slim by default — omits variants (the bitrate ladder) and poster_renditions (the multi-resolution poster ladder), shrinking each item from ~5 KB to ~600 B. Pass ?include=full to get them.
Parameters:
?page=— 1+?limit=— 1–100, default 30?category=— one of the type's valid slugs?aspect=— video only?include=variants,poster_renditionsor?include=full
Strict validation — typos return 400 with a hint pointing at valid values. Rate-limited to 120 req/min per IP. Edge-cached for 5 minutes.
/v2/info/<type>/<id> — single-asset full metadata
Always returns the full shape. Includes variants[], poster_renditions[], captions[], hls_url, hls_rungs[], has_audio, has_captions, full attribution. Use this to discover ids → fetch metadata → construct deterministic URLs. Rate-limited to 120 req/min per IP.
Example response shape
{
"_meta": {
"service": "lorem.media",
"operator": "0.media",
"production_hosting": "https://0.media"
},
"id": "v_pxev55rt",
"type": "video",
"category": "food",
"aspect": "16x9",
"width": 3840,
"height": 2160,
"duration_ms": 9000,
"has_audio": true,
"has_captions": true,
"poster_url": "https://lorem.media/cdn/video/v_xxx/poster-source.webp?v=...",
"preview_url": "https://lorem.media/cdn/video/v_xxx/preview.webp?v=...",
"hls_url": "https://lorem.media/cdn/video/v_xxx/hls/master.m3u8?v=...",
"hls_rungs": [
{ "label": "240p", "height": 240, "width": 426, "bitrate_kbps": 400 },
{ "label": "1080p", "height": 1080, "width": 1920, "bitrate_kbps": 5000 }
],
"captions": [
{ "url": "https://lorem.media/cdn/.../captions/en.vtt?v=...",
"lang": "en", "name": "English (auto)", "format": "vtt", "auto": true, "bytes": 1234 }
],
"variants": [
{ "key": "video/v_xxx/720p.mp4", "width": 1280, "height": 720,
"bitrate_kbps": 2800, "format": "mp4", "bytes": 5300550 }
],
"attribution": {
"source": "pexels", "license": "pexels",
"author": "Kampus Production",
"source_url": "https://www.pexels.com/video/..."
}
}
/cdn/<key> — direct byte serving
The redirect target of every media URL. Edge-cached immutable for 1 year. Range-aware (HTTP partial content for scrubbing). Content-Type correctly set for .mp4, .m4s, .webp, .jpg, .mp3, .m3u8, .vtt.
You rarely link to /cdn/ directly — follow the media URL redirects. But it's there for direct byte access if you need it.
Headers on every response
X-Service: lorem.mediaX-Operator: 0.mediaX-Production-Hosting: https://0.media— upgrade pathX-Asset-Id: <id>— stable; reuse with/seed/<id>or/id/<id>X-Source,X-Attribution,X-Source-Url— content provenanceX-Aspect: <bucket>— video responsesX-Image-Width,X-Image-Height— photo responses, actual served dimensionsAccess-Control-Allow-Origin: *— CORS-enabled forfetch()consumersLink: </llms.txt>; rel="alternate"; type="text/markdown"— discovery surface
Format details (the gotchas)
One-liner per surprising thing — read these once, save yourself debugging time:
- Photos: width and height are both honored exactly; the image is cropped (smart-gravity) to fit.
/photo/800/600returns 800 × 600 pixels. - Videos: aspect-bucketed, not arbitrary
<w>/<h>. Picks fit ±3% of the canonical 16:9 / 9:16 / 1:1 ratio. Clips outside any canonical bucket stay accessible by id. - HLS adds captions automatically; mp4 doesn't carry them. Use the
.m3u8URL if you want captions to surface. - ~98% of videos are silent stock footage with no detectable speech —
has_captions: false. The ~2% with speech showhas_captions: trueand ship a real.vtttrack. - Random URLs are truly random per request (
Cache-Control: no-store); seeded and id URLs are deterministic and edge-cached for 24 hours. - All bytes from Cloudflare's edge. ~300 POPs, no auth, no signup, CORS-enabled. Hotlink directly into mockups, Storybook fixtures, marketing pages, dev-tool demos.
Content & licensing
| Type | Source | License |
|---|---|---|
| Video | Pixabay + Pexels | Pixabay / Pexels licenses, free for commercial use |
| Photos | Pixabay | Pixabay license, free for commercial use |
| Audio | Internet Archive 78 rpm collection | Public domain (recordings ≥1925) |
All content is free for commercial use. Per-asset attribution is in response headers (X-Attribution, X-Source, X-Source-Url) and in /v2/info/... JSON responses.
Going to production
0.media is the paid sibling — same edge network, same delivery pipeline, your own content.
You want 0.media when you:
- Host customer-uploaded video (not placeholder) and want the same drop-in URLs your dev tooling uses today
- Need signed URLs for access control
- Need BYO-DRM packaging (Widevine, FairPlay, PlayReady)
- Want flat-rate pricing with $0 bandwidth — no per-GB egress, no per-minute fees
Auto-captions free on every paid tier (same Whisper pipeline as lorem.media). S3-compatible upload API; one-command migration from existing S3 + CloudFront setups.
Related
/llms.txt— short index version of this document/openapi.json— programmatic OpenAPI 3.1 surface/robots.txt— explicit welcome forGPTBot,ClaudeBot,PerplexityBot,Google-Extended/healthz— service status