---
title: "Streams"
description: "Stream management API — create, start, stop, and configure live broadcasts"
canonical: "https://docs.ainative.studio/docs/live-streaming/streams"
last-updated: "2026-10-22T17:48:46.689Z"
---

# Streams

Source: https://docs.ainative.studio/docs/live-streaming/streams

> Stream management API — create, start, stop, and configure live broadcasts

## Streams

Streams are the core resource of the Live Streaming API. A stream has a lifecycle — created offline, pushed live via RTMPS, then ended. Each stream is backed by a Cloudflare Live input and returns an HLS playback URL for viewers.

## Create a stream

<span className="badge badge--post">POST</span> `/api/v1/streams/`

Creates a new livestream and provisions a Cloudflare Live ingest input. Returns RTMPS credentials for your broadcasting software.

**Auth required:** Yes

**Rate limit:** 10 requests per minute

A user can only have one active (live) stream at a time. Creating a second stream while one is active returns `400`.

### Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | Yes | Stream title |
| `description` | string | No | Stream description |
| `category_id` | UUID | No | Category to browse under |
| `thumbnail_url` | string | No | Thumbnail image URL |

```json
{
  "title": "Building a REST API in 60 minutes",
  "description": "Live coding session — FastAPI from scratch",
  "category_id": "7a2f3c80-e11b-4d8a-9e3c-1f2a3b4c5d6e"
}
```

### Response

Returns a `StreamWithIngestResponse` including private ingest credentials.

```json
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "title": "Building a REST API in 60 minutes",
  "description": "Live coding session — FastAPI from scratch",
  "status": "offline",
  "viewer_count": 0,
  "playback_url": "https://customer-xyz.cloudflarestream.com/abc123/manifest/video.m3u8",
  "thumbnail_url": null,
  "started_at": null,
  "created_at": "2026-04-25T10:00:00Z",
  "ingest": {
    "rtmps_url": "rtmps://live.cloudflare.com:443/live/",
    "stream_key": "your-stream-key-here"
  }
}
```

Configure OBS: Settings → Stream → Custom, set **Server** to `rtmps_url` and **Stream Key** to `stream_key`.

---

## List streams

<span className="badge badge--get">GET</span> `/api/v1/streams/`

Browse and discover livestreams. Live streams appear first, sorted by viewer count. Ended streams follow, sorted by most recent.

**Auth required:** No (optional — authenticated users may see additional metadata)

### Query parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `status_filter` | string | — | Filter: `live`, `ended`, or `all` |
| `category_id` | UUID | — | Filter by category |
| `user_id` | UUID | — | Filter streams from a specific user |
| `search` | string | — | Search in title and description |
| `tenant_id` | UUID | — | Filter by organization (multi-tenant scoping) |
| `page` | integer | `1` | Page number |
| `per_page` | integer | `20` | Results per page (max 100) |

```bash
## Get all live streams in the "Technology" category
GET /api/v1/streams/?status_filter=live&category_id=7a2f3c80-...&per_page=20
```

### Response

```json
{
  "streams": [
    {
      "id": "a1b2c3d4-...",
      "title": "Building a REST API in 60 minutes",
      "status": "live",
      "viewer_count": 142,
      "playback_url": "https://customer-xyz.cloudflarestream.com/abc123/manifest/video.m3u8",
      "thumbnail_url": "https://...",
      "started_at": "2026-04-25T10:05:00Z",
      "streamer": {
        "id": "...",
        "username": "alice",
        "display_name": "Alice Chen",
        "avatar_url": "https://...",
        "follower_count": 1523,
        "is_verified": false
      }
    }
  ],
  "total": 47,
  "page": 1,
  "per_page": 20,
  "has_more": true
}
```

---

## Get stream details

<span className="badge badge--get">GET</span> `/api/v1/streams/id/{stream_id}`

Get full details for a specific stream. If the authenticated user is the stream owner, the response includes ingest credentials (`rtmps_url`, `stream_key`).

**Auth required:** No (owner auth reveals ingest info)

### Path parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `stream_id` | UUID | The stream ID |

```bash
GET /api/v1/streams/id/a1b2c3d4-e5f6-7890-abcd-ef1234567890
Authorization: Bearer $API_KEY
```

---

## Update stream

<span className="badge badge--patch">PATCH</span> `/api/v1/streams/id/{stream_id}`

<span className="badge badge--put">PUT</span> `/api/v1/streams/id/{stream_id}`

Update stream title, description, category, or thumbnail. Owner only. Both `PUT` and `PATCH` are supported.

**Auth required:** Yes (owner only)

### Request body

All fields are optional — include only what you want to change.

| Field | Type | Description |
|-------|------|-------------|
| `title` | string | Stream title |
| `description` | string | Stream description |
| `category_id` | UUID | Category ID |
| `thumbnail_url` | string | Thumbnail URL |

---

## Delete stream

<span className="badge badge--delete">DELETE</span> `/api/v1/streams/id/{stream_id}`

Permanently delete a stream and its associated Cloudflare Live input. Owner only.

**Auth required:** Yes (owner only)

Returns `204 No Content` on success.

---

## Start stream

<span className="badge badge--post">POST</span> `/api/v1/streams/id/{id}/start`

Manually transition a stream from `offline` to `live`. Sets `started_at` to the current timestamp.

**Auth required:** Yes (owner only)

Returns `400` if the stream is already live or has ended. Ended streams cannot be restarted — create a new stream instead.

---

## End stream

<span className="badge badge--post">POST</span> `/api/v1/streams/{stream_id}/end`

Manually end a live stream. Sets status to `ended` and records the end time.

**Auth required:** Yes (owner only)

Returns `400` if the stream is already ended.

---

## Regenerate stream key

<span className="badge badge--post">POST</span> `/api/v1/streams/{stream_id}/regenerate-key`

Generate a new stream key and RTMPS URL. **Immediately invalidates the previous key.** Use this if your stream key is compromised.

**Auth required:** Yes (owner only)

### Response

```json
{
  "rtmps_url": "rtmps://live.cloudflare.com:443/live/",
  "stream_key": "new-stream-key-abc123"
}
```

---

## Get user streams

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/streams`

List all streams from a specific user. Public endpoint.

### Query parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `status_filter` | string | `all` | Filter: `live`, `ended`, or `all` |
| `page` | integer | `1` | Page number |
| `per_page` | integer | `20` | Results per page (max 100) |

---

## Check if user is live

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/live`

Check whether a user is currently live streaming. Returns `is_live: false` (not 404) when the user exists but is not streaming.

**Auth required:** No

### Response

```json
{
  "is_live": true,
  "stream": {
    "id": "a1b2c3d4-...",
    "title": "Morning coding session",
    "viewer_count": 89,
    "status": "live"
  }
}
```

---

## Stream search

<span className="badge badge--get">GET</span> `/api/v1/streams/search`

Full-text search across stream titles, descriptions, and streamer usernames. Uses PostgreSQL full-text search with relevance ranking. Live streams are prioritized.

**Auth required:** No

### Query parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | Yes | Search query (1–200 chars) |
| `category_id` | UUID | No | Filter by category |
| `status_filter` | string | No | `live`, `ended`, or `all` |
| `start_date` | datetime | No | Streams started after this date |
| `end_date` | datetime | No | Streams started before this date |
| `page` | integer | No | Page number (default 1) |
| `per_page` | integer | No | Results per page (max 100) |

```bash
GET /api/v1/streams/search?query=python+tutorial&status_filter=live
```

---

## Search suggestions

<span className="badge badge--get">GET</span> `/api/v1/streams/search/suggestions`

Get autocomplete suggestions from stream titles, streamer usernames, and category names.

**Auth required:** No

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `query` | string | — | Partial query (required, 1–100 chars) |
| `limit` | integer | `5` | Max suggestions (max 20) |

---

## Popular searches

<span className="badge badge--get">GET</span> `/api/v1/streams/search/popular`

Get popular search terms based on current live streams with high viewer counts and popular categories.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | integer | `10` | Max results (max 50) |

---

## Trending searches

<span className="badge badge--get">GET</span> `/api/v1/streams/search/trending`

Get trending search terms based on recently started streams.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `hours` | integer | `24` | Time window in hours (max 168) |
| `limit` | integer | `10` | Max results (max 50) |

---

## Trending streams

<span className="badge badge--get">GET</span> `/api/v1/streams/trending`

Get trending live streams with a computed trending score (0–100). The algorithm weights viewer count (40%), viewer growth rate in the last 30 minutes (30%), stream freshness (20%), and chat engagement (10%).

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `time_window` | integer | `1` | Hours to consider (1–24) |
| `limit` | integer | `10` | Max results (max 50) |
| `category_id` | UUID | — | Optional category filter |

### Response

```json
{
  "streams": [
    {
      "stream": { "id": "...", "title": "...", "viewer_count": 5200, "status": "live" },
      "trending_score": 87.4
    }
  ],
  "total": 10,
  "limit": 10,
  "time_window_hours": 1
}
```

---

## Rising streamers

<span className="badge badge--get">GET</span> `/api/v1/streams/rising`

Get rising and new streamers with high viewer growth rates. Streamers are currently live, typically with fewer total streaming hours, and are growing quickly.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | integer | `20` | Max results (max 100) |
| `category_id` | UUID | — | Optional category filter |

---

## Streamer profiles

### Get public profile

<span className="badge badge--get">GET</span> `/api/v1/streams/streamers/{username}/profile`

Get a streamer's public profile including bio, social links, follower count, partner status, and current live status.

**Auth required:** No

### Get own profile

<span className="badge badge--get">GET</span> `/api/v1/streams/me/profile`

Get the authenticated user's own streamer profile (includes private fields).

**Auth required:** Yes

### Update own profile

<span className="badge badge--patch">PATCH</span> `/api/v1/streams/me/profile`

Update your streamer profile.

**Auth required:** Yes

| Field | Type | Description |
|-------|------|-------------|
| `bio` | string | Channel bio/description |
| `stream_title` | string | Default stream title |
| `stream_category_id` | UUID | Default stream category |
| `social_links` | object | Social media links |
| `avatar_url` | string | Avatar image URL |
| `banner_url` | string | Channel banner URL |

---

## Media uploads

### Upload avatar

<span className="badge badge--post">POST</span> `/api/v1/streams/me/profile/avatar`

Upload an avatar image. Must be JPEG or PNG, max 2 MB. Automatically resized to 256×256 pixels.

**Auth required:** Yes  
**Content-Type:** `multipart/form-data`

Returns `{"avatar_url": "https://..."}`.

### Delete avatar

<span className="badge badge--delete">DELETE</span> `/api/v1/streams/me/profile/avatar`

Remove the avatar image. Returns `204 No Content`.

### Upload banner

<span className="badge badge--post">POST</span> `/api/v1/streams/me/profile/banner`

Upload a channel banner. Must be JPEG or PNG, max 5 MB. Automatically resized to 1920×480 pixels.

**Auth required:** Yes  
**Content-Type:** `multipart/form-data`

Returns `{"banner_url": "https://..."}`.

### Delete banner

<span className="badge badge--delete">DELETE</span> `/api/v1/streams/me/profile/banner`

Remove the channel banner. Returns `204 No Content`.

---

## Moderator management

### Add moderator or VIP

<span className="badge badge--post">POST</span> `/api/v1/streams/{stream_id}/moderators`

Grant a user a moderator or VIP badge. Broadcaster only.

**Auth required:** Yes (broadcaster only)

```json
{
  "user_id": "...",
  "badge_type": "moderator"
}
```

`badge_type` must be `"moderator"` or `"vip"`.

### Remove moderator or VIP

<span className="badge badge--delete">DELETE</span> `/api/v1/streams/{stream_id}/moderators/{user_id}`

Remove a moderator or VIP badge. Broadcaster only. Returns `204 No Content`.

### List moderators

<span className="badge badge--get">GET</span> `/api/v1/streams/{stream_id}/moderators`

Get all moderators and VIPs for a stream.

| Parameter | Type | Description |
|-----------|------|-------------|
| `badge_type` | string | Filter: `moderator` or `vip` |

### Get user badges

<span className="badge badge--get">GET</span> `/api/v1/streams/{stream_id}/users/{user_id}/badges`

Get all badges a specific user holds in a stream context. Possible badges: `broadcaster`, `moderator`, `vip`, `subscriber`, `verified`.

---

## Follow system

### Follow a user

<span className="badge badge--post">POST</span> `/api/v1/streams/users/{username}/follow`

Follow a streamer. Notifications are enabled by default.

**Auth required:** Yes

```json
{
  "notifications_enabled": true
}
```

Returns `400` if already following or trying to follow yourself.

### Unfollow a user

<span className="badge badge--delete">DELETE</span> `/api/v1/streams/users/{username}/follow`

Stop following a user. Returns `204 No Content`.

**Auth required:** Yes

### Check follow status

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/follow`

Check whether the authenticated user is following the specified user.

**Auth required:** Yes

```json
{
  "following": true,
  "notifications_enabled": true
}
```

### Get followers

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/followers`

List users following the specified user. Paginated.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | `1` | Page number |
| `per_page` | integer | `20` | Results per page (max 100) |

### Get following

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/following`

List users that the specified user follows. Paginated.

---

## Follow notifications

### Get notifications

<span className="badge badge--get">GET</span> `/api/v1/streams/notifications/follows`

Get paginated follow notifications for the authenticated user. Returns most recent first.

**Auth required:** Yes

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | `1` | Page number |
| `per_page` | integer | `20` | Items per page (max 100) |

### Get unread count

<span className="badge badge--get">GET</span> `/api/v1/streams/notifications/follows/unread-count`

Get the count of unread follow notifications. Useful for displaying notification badges.

**Auth required:** Yes

```json
{ "unread_count": 3 }
```

### Mark notification as read

<span className="badge badge--post">POST</span> `/api/v1/streams/notifications/follows/{notification_id}/read`

Mark a specific notification as read. Returns `204 No Content`.

**Auth required:** Yes (notification owner only)

---

## Stream schedule

Streamers can publish a recurring weekly schedule so followers know when to expect them.

### Create schedule entry

<span className="badge badge--post">POST</span> `/api/v1/streams/me/schedule`

**Auth required:** Yes

```json
{
  "day_of_week": 1,
  "start_time": "18:00",
  "end_time": "20:00",
  "timezone": "America/New_York",
  "title": "Evening coding sessions"
}
```

`day_of_week`: 0 = Monday, 6 = Sunday.

### Get own schedule

<span className="badge badge--get">GET</span> `/api/v1/streams/me/schedule`

Get your full weekly schedule grouped by day. **Auth required:** Yes

### Get user's public schedule

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/schedule`

Get any user's public stream schedule. **Auth required:** No

### Update schedule entry

<span className="badge badge--patch">PATCH</span> `/api/v1/streams/me/schedule/{schedule_id}`

**Auth required:** Yes (owner only)

### Delete schedule entry

<span className="badge badge--delete">DELETE</span> `/api/v1/streams/me/schedule/{schedule_id}`

**Auth required:** Yes (owner only). Returns `204 No Content`.

### Get upcoming streams

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/upcoming`

Get upcoming scheduled stream instances for a user based on their recurring schedule.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `days` | integer | `7` | Days to look ahead (1–30) |

---

## User profile (basic)

<span className="badge badge--get">GET</span> `/api/v1/streams/users/{username}/profile`

Get a user's basic profile — follower/following counts, total views, and current live status.

**Auth required:** No

```json
{
  "id": "...",
  "username": "alice",
  "display_name": "Alice Chen",
  "bio": "Full-stack dev streaming daily",
  "avatar_url": "https://...",
  "follower_count": 1523,
  "following_count": 87,
  "is_live": true,
  "current_stream": {
    "id": "...",
    "title": "Morning standup coding",
    "viewer_count": 142,
    "status": "live"
  },
  "total_views": 98432,
  "created_at": "2025-01-15T09:00:00Z"
}
```
