LiveKit docs › Other › Ingress › Ingress API

---

# Ingress API

> Use LiveKit's ingress service to import live streams from non-WebRTC sources into LiveKit rooms.

## API

The Ingress API is available within the server SDKs and the CLI:

- [Go Ingress Client](https://pkg.go.dev/github.com/livekit/server-sdk-go/v2#IngressClient)
- [JS Ingress Client](https://docs.livekit.io/reference/server-sdk-js/classes/IngressClient.html.md)
- [Ruby Ingress Client](https://github.com/livekit/server-sdk-ruby/blob/main/lib/livekit/ingress_service_client.rb)
- [Python Ingress Client](https://docs.livekit.io/reference/python/livekit/api/ingress_service.html.md)
- [Java Ingress Client](https://github.com/livekit/server-sdk-kotlin/blob/main/src/main/kotlin/io/livekit/server/IngressServiceClient.kt)
- [CLI](https://github.com/livekit/livekit-cli/blob/main/cmd/lk/ingress.go)

### CreateIngress

#### WHIP / RTMP example

To provision an ingress with the Ingress service, use the `CreateIngress` API. It returns an `IngressInfo` object that describes the created ingress, along with connection settings. These parameters can also be queried at any time using the `ListIngress` API

**LiveKit CLI**:

Create a file at `ingress.json` with the following content:

```json
{
    "input_type": 0 for RTMP, 1 for WHIP
    "name": "Name of the ingress goes here",
    "room_name": "Name of the room to connect to",
    "participant_identity": "Unique identity for the room participant the Ingress service will connect as",
    "participant_name": "Name displayed in the room for the participant",
    "enable_transcoding": true // Transcode the input stream. Can only be false for WHIP,
}

```

Then create the ingress using `lk`:

```shell
export LIVEKIT_URL=https://my-livekit-host
export LIVEKIT_API_KEY=livekit-api-key
export LIVEKIT_API_SECRET=livekit-api-secret

lk ingress create ingress.json

```

---

**JavaScript**:

```typescript
import { IngressClient, IngressInfo, IngressInput } from 'livekit-server-sdk';

const livekitHost = 'https://my-livekit-host';
const ingressClient = new IngressClient(livekitHost, 'api-key', 'secret-key');

const ingress = {
  name: 'my-ingress',
  roomName: 'my-room',
  participantIdentity: 'my-participant',
  participantName: 'My Participant',
  // Transcode the input stream. Can only be false for WHIP.
  enableTranscoding: false,
};

// Use IngressInput.WHIP_INPUT to create a WHIP endpoint
await ingressClient.createIngress(IngressInput.RTMP_INPUT, ingress);

```

---

**Go**:

```go
ctx := context.Background()
ingressClient := lksdk.NewIngressClient(
    "https://my-livekit-host",
    "livekit-api-key",
    "livekit-api-secret",
)

t := true

ingressRequest := &livekit.CreateIngressRequest{
    InputType:           livekit.IngressInput_RTMP_INPUT, // Or livekit.IngressInput_WHIP_INPUT
    Name:                "my-ingress",
    RoomName:            "my-room",
    ParticipantIdentity: "my-participant",
    ParticipantName:     "My Participant",
    // Transcode the input stream. Can only be false for WHIP.
    EnableTranscoding:   &t,
}

info, err := ingressClient.CreateIngress(ctx, ingressRequest)
ingressID := info.IngressId

```

---

**Ruby**:

```ruby
ingressClient = LiveKit::IngressServiceClient.new(url, api_key: "yourkey", api_secret: "yoursecret")
info = ingressClient.create_ingress(
  :RTMP_INPUT, # Or WHIP_INPUT
  name: "my-ingress",
  room_name: "my-room",
  participant_identity: "my-participant",
  participant_name: "My Participant",

)
puts info.ingress_id

```

#### URL Input example

With URL Input, ingress will begin immediately after `CreateIngress` is called. `URL_INPUT` ingress can't be reused.

**LiveKit CLI**:

Create a file at `ingress.json` with the following content:

```json
{
  "input_type": "URL_INPUT", // or 2
  "name": "Name of the ingress goes here",
  "room_name": "Name of the room to connect to",
  "participant_identity": "Unique identity for the room participant the Ingress service will connect as",
  "participant_name": "Name displayed in the room for the participant",
  "url": "HTTP(S) or SRT url to the file or stream"
}

```

Then create the ingress using `lk`:

```shell
export LIVEKIT_URL=https://my-livekit-host
export LIVEKIT_API_KEY=livekit-api-key
export LIVEKIT_API_SECRET=livekit-api-secret

lk ingress create ingress.json

```

---

**JavaScript**:

```typescript
import { IngressClient, IngressInfo, IngressInput } from 'livekit-server-sdk';

const livekitHost = 'https://my-livekit-host';
const ingressClient = new IngressClient(livekitHost, 'api-key', 'secret-key');

const ingress = {
  name: 'my-ingress',
  roomName: 'my-room',
  participantIdentity: 'my-participant',
  participantName: 'My Participant',
  url: 'https://domain.com/video.m3u8', // or 'srt://domain.com:7001'
};

await ingressClient.createIngress(IngressInput.URL_INPUT, ingress);

```

---

**Go**:

```go
ctx := context.Background()
ingressClient := lksdk.NewIngressClient(
    "https://my-livekit-host",
    "livekit-api-key",
    "livekit-api-secret",
)

ingressRequest := &livekit.CreateIngressRequest{
    InputType:           livekit.IngressInput_URL_INPUT,
    Name:                "my-ingress",
    RoomName:            "my-room",
    ParticipantIdentity: "my-participant",
    ParticipantName:     "My Participant",
    Url:                 "https://domain.com/video.m3u8", // or 'srt://domain.com:7001'
}

info, err := ingressClient.CreateIngress(ctx, ingressRequest)
ingressID := info.IngressId

```

---

**Ruby**:

```ruby
ingressClient = LiveKit::IngressServiceClient.new(url, api_key: "yourkey", api_secret: "yoursecret")
info = ingressClient.create_ingress(
  :URL_INPUT,
  name: "my-ingress",
  room_name: "my-room",
  participant_identity: "my-participant",
  participant_name: "My Participant",
  url: "https://domain.com/video.m3u8", # or 'srt://domain.com:7001'
)
puts info.ingress_id

```

### ListIngress

**LiveKit CLI**:

```shell
lk ingress list

```

The optional `--room` option allows to restrict the output to the Ingress associated to a given room. The `--id` option can check if a specific ingress is active.

---

**JavaScript**:

```js
await ingressClient.listIngress('my-room');

```

The `roomName` parameter can be left empty to list all Ingress.

---

**Go**:

```go
listRequest := &livekit.ListIngressRequest{
    RoomName:            "my-room",   // Optional parameter to restrict the list to only one room. Leave empty to list all Ingress.
}

infoArray, err := ingressClient.ListIngress(ctx, listRequest)

```

---

**Ruby**:

```ruby
puts ingressClient.list_ingress(
  # optional
  room_name: "my-room"
)

```

### UpdateIngress

The ingress configuration can be updated using the `UpdateIngress` API. This enables the ability to reuse the same ingress URL to publish to different rooms. Only reusable ingresses, such as RTMP or WHIP, can be updated.

**LiveKit CLI**:

Create a file at `ingress.json` with the fields to be updated.

```json
{
  "ingress_id": "Ingress ID of the ingress to update",
  "name": "Name of the ingress goes here",
  "room_name": "Name of the room to connect to",
  "participant_identity": "Unique identity for the room participant the Ingress service will connect as",
  "participant_name": "Name displayed in the room for the participant"
}

```

The only required field is `ingress_id`. Non-provided fields are left unchanged.

```shell
lk ingress update ingress.json

```

---

**JavaScript**:

```js
const update = {
  name: 'my-other-ingress',
  roomName: 'my-other-room',
  participantIdentity: 'my-other-participant',
  participantName: 'My Other Participant',
};

await ingressClient.updateIngress(ingressID, update);

```

Parameters left empty in the update object are left unchanged.

---

**Go**:

```go
updateRequest := &livekit.UpdateIngressRequest{
    IngressId:           "ingressID",        // required parameter indicating what Ingress to update
    Name:                "my-other-ingress",
    RoomName:            "my-other-room",
    ParticipantIdentity: "my-other-participant",
    ParticipantName:     "My Other Participant",
}

info, err := ingressClient.UpdateIngress(ctx, updateRequest)

```

Non specified fields are left unchanged.

---

**Ruby**:

```ruby
# only specified fields are updated, all fields are optional
puts ingressClient.update_ingress(
  "ingress-id",
  name: "ingress-name",
  room_name: "my-room",
  participant_identity: "my-participant",
  participant_name: "My Participant",
  audio: LiveKit::Proto::IngressAudioOptions.new(...),
  video: LiveKit::Proto::IngressVideoOptions.new(...),
)

```

### DeleteIngress

An ingress can be reused multiple times. When not needed anymore, it can be deleted using the `DeleteIngress` API:

**LiveKit CLI**:

```shell
lk ingress delete <INGRESS_ID>

```

---

**JavaScript**:

```js
await ingressClient.deleteIngress('ingress_id');

```

---

**Go**:

```go
deleteRequest := &livekit.DeleteIngressRequest{
    IngressId:  "ingress_id",
}

info, err := ingressClient.DeleteIngress(ctx, deleteRequest)

```

---

**Ruby**:

```ruby
puts ingressClient.delete_ingress("ingress-id")

```

## Types

The Ingress service includes the following types.

### ListIngressResponse

| Field | Type | Description |
| items | array<[IngressInfo](#ingressinfo)> | List of ingress endpoints. |
| next_page_token | TokenPagination | Token for the next page of results. |

### IngressInfo

Describes the ingress and its connection settings.

| Field | Type | Description |
| ingress_id | string | Unique ingress ID. |
| name | string | User-provided identifier for the ingress. |
| stream_key | string | Stream key for the encoder (RTMP). |
| url | string | URL for the encoder (RTMP, WHIP) or source to pull from (URL input). Format depends on input type: `rtmp://` for RTMP, `http://` for URL. |
| input_type | [IngressInput](#ingressinput) | Type of input. |
| enable_transcoding | bool | Whether transcoding is enabled. |
| audio | [IngressAudioOptions](#ingressaudiooptions) | Audio options. |
| video | [IngressVideoOptions](#ingressvideooptions) | Video options. |
| room_name | string | Room the ingress publishes to. |
| participant_identity | string | Identity of the publishing participant. |
| participant_name | string | Display name of the publishing participant. |
| participant_metadata | string | Metadata associated with the publishing participant. |
| reusable | bool | Whether the ingress can be reused (e.g. for another room). |
| state | [IngressState](#ingressstate) | Current state, including error or debug info (bitrate, resolution, bandwidth). |
| enabled | bool | When false, new connection attempts are rejected. Default is true. |

### IngressState

Describes current endpoint status, errors, and input media state.

| Field | Type | Description |
| status | [IngressState.Status](#ingressstatestatus) | Endpoint status. |
| error | string | Error or non-compliance description, if any. |
| video | [InputVideoState](#inputvideostate) | Incoming video state. |
| audio | [InputAudioState](#inputaudiostate) | Incoming audio state. |
| room_id | string | ID of the current or previous room published to. |
| started_at | int64 | When the endpoint started publishing. |
| ended_at | int64 | When the endpoint stopped publishing. |
| updated_at | int64 | Last update timestamp. |
| resource_id | string | Resource identifier. |
| tracks | array<TrackInfo> | Published track info. |

### IngressState.Status

Enum. Endpoint status values:

| Name | Value | Description |
| ENDPOINT_INACTIVE | 0 | Endpoint is inactive. |
| ENDPOINT_BUFFERING | 1 | Endpoint is buffering. |
| ENDPOINT_PUBLISHING | 2 | Endpoint is publishing. |
| ENDPOINT_ERROR | 3 | Endpoint encountered an error. |
| ENDPOINT_COMPLETE | 4 | Endpoint finished (e.g. stream ended). |

### InputVideoState

State of the incoming video input.

| Field | Type | Description |
| mime_type | string | MIME type of the video. |
| average_bitrate | uint32 | Average bitrate in bps. |
| width | uint32 | Frame width in pixels. |
| height | uint32 | Frame height in pixels. |
| framerate | double | Frame rate. |

### InputAudioState

State of the incoming audio input.

| Field | Type | Description |
| mime_type | string | MIME type of the audio. |
| average_bitrate | uint32 | Average bitrate in bps. |
| channels | uint32 | Number of audio channels. |
| sample_rate | uint32 | Sample rate in Hz. |

### IngressInput

Enum. Type of ingress input:

| Name | Value | Description |
| RTMP_INPUT | 0 | RTMP push input. |
| WHIP_INPUT | 1 | WHIP push input. |
| URL_INPUT | 2 | Pull from a URL. Only HTTP URLs supported (single media file or HLS stream). |

### IngressAudioOptions

| Field | Type | Description |
| name | string | Track name. |
| source | TrackSource | Track source. |
| encoding_options | [IngressAudioEncodingPreset](#ingressaudioencodingpreset) or [IngressAudioEncodingOptions](#ingressaudioencodingoptions) | Preset or custom encoding options. |

### IngressVideoOptions

| Field | Type | Description |
| name | string | Track name. |
| source | TrackSource | Track source. |
| encoding_options | [IngressVideoEncodingPreset](#ingressvideoencodingpreset) or [IngressVideoEncodingOptions](#ingressvideoencodingoptions) | Preset or custom encoding options. |

### IngressAudioEncodingPreset

Enum. Audio encoding presets:

| Name | Value | Description |
| OPUS_STEREO_96KBPS | 0 | OPUS, 2 channels, 96 kbps. |
| OPUS_MONO_64KBS | 1 | OPUS, 1 channel, 64 kbps. |

### IngressVideoEncodingPreset

Enum. Video encoding presets:

| Name | Value | Description |
| H264_720P_30FPS_3_LAYERS | 0 | 1280×720, 30 fps, 1900 kbps main layer, 3 layers total. |
| H264_1080P_30FPS_3_LAYERS | 1 | 1920×1080, 30 fps, 3500 kbps main layer, 3 layers total. |
| H264_540P_25FPS_2_LAYERS | 2 | 960×540, 25 fps, 1000 kbps main layer, 2 layers total. |
| H264_720P_30FPS_1_LAYER | 3 | 1280×720, 30 fps, 1900 kbps, no simulcast. |
| H264_1080P_30FPS_1_LAYER | 4 | 1920×1080, 30 fps, 3500 kbps, no simulcast. |
| H264_720P_30FPS_3_LAYERS_HIGH_MOTION | 5 | 1280×720, 30 fps, 2500 kbps main layer, 3 layers, higher bitrate for high motion. |
| H264_1080P_30FPS_3_LAYERS_HIGH_MOTION | 6 | 1920×1080, 30 fps, 4500 kbps main layer, 3 layers, higher bitrate for high motion. |
| H264_540P_25FPS_2_LAYERS_HIGH_MOTION | 7 | 960×540, 25 fps, 1300 kbps main layer, 2 layers, higher bitrate for high motion. |
| H264_720P_30FPS_1_LAYER_HIGH_MOTION | 8 | 1280×720, 30 fps, 2500 kbps, no simulcast, higher bitrate for high motion. |
| H264_1080P_30FPS_1_LAYER_HIGH_MOTION | 9 | 1920×1080, 30 fps, 4500 kbps, no simulcast, higher bitrate for high motion. |

### IngressAudioEncodingOptions

Custom audio encoding options.

| Field | Type | Description |
| audio_codec | AudioCodec | Desired audio codec to publish to the room. |
| bitrate | uint32 | Bitrate in bps. |
| disable_dtx | bool | Whether to disable DTX. |
| channels | uint32 | Number of channels. |

### IngressVideoEncodingOptions

Custom video encoding options.

| Field | Type | Description |
| video_codec | VideoCodec | Desired video codec to publish to the room. |
| frame_rate | double | Frame rate. |
| layers | array<VideoLayer> | Simulcast layers. When empty, layers are typically set to 1/2 and 1/4 of dimensions. |

---

This document was rendered at 2026-06-07T11:33:39.225Z.
For the latest version of this document, see [https://docs.livekit.io/reference/other/ingress/api.md](https://docs.livekit.io/reference/other/ingress/api.md).

To explore all LiveKit documentation, see [llms.txt](https://docs.livekit.io/llms.txt).