Skip to content

# 🎬 Videos

## Overview

The `POST /api/videos` endpoint is used to retrieve videos from the given source. It contains information about the videos, channels, and other relevant information.

### Request

`POST /api/videos`

    {
      "query": "kittens",
      "channel": "my-channel",
      "channels": ["my-channel", "another-channel"], // Channels supporting multiple sites/channels
      "sort": "new",
      "page": 1,
      "pageSize": 40,
      "clientVersion": "2.2.7-38", // {version}—{build}, useful for feature availability
      "blockedKeywords": ["example"],
      "blockedUploaders": ["uploader-id-123"],
      // Your server's custom parameters will be added here
      "quality": "hd",
      "duration": "short",
      "flavor": "mint_chocolate_chip"
    }

### Request Parameters

Field | Type | Default | Description
--- | --- | --- | ---
`query` | `string` | `""` | Search query
`channel` | `string` | — | Single channel identifier (as defined in your server's status response)
`channels` | `string[]` | — | Multiple channel identifiers to search across (overrides `channel`)
`sort` | `string` | `"relevance"` | Sort option ID (as defined in your server's status response options)
`page` | `number` | `1` | Page number for pagination
`pageSize` | `number` | `40` | Results per page
`clientVersion` | `string` | — | App version string (e.g., `"2.2.7-38"`)
`blockedKeywords` | `string[]` | `[]` | Keywords to exclude from results
`blockedUploaders` | `string[]` | `[]` | Uploader IDs to exclude from results
(server options) | `any` | — | Any option IDs defined in your server's status response are forwarded as-is

Server-Defined Filter Options

Filter parameters beyond the core ones above (e.g., duration, quality, content type, sexuality) are entirely defined by your server via the `options` array in the Status endpoint response. Whatever `id` values you expose there will be sent back verbatim when the user selects them. Your `/api/videos` handler receives them as top-level keys in the request body.

### Response

    {
      "pageInfo": {
        "hasNextPage": true,
        "recommendations": ["cats", "pets", "animals"],
        "error": null, // e.g. "No results were found"
        "message": null, // e.g. "Showing cached results" or "Some content filtered based on preferences"
        "parameters": {
          "totalResults": 1000
        }
      },
      "items": [
        {
          "id": "c85017ca87477168d648727753c4ded8a35f173e22ef93743e707b296becb299",
          "title": "20 Minutes of Adorable Kittens   BEST Compilation",
          "url": "https://www.example.com/watch?v=y0sF5xhGreA",
          "duration": 110,
          "channel": "my-channel",
          "thumb": "https://i.example.com/vi/y0sF5xhGreA/hqdefault.jpg",
          "views": 14622653,
          "rating": 95,
          "uploader": "Awesome Vids",
          "uploaderUrl": "https://www.example.com/@awesomeVids",
          "uploaderId": "awesomeVids",
          "verified": false,
          "isVR": false,
          "tags": ["cats", "kittens", "pets"],
          "categories": ["Animals", "Entertainment"],
          "uploadedAt": "2025-01-03T10:15:54.000Z",
          "preview": "https://example.com/preview.mp4",
          "uploaderId": "petcollective",
          "aspectRatio": 1.78,
          "uploaderProfile": {
            "id": "abc123",
            "name": "Awesome Vids",
            "avatar": "https://example.com/avatar.jpg",
            "videoCount": 342,
            "totalViews": 14500000
          },
          "formats": [
            {
              "url": "https://www.example.com/watch?v=y0sF5xhGreA",
              "quality": 1080,
              "format": "mp4",
              "formatId": "mp4-1080",
              "protocol": "m3u8_native",
              "ext": "mp4",
              "httpHeaders": {
                "Referer": "https://www.example.com"
              }
            }
          ],
          "embed": {
            "source": "https://www.example.com/watch?v=y0sF5xhGreA",
            "html": "<iframe src='...' width='560' height='315'></iframe>",
            "width": 560,
            "height": 315
          }
        }
      ]
    }

## Field Reference

### Response Root Fields

Field | Type | Description
--- | --- | ---
`pageInfo` | `PageInfo` | Pagination and status metadata
`items` | `Video[]` | Array of video objects

### PageInfo Object Fields

Field | Type | Required | Description
--- | --- | --- | ---
`hasNextPage` | `boolean` | ✅ | Whether more results are available
`recommendations` | `string[]` | ⚪ | Suggested search terms
`error` | `string` | ⚪ | Error message (e.g., "No results were found")
`message` | `string` | ⚪ | Informational message (e.g., "Showing cached results")
`parameters` | `object` | ⚪ | Additional metadata (e.g. `"cursor": "abc123"`)

Error vs Message

- `error`: Use for actual errors that prevent normal operation (no results, API failure, invalid query)
- `message`: Use for informational notices that don't indicate failure (cached results, filtered content, rate limiting info, feature announcements)

### Video Object Fields

Field | Type | Required | Description
--- | --- | --- | ---
`title` | `string` | ✅ | Video title
`url` | `string` | ✅ | Video source URL
`duration` | `number` | ✅ | Video length in seconds
`channel` | `string` | ✅ | Source channel/platform identifier
`thumb` | `string` | ✅ | Thumbnail image URL
`id` | `string` | ⚪ | Unique video identifier (auto-generated if not provided)
`network` | `string` | — | Alias for `channel` — added by the server on every response item
`isVR` | `boolean` | — | `true` when VR content is detected from tags — added by the server
`uploaderProfile` | `UploaderProfile` | — | Partial uploader profile card — added by the server when a profile exists
`views` | `number` | ⚪ | View count
`rating` | `number` | ⚪ | Video percent rating score
`uploader` | `string` | ⚪ | Content creator name
`uploaderUrl` | `string` | ⚪ | Creator's profile URL
`uploaderId` | `string` | ⚪ | Unique uploader identifier
`tags` | `string[]` | ⚪ | Array of content tags (ordering is up to your server)
`categories` | `string[]` | ⚪ | Array of content categories
`uploadedAt` | `Date|String` | ⚪ | Upload date (flexible format)
`preview` | `string` | ⚪ | Preview GIF/video URL
`formats` | `Format[]` | ⚪ | Video format array
`aspectRatio` | `number` | ⚪ | Video aspect ratio (e.g., 1.78 for 16:9)
`embed` | `EmbedData` | ⚪ | Embed data for Reddit-style content
`isLive` | `boolean` | ⚪ | `true` when the video is a live stream — replaces duration with a "LIVE" badge
`liveStatus` | `string` | ⚪ | yt-dlp live status: `"live"`, `"not_live"`, `"was_live"`, `"post_live"` — used alongside `isLive` for badge and availability UI
`availability` | `string` | ⚪ | Content availability hint — `"offline"` shows an unavailable badge instead of duration

Live & Availability Display

The app uses `isLive` and `liveStatus` together to determine how a video card renders:

- Live badge shown when `isLive == true` or `liveStatus == "live"` — the duration label is hidden and replaced with a "LIVE" indicator.
- Unavailable badge shown when `availability == "offline"`, or `liveStatus` is `"not_live"` or `"was_live"` — again replacing the duration.
- Normal duration shown otherwise.

During playback, the player also detects live streams from the HLS stream itself (indefinite duration) and adjusts transport controls accordingly — so `isLive` is not required for playback to behave correctly, but it ensures the video card UI is accurate before the stream is loaded.

### UploaderProfile Object

Attached to each video item when a known uploader profile exists. Used to render the uploader row on video cards without a separate API call.

Field | Type | Description
--- | --- | ---
`id` | `string` | Uploader identifier
`name` | `string` | Display name
`avatar` | `string \| null` | Avatar image URL (canonical)
`videoCount` | `number` | Total indexed videos
`totalViews` | `number` | Aggregated view count

See JSON key names for camelCase vs snake_case and the legacy `profile_picture_url` avatar key.

### Format Object Fields

Field | Type | Required | Description
--- | --- | --- | ---
`url` | `string` | ✅ | Direct video stream URL
`formatId` | `string` | Auto-generated | Format identifier (provided or auto-generated)
`ext` | `string` | ⚪ | File extension ("mp4", "webm")
`protocol` | `string` | ⚪ | Stream protocol ("https", "m3u8_native") - important for playback
`httpHeaders` | `object` | ⚪ (🔑 CDN) | Required headers for CDN authentication
`height` | `number` | ⚪ (📱 UX) | Video height in pixels (240, 480, 720, 1080) - recommended for better format IDs
`width` | `number` | ⚪ | Video width in pixels
`resolution` | `string` | ⚪ | Resolution string ("720p" or "1280x720")
`format` | `string` | ⚪ | Format description
`fps` | `number` | ⚪ | Frames per second
`quality` | `number` | ⚪ | yt-dlp quality score (fallback for `formatId` generation)
`vcodec` | `string` | ⚪ | Video codec ("avc1.640020", "h264")
`acodec` | `string` | ⚪ | Audio codec ("mp4a.40.2", "aac")
`tbr` | `number` | ⚪ | Total bitrate
`abr` | `number` | ⚪ | Audio bitrate
`vbr` | `number` | ⚪ | Video bitrate
`dynamicRange` | `string` | ⚪ | Dynamic range ("SDR", "HDR")
`aspectRatio` | `number` | ⚪ | Video aspect ratio
`filesize` | `number` | ⚪ | File size in bytes
`language` | `string` | ⚪ | Audio language
`container` | `string` | ⚪ | Container format
`downloaderOptions` | `object` | ⚪ | Download configuration (defaults to 1MB chunks)

CDN Authentication

Many premium/CDN streams require `httpHeaders` for authentication. Common headers include:

- `User-Agent` - Required by most CDNs
- `Referer` - Required for hotlink protection
- `Accept`, `Accept-Language` - For content negotiation

Format ID Generation

If `formatId` is not provided, it's auto-generated in this order:

1. `{format}_{height}p` (e.g., "mp4_720p")
2. `{format}_{resolution}` (e.g., "mp4_1280x720")
3. `{format}_{quality}` (e.g., "mp4_2.5")
4. `{format}` only

Best Practices

- Only `url` is truly required - everything else is optional
- Include `height` for meaningful format IDs and better user experience
- Add `httpHeaders` for CDN streams that need authentication
- Providing `formats` bypasses yt-dlp extraction (faster response)
- Providing `preview` avoids on-device preview generation

Was this page helpful?
Thanks for your feedback!
Thanks for your feedback! Help us improve this page by