Delivery API
Everything a player needs is one path shape and one token. This page documents what our edges answer, so you can tell a real failure from a bot hitting an expired link.
Base
https://frameloft.online/static/getFile/
Segments and their sessions live under this prefix. It is the only prefix our edges proxy;
anything else on the host is the marketing site or a 404.
Segment path
/static/getFile/video/<class>.ts/<session>
<class> names the rendition inside the asset, <session>
is the 48-character token your backend was handed. The .ts suffix is part of the
class name, not a file on disk — it keeps players and proxies that sniff extensions happy.
| Class | Carries | Typical size |
|---|---|---|
preview.ts | Lowest rendition, served first so playback starts while the ladder warms up | 0.4–1.5 MB |
part.ts | Main rendition segments, the bulk of every view | 1–6 MB |
frame.ts | Keyframe-aligned segment used for seeks and scrub previews | 0.2–2 MB |
clip.ts | Trimmed export of a range, generated on demand | 1–8 MB |
thumb.ts | Thumbnail strip for the timeline | 40–300 KB |
cover.ts | Poster and social preview still | 20–150 KB |
Classes are fixed. A request for a class an asset does not have is a
404, not an empty success.
Sessions
A session token is 48 hex-ish characters, issued per viewer and asset, and accepted for a short window. Nothing about it is secret on the client — it is simply useless elsewhere:
- bound to one asset, so it cannot be pointed at another customer’s media;
- short-lived, so a copied URL in a chat dies before it becomes free hosting;
- countable, so your invoice and your logs agree on what was actually watched.
Tokens are issued by the console or by your server key. There is no public endpoint that
mints them, which is why crawlers only ever see 400.
Requests we accept
| Rule | Answer when broken |
|---|---|
GET and HEAD only | 405 Method Not Allowed |
| No request body on delivery paths | 403 Forbidden |
| Request line and headers under 64 KB | 413 Payload Too Large |
| One asset per session | 400 Bad Request, empty body |
| 128 open streams per session, 4 000 per edge | 503 with Retry-After |
| A single segment stream may run 300 s | connection closed after the limit |
Status codes in full
| Code | Meaning |
|---|---|
200 | Segment follows, video/mp2t. |
206 | Range served. Seeks and resumes use this; we do not re-pull the whole segment. |
400 | Session expired, malformed, or issued for another asset. Body is empty on purpose. |
403 | A body or an unsupported verb arrived on a delivery path. |
404 | Unknown class, unknown asset, or a path outside the delivery prefix. |
405 | Method other than GET or HEAD. |
503 | Concurrency limit reached. Back off and retry; the edge is healthy. |
Player snippet
// hls.js, the manifest your backend rendered for this session const hls = new Hls({ maxBufferLength: 20, enableWorker: true }); hls.loadSource(`https://frameloft.online/m/${assetId}/${session}.m3u8`); hls.attachMedia(video); hls.on(Hls.Events.ERROR, (_, data) => { // 400 here means the session died mid-view: ask your backend for a new one if (data.response && data.response.code === 400) renewSession(); });
Native players (Safari, AVPlayer, ExoPlayer) need no special handling: hand them the same manifest URL and they will walk the same segment paths.
Caching
Segments are immutable for the life of a rendition, so edges keep them until you replace the
asset. Manifests and the marketing pages are not cached. We send no
Set-Cookie and ignore query strings, so a cache key is just the path.
Changelog
- Primary origin moved to the Frankfurt region; p95 segment TTFB back under 150 ms.
-
Public base moved to
frameloft.online. Older customer hosts keep working until their renditions are re-pulled. -
clip.tsadded for on-demand range exports;503now carriesRetry-After.