> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.beatapi.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.beatapi.io/_mcp/server.

# 실시간 API

POST https://api.beatapi.io/v1/realtime/sessions
Content-Type: application/json

수명이 짧은 실시간 API 브라우저 세션을 만들고 BeatAPI 실시간 SDK용 클라이언트 시크릿을 받습니다.

신뢰할 수 있는 서버에서 세션을 만들고, 브라우저에는 `client_secret`만 전달하고, `@beatapi/realtime`으로 연결한 뒤, 사용이 끝나면 세션을 종료하세요. 보안과 수명 주기 체크리스트는 [빠른 시작](/quick-guide)을 참고하세요.

Reference: https://docs.beatapi.io/api-reference/realtime-api

## Authentication

- `Authorization` header (bearer token, required) — API 키를 `Authorization: Bearer <BEATAPI_API_KEY>`로 보내세요.

## Servers

- `https://api.beatapi.io` (Direct, default)
- `https://api.beatapi.io` (API)

## Request

### Headers

- `Idempotency-Key` (string, required)

### Body (application/json)

This endpoint expects an object.

- `max_duration_seconds` (enum, required) — 필수인 라이브 세션 최대 길이(초)입니다. 선택한 15, 60 또는 300초 등급의 USD 금액이 보류됩니다.
  - Allowed values: `15`, `60`, `300`
- `allowed_origins` (list of string, required) — 수명이 짧은 세션 시크릿을 사용할 수 있는 정확한 브라우저 출처입니다.
- `metadata` (map from string to string, optional) — 자체 상관관계 추적을 위한 선택적 서버 정의 문자열 메타데이터입니다. 키는 최대 20개, 키는 최대 64자, 값은 최대 256자입니다.

## Response

### 201

실시간 세션 생성됨

- `data` (RealtimeSessionCreated, required) — 1회용 단기 브라우저 자격 증명을 포함해 생성된 Realtime 세션입니다.

## Errors

### 400 Bad Request Error

JSON 형식이 잘못되었거나, 이 엔드포인트가 받지 않는 필드·값·조합이 있거나(`bad_request`, 메시지에 해당 필드가 표시됨), 입력이 콘텐츠 검수에서 거부되었습니다(`content_policy_violation`). 그대로 재시도할 수 없습니다.

- `error` (ErrorError, required) — 구조화된 BeatAPI 오류입니다. 프로그램 로직에는 `code`(또는 `retryable`)를 사용하고, 지원 요청용으로 `request_id`를 보관하세요.

### 401 Unauthorized Error

API 키가 없거나(`missing_api_key`), 키가 잘못되었거나 만료, 비활성화, 소진되었습니다(`invalid_api_key`, 메시지에 원인이 표시됨). `Authorization: Bearer <key>` 형식으로 보내세요. 키는 `sk-` 접두사가 있어도 없어도 동작합니다. 재시도할 수 없습니다.

- `error` (ErrorError, required) — 구조화된 BeatAPI 오류입니다. 프로그램 로직에는 `code`(또는 `retryable`)를 사용하고, 지원 요청용으로 `request_id`를 보관하세요.

### 402 Payment Required Error

잔액이 부족합니다(`insufficient_credits`). 계정 잔액이 0이거나 이 요청의 가격보다 적습니다. 충전한 뒤 다시 보내세요. 충전 전에는 재시도할 수 없습니다.

- `error` (ErrorError, required) — 구조화된 BeatAPI 오류입니다. 프로그램 로직에는 `code`(또는 `retryable`)를 사용하고, 지원 요청용으로 `request_id`를 보관하세요.

### 409 Conflict Error

`Idempotency-Key`가 24시간 안에 다른 요청 본문이나 경로에 이미 사용되었거나, 이 키를 사용한 첫 요청이 아직 처리 중입니다(`idempotency_conflict`). 그대로 재시도할 수 없습니다.

- `error` (ErrorError, required) — 구조화된 BeatAPI 오류입니다. 프로그램 로직에는 `code`(또는 `retryable`)를 사용하고, 지원 요청용으로 `request_id`를 보관하세요.

### 429 Too Many Requests Error

요청이 너무 많습니다(`rate_limit_exceeded`, 무료 모델 한도 포함). `Retry-After`(또는 `error.retry_after_seconds`)에 표시된 초만큼 기다린 뒤 재시도할 수 있습니다.

- `error` (ErrorError, required) — 구조화된 BeatAPI 오류입니다. 프로그램 로직에는 `code`(또는 `retryable`)를 사용하고, 지원 요청용으로 `request_id`를 보관하세요.

### 503 Service Unavailable Error

이 배포 환경에서 Realtime Video가 활성화되어 있지 않거나(`realtime_disabled`, 재시도 불가. 직접 조치할 방법이 없으니 지원팀에 문의) 용량을 일시적으로 사용할 수 없습니다(`realtime_capacity_unavailable`, 재시도 가능. 잠시 후 백오프를 두고 재시도하며 대기 시간은 표시되지 않음).

- `error` (ErrorError, required) — 구조화된 BeatAPI 오류입니다. 프로그램 로직에는 `code`(또는 `retryable`)를 사용하고, 지원 요청용으로 `request_id`를 보관하세요.

## Types

### RealtimeSessionCreated

- `id` (string, required) — 세션을 조회하거나 종료할 때 쓰는 안정적인 Realtime 세션 ID입니다.
- `object` (enum, required) — 객체 구분자이며 항상 `realtime.session`입니다.
  - Allowed values: `realtime.session`
- `status` (enum, required) — active는 원격 출력이 시작된 뒤 BeatAPI가 첫 과금 하트비트를 수락했음을 뜻합니다.
  - Allowed values: `ready`, `connecting`, `active`, `closed`, `failed`, `expired`
- `expires_at` (datetime, required) — 연결되지 않은 단기 세션 자격 증명이 만료되는 시간입니다.
- `max_duration_seconds` (enum, required) — 선택한 최대 실시간 길이와 과금 등급(초)입니다.
  - Allowed values: `15`, `60`, `300`
- `allowed_origins` (list of string, required) — 이 세션을 사용할 수 있도록 승인된 정확한 브라우저 origin입니다.
- `credits` (RealtimeSessionCreatedCredits, required) — 이 Realtime 세션의 USD 보류, 정산, 환불 수명 주기입니다. 호환성을 위해 필드 이름을 유지합니다.
- `request_id` (string, required) — 로그와 BeatAPI 지원 요청용으로 보관할 상관관계 ID입니다.
- `created_at` (datetime, required) — 세션이 생성된 시간입니다.
- `connected_at` (datetime, required, nullable) — BeatAPI가 첫 과금 하트비트를 수락한 시간이며, 과금 활성화 전에는 null입니다.
- `closed_at` (datetime, required, nullable) — 세션이 종료된 시간이며, 열려 있는 동안에는 null입니다.
- `client_secret` (string, required) — POST에서만 반환되는 단기 BeatAPI 브라우저 자격 증명입니다. sk_ API 키를 브라우저에 절대 노출하지 마세요.

### ErrorError

구조화된 BeatAPI 오류입니다. 프로그램 로직에는 `code`(또는 `retryable`)를 사용하고, 지원 요청용으로 `request_id`를 보관하세요.

- `code` (enum, required) — 기계가 읽을 수 있는 안정적인 오류 코드입니다. `unauthorized`는 401의 레거시 이름입니다. 2026-09-29부터 401은 `missing_api_key` 또는 `invalid_api_key`이지만 `unauthorized`가 여전히 나타날 수 있으며, `invalid_api_key`처럼 처리하세요. `result_expired`는 `operation: "result"`가 있는 `POST /v1/capabilities/run`에서만 반환됩니다.
  - Allowed values: `bad_request`, `missing_api_key`, `invalid_api_key`, `unauthorized`, `forbidden`, `not_found`, `insufficient_credits`, `idempotency_conflict`, `rate_limit_exceeded`, `content_policy_violation`, `processing_unavailable`, `processing_failed`, `processing_timeout`, `result_transfer_failed`, `result_expired`, `not_implemented`, `realtime_disabled`, `realtime_capacity_unavailable`, `realtime_session_expired`, `origin_not_allowed`, `invalid_client_secret`, `transport_not_allowed`, `internal_error`
- `message` (string, required) — 원인을 설명하는 영어 문장입니다. `bad_request`에서는 필드 이름을 명시합니다. 파싱용이 아니라 사람과 로그를 위한 문장입니다.
- `request_id` (string, required) — BeatAPI 지원 요청용으로 보관할 상관관계 ID입니다.
- `retryable` (boolean, optional) — 같은 호출을 다시 하면 성공할 수 있는지 여부입니다. 공개 표의 모든 코드에 있으며, 표에 없는 코드에서만 빠질 수 있습니다. rate_limit_exceeded, internal_error, processing_unavailable, processing_timeout, processing_failed, result_transfer_failed, realtime_capacity_unavailable은 true입니다. 요청, 키 또는 잔액을 먼저 바꿔야 하는 경우(bad_request, content_policy_violation, missing_api_key, invalid_api_key, unauthorized, insufficient_credits, forbidden, not_found, idempotency_conflict, result_expired, not_implemented, realtime_disabled 및 기타 realtime 자격 증명 코드)는 false입니다.
- `retry_after_seconds` (integer, optional) — 재시도 전에 기다릴 초 수입니다. 429 `rate_limit_exceeded`에서만 전송되며, 같은 값이 `Retry-After` 헤더로도 전송됩니다. 다른 코드에는 없습니다.
- `docs` (string, optional) — 오류 대처 방법을 알려 주는 공개 Skill 페이지입니다. 기능 엔드포인트(`/v1/capabilities/search`, `/v1/capabilities/inspect`, `/v1/capabilities/run`)가 응답한 오류에만 있으며, 다른 엔드포인트에는 없습니다.

### RealtimeSessionCreatedCredits

이 Realtime 세션의 USD 보류, 정산, 환불 수명 주기입니다. 호환성을 위해 필드 이름을 유지합니다.

- `reserved` (double, required) — 세션 생성 시 보류된 USD 금액입니다.
- `settled` (double, required) — 첫 과금 하트비트가 접수된 뒤 정산된 USD 금액입니다.
- `refunded` (double, required) — 과금이 활성화되지 않고 세션이 끝나면 환불되는 USD 금액입니다.

## Examples

**Request**

```json
{
  "max_duration_seconds": 60,
  "allowed_origins": [
    "https://app.example.com"
  ],
  "metadata": {
    "customer_id": "cus_123"
  }
}
```

**Response**

```json
{
  "data": {
    "id": "rts_8K2qA",
    "object": "realtime.session",
    "status": "ready",
    "expires_at": "2026-08-12T10:01:00.000Z",
    "max_duration_seconds": 60,
    "allowed_origins": [
      "https://app.example.com"
    ],
    "credits": {
      "reserved": 1.2,
      "settled": 0,
      "refunded": 0
    },
    "request_id": "req_abc123",
    "created_at": "2026-08-12T10:00:00.000Z",
    "connected_at": null,
    "closed_at": null,
    "client_secret": "brt_live_example_short_lived_secret"
  }
}
```

**SDK Code**

```python 실시간 API
import requests

url = "https://api.beatapi.io/v1/realtime/sessions"

payload = {
    "max_duration_seconds": 60,
    "allowed_origins": ["https://app.example.com"],
    "metadata": { "customer_id": "cus_123" }
}
headers = {
    "Idempotency-Key": "rts-create-cus_123-01",
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript 실시간 API
const url = 'https://api.beatapi.io/v1/realtime/sessions';
const options = {
  method: 'POST',
  headers: {
    'Idempotency-Key': 'rts-create-cus_123-01',
    Authorization: 'Bearer <token>',
    'Content-Type': 'application/json'
  },
  body: '{"max_duration_seconds":60,"allowed_origins":["https://app.example.com"],"metadata":{"customer_id":"cus_123"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go 실시간 API
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.beatapi.io/v1/realtime/sessions"

	payload := strings.NewReader("{\n  \"max_duration_seconds\": 60,\n  \"allowed_origins\": [\n    \"https://app.example.com\"\n  ],\n  \"metadata\": {\n    \"customer_id\": \"cus_123\"\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Idempotency-Key", "rts-create-cus_123-01")
	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby 실시간 API
require 'uri'
require 'net/http'

url = URI("https://api.beatapi.io/v1/realtime/sessions")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = 'rts-create-cus_123-01'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"max_duration_seconds\": 60,\n  \"allowed_origins\": [\n    \"https://app.example.com\"\n  ],\n  \"metadata\": {\n    \"customer_id\": \"cus_123\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

```java 실시간 API
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.beatapi.io/v1/realtime/sessions")
  .header("Idempotency-Key", "rts-create-cus_123-01")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"max_duration_seconds\": 60,\n  \"allowed_origins\": [\n    \"https://app.example.com\"\n  ],\n  \"metadata\": {\n    \"customer_id\": \"cus_123\"\n  }\n}")
  .asString();
```

```php 실시간 API
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.beatapi.io/v1/realtime/sessions', [
  'body' => '{
  "max_duration_seconds": 60,
  "allowed_origins": [
    "https://app.example.com"
  ],
  "metadata": {
    "customer_id": "cus_123"
  }
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
    'Idempotency-Key' => 'rts-create-cus_123-01',
  ],
]);

echo $response->getBody();
```

```csharp 실시간 API
using RestSharp;

var client = new RestClient("https://api.beatapi.io/v1/realtime/sessions");
var request = new RestRequest(Method.POST);
request.AddHeader("Idempotency-Key", "rts-create-cus_123-01");
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"max_duration_seconds\": 60,\n  \"allowed_origins\": [\n    \"https://app.example.com\"\n  ],\n  \"metadata\": {\n    \"customer_id\": \"cus_123\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift 실시간 API
import Foundation

let headers = [
  "Idempotency-Key": "rts-create-cus_123-01",
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "max_duration_seconds": 60,
  "allowed_origins": ["https://app.example.com"],
  "metadata": ["customer_id": "cus_123"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.beatapi.io/v1/realtime/sessions")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```