> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.voicecheap.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 번역 상태 가져오기

> 번역 프로젝트의 상태를 확인하고 결과를 검색합니다

# 번역 상태 가져오기

번역 프로젝트의 현재 상태를 검색합니다. 이 엔드포인트를 사용하여 진행 상황을 폴링하고 처리가 완료되면 번역된 비디오 URL을 가져옵니다.

## 요청

### 헤더

<ParamField header="x-api-key" type="string" required>
  VoiceCheap API 키입니다. [app.voicecheap.ai/page-api](https://app.voicecheap.ai/page-api)에서 키를 발급받으세요.
</ParamField>

### 경로 매개변수

<ParamField path="projectId" type="string" required>
  [Start Translation](/docs/ko/api-reference/translate) 엔드포인트에서 반환된 번역 프로젝트의 고유 식별자입니다.
</ParamField>

## 응답

응답 구조는 번역 상태에 따라 다릅니다.

### 공통 필드

<ResponseField name="projectId" type="string" required>
  프로젝트의 고유 식별자
</ResponseField>

<ResponseField name="projectName" type="string" required>
  프로젝트 이름
</ResponseField>

<ResponseField name="originalVideoUrl" type="string" required>
  원본 업로드 비디오/오디오 파일의 URL
</ResponseField>

<ResponseField name="originalLanguage" type="string" required>
  감지되거나 지정된 원본 언어
</ResponseField>

<ResponseField name="targetLanguage" type="string" required>
  번역 대상 언어
</ResponseField>

<ResponseField name="duration" type="number" required>
  콘텐츠 지속 시간(초)
</ResponseField>

<ResponseField name="createdAt" type="number" required>
  프로젝트가 생성된 Unix 타임스탬프
</ResponseField>

<ResponseField name="workflow" type="string" required>
  프로젝트를 생성한 API 워크플로우: `translation` 또는 `transcription`.
</ResponseField>

<ResponseField name="transcriptionStatus" type="string" required>
  소스 전사 상태: `processing`, `success` 또는 `failed`. 이 신호는 더빙 완료 여부와 독립적입니다.
</ResponseField>

<ResponseField name="transcripts" type="object" required>
  사용 가능한 정규화된 전사본입니다.

  <Expandable title="transcripts 속성">
    <ResponseField name="transcripts.original" type="object | null">
      타임스탬프와 화자를 포함하여 사용 가능한 경우의 원본 언어 전사본입니다.
    </ResponseField>

    <ResponseField name="transcripts.translations" type="array">
      언어 및 작업 식별자와 함께 사용 가능한 대상 언어 전사본입니다.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="workId" type="string">
  현재 보고된 번역 버전에 대한 번역 작업 식별자입니다.
</ResponseField>

<ResponseField name="translatedVersionId" type="string">
  현재 보고된 번역 버전에 대한 번역 버전 식별자입니다.
</ResponseField>

<ResponseField name="actualProgressStep" type="string">
  현재 프로젝트 단계입니다. 생성 또는 전사 중에는 활성 생성 단계를 반영합니다.
  예시로는 `downloading_content`, `content_validation` 및 `transcription_processing`가 있습니다.
  더빙이 시작되면 `smart_sync` 또는 `audio_assembling`과 같은 활성 더빙 단계로 전환될 수 있습니다.
  `status: processing`은(는) `actualProgressStep: done`과(와) 페어링되지 않습니다.
</ResponseField>

<ResponseField name="translationAndTranscriptionProgress" type="number">
  프로젝트 생성/전사에 대한 대략적인 진행률(0-100)
</ResponseField>

<ResponseField name="dubbingStep" type="string">
  대상 언어에 대한 현재 더빙 단계(예: `smart_sync`, `audio_enhancement`, `video_upload`)
</ResponseField>

<ResponseField name="dubbingProgress" type="number">
  더빙에 대한 대략적인 진행률(0-100)
</ResponseField>

<ResponseField name="status" type="string" required>
  현재 번역 상태: `processing`, `success` 또는 `failed`
</ResponseField>

### Lip Sync 필드

립싱크가 요청된 경우, 응답에는 추가적인 `lipSync` 객체가 포함됩니다:

<ResponseField name="lipSync" type="object">
  립싱크 상태 및 출력 세부 정보(립싱크가 요청된 경우에만 표시됨)

  <Expandable title="lipSync 속성">
    <ResponseField name="lipSync.jobId" type="string">
      립싱크 작업 식별자
    </ResponseField>

    <ResponseField name="lipSync.status" type="string">
      립싱크 상태: `PENDING`, `PROCESSING`, `COMPLETED`, `FAILED`, `REJECTED` 또는 `CANCELED`
    </ResponseField>

    <ResponseField name="lipSync.videoUrl" type="string">
      립싱크된 비디오의 URL입니다. `lipSync.status`이(가) `COMPLETED`일 때 최종 립싱크 출력에 이 URL을 사용하세요.
    </ResponseField>

    <ResponseField name="lipSync.errorMessage" type="string">
      립싱크 실패 시 오류 세부 정보
    </ResponseField>

    <ResponseField name="lipSync.type" type="string">
      립싱크 모드: `standard`, `pro` 또는 `studio`.
    </ResponseField>

    <ResponseField name="lipSync.createdAt" type="string">
      립싱크 시도가 생성된 ISO 타임스탬프입니다.
    </ResponseField>

    <ResponseField name="lipSync.requestedAt" type="string">
      사용 가능한 경우, 립싱크 시도가 요청된 ISO 타임스탬프입니다.
    </ResponseField>

    <ResponseField name="lipSync.completedAt" type="string">
      사용 가능한 경우, 립싱크가 완료된 ISO 타임스탬프입니다.
    </ResponseField>

    <ResponseField name="lipSync.failedAt" type="string">
      사용 가능한 경우, 립싱크가 실패한 ISO 타임스탬프입니다.
    </ResponseField>

    <ResponseField name="lipSync.timedOutAt" type="string">
      립싱크 시간이 초과된 경우의 ISO 타임스탬프입니다.
    </ResponseField>

    <ResponseField name="lipSync.activeSpeakerDetectionEnabled" type="boolean">
      이 립싱크 시도에 활성 화자 감지 기능이 활성화되었는지 여부입니다.
    </ResponseField>
  </Expandable>
</ResponseField>

<Info>
  립싱크가 요청되면, `lipSync.status`이 `COMPLETED`될 때까지 번역은 `processing` 상태로 유지됩니다. 립싱크가 실패하면 상태는
  `failed`이 되고 `error.code`은 `LIPSYNC_FAILED`입니다. 립싱크가 성공하면 `lipSync.videoUrl`에서 립싱크된 에셋을 읽어오십시오. `translatedVideoUrl`
  는 립싱크 오버레이 이전의 번역된 비디오 출력입니다.
</Info>

<Tip>전체 번역 버전 기록이나 전체 립싱크 기록이 필요할 때 `GET /v1/projects/{projectId}`을 사용하십시오.</Tip>

### 성공 응답 필드

번역 워크플로우의 경우, `status`이 `success`일 때 다음 필드가 포함됩니다. 전사 전용 워크플로우는 번역된 미디어 필드 없이 `success`에 도달합니다.

<ResponseField name="translatedVideoUrl" type="string">
  번역된 비디오 파일을 다운로드하기 위한 URL
</ResponseField>

<ResponseField name="translatedAudioUrl" type="string">
  번역된 오디오 파일을 별도로 다운로드하기 위한 URL
</ResponseField>

### 실패 응답 필드

`status`이 `failed`일 때, 다음 추가 필드가 포함됩니다:

<ResponseField name="error" type="object">
  오류 세부 정보

  <Expandable title="오류 속성">
    <ResponseField name="error.code" type="string">
      기계 판독 가능한 오류 코드
    </ResponseField>

    <ResponseField name="error.message" type="string">
      사람이 읽을 수 있는 오류 설명
    </ResponseField>
  </Expandable>
</ResponseField>

## 예시

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.voicecheap.ai/v1/translate/abc123-def456-ghi789/status" \
    -H "x-api-key: vc_your-api-key"
  ```

  ```typescript TypeScript theme={null}
  type TranslationStatus = 'processing' | 'success' | 'failed';

  interface Transcript {
    source: 'original' | 'translated';
    language: string;
    text: string;
    segments: Array<{
      index: number;
      text: string;
      begin: number;
      end: number;
      speaker: number;
    }>;
  }

  interface TranslationStatusBase {
    projectId: string;
    projectName: string;
    originalVideoUrl: string;
    originalLanguage: string;
    targetLanguage: string;
    duration: number;
    createdAt: number;
    workflow: 'translation' | 'transcription';
    transcriptionStatus: TranslationStatus;
    transcripts: {
      original: Transcript | null;
      translations: Transcript[];
    };
    workId?: string;
    translatedVersionId?: string;
    actualProgressStep?: string;
    translationAndTranscriptionProgress?: number;
    dubbingStep?: string;
    dubbingProgress?: number;
    lipSync?: {
      jobId: string;
      status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED' | 'REJECTED' | 'CANCELED';
      videoUrl: string;
      errorMessage: string | null;
      type: 'standard' | 'pro' | 'studio';
      createdAt?: string;
      requestedAt?: string;
      completedAt?: string;
      failedAt?: string;
      timedOutAt?: string;
      activeSpeakerDetectionEnabled?: boolean;
    };
  }

  interface TranslationStatusProcessing extends TranslationStatusBase {
    status: 'processing';
  }

  interface TranslationStatusSuccess extends TranslationStatusBase {
    status: 'success';
    translatedVideoUrl?: string;
    translatedAudioUrl?: string;
  }

  interface TranslationStatusFailed extends TranslationStatusBase {
    status: 'failed';
    error: {
      code: string;
      message: string;
    };
  }

  type TranslationStatusResponse = TranslationStatusProcessing | TranslationStatusSuccess | TranslationStatusFailed;

  async function getTranslationStatus(projectId: string): Promise<TranslationStatusResponse> {
    const response = await fetch(`https://api.voicecheap.ai/v1/translate/${projectId}/status`, {
      method: 'GET',
      headers: {
        'x-api-key': 'vc_your-api-key',
      },
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || 'Failed to get status');
    }

    return response.json();
  }

  // Polling function with typed response handling
  async function pollUntilComplete(projectId: string, intervalMs: number = 10000): Promise<TranslationStatusSuccess> {
    while (true) {
      const status = await getTranslationStatus(projectId);

      if (status.status === 'success') {
        return status;
      }

      if (status.status === 'failed') {
        throw new Error(`Translation failed: ${status.error.message}`);
      }

      console.log('Still processing...');
      await new Promise((resolve) => setTimeout(resolve, intervalMs));
    }
  }

  // Usage
  try {
    const result = await pollUntilComplete('abc123-def456-ghi789');
    console.log('Translated video:', result.translatedVideoUrl);
    console.log('Translated audio:', result.translatedAudioUrl);
  } catch (error) {
    console.error('Error:', error);
  }
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.voicecheap.ai/v1/translate/abc123-def456-ghi789/status', {
    method: 'GET',
    headers: {
      'x-api-key': 'vc_your-api-key',
    },
  });

  const status = await response.json();

  if (status.status === 'success') {
    console.log('Translated video:', status.translatedVideoUrl);
  } else if (status.status === 'processing') {
    console.log('Still processing...');
  } else if (status.status === 'failed') {
    console.error('Translation failed:', status.error.message);
  }
  ```

  ```python Python theme={null}
  import requests
  import time

  headers = {'x-api-key': 'vc_your-api-key'}
  project_id = 'abc123-def456-ghi789'

  # Poll until complete
  while True:
      response = requests.get(
          f'https://api.voicecheap.ai/v1/translate/{project_id}/status',
          headers=headers
      )
      status = response.json()

      if status['status'] == 'success':
          print(f"Translated video: {status['translatedVideoUrl']}")
          print(f"Translated audio: {status['translatedAudioUrl']}")
          break
      elif status['status'] == 'failed':
          print(f"Translation failed: {status['error']['message']}")
          break
      else:
          print("Still processing...")
          time.sleep(10)  # Wait 10 seconds before polling again
  ```

  ```php PHP theme={null}
  <?php
  $apiKey = 'vc_your-api-key';
  $projectId = 'abc123-def456-ghi789';

  $curl = curl_init();

  curl_setopt_array($curl, [
      CURLOPT_URL => "https://api.voicecheap.ai/v1/translate/{$projectId}/status",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_HTTPHEADER => [
          "x-api-key: {$apiKey}"
      ]
  ]);

  $response = curl_exec($curl);
  $status = json_decode($response, true);

  switch ($status['status']) {
      case 'success':
          echo "Translated video: " . $status['translatedVideoUrl'] . "\n";
          echo "Translated audio: " . $status['translatedAudioUrl'];
          break;
      case 'processing':
          echo "Still processing...";
          break;
      case 'failed':
          echo "Failed: " . $status['error']['message'];
          break;
  }
  ```
</CodeGroup>

## 응답 예시

### 처리 상태

```json theme={null}
{
  "projectId": "abc123-def456-ghi789",
  "projectName": "My Spanish Translation",
  "originalVideoUrl": "https://storage.voicecheap.ai/...",
  "originalLanguage": "en",
  "targetLanguage": "spanish",
  "duration": 125.5,
  "createdAt": 1702234567890,
  "workflow": "translation",
  "transcriptionStatus": "processing",
  "transcripts": { "original": null, "translations": [] },
  "actualProgressStep": "downloading_content",
  "translationAndTranscriptionProgress": 11,
  "dubbingStep": "smart_sync",
  "dubbingProgress": 30,
  "status": "processing"
}
```

### 처리 상태 (Lip Sync 요청됨)

```json theme={null}
{
  "projectId": "abc123-def456-ghi789",
  "projectName": "My Spanish Translation",
  "originalVideoUrl": "https://storage.voicecheap.ai/...",
  "originalLanguage": "en",
  "targetLanguage": "spanish",
  "duration": 125.5,
  "createdAt": 1702234567890,
  "workflow": "translation",
  "transcriptionStatus": "success",
  "transcripts": { "original": null, "translations": [] },
  "actualProgressStep": "finalizing",
  "translationAndTranscriptionProgress": 95,
  "dubbingStep": "video_upload",
  "dubbingProgress": 95,
  "status": "processing",
  "lipSync": {
    "jobId": "syncjob_123",
    "status": "PROCESSING",
    "videoUrl": "",
    "errorMessage": null,
    "type": "standard"
  }
}
```

### 성공 상태

```json theme={null}
{
  "projectId": "abc123-def456-ghi789",
  "projectName": "My Spanish Translation",
  "originalVideoUrl": "https://storage.voicecheap.ai/...",
  "originalLanguage": "en",
  "targetLanguage": "spanish",
  "duration": 125.5,
  "createdAt": 1702234567890,
  "workflow": "translation",
  "transcriptionStatus": "success",
  "transcripts": { "original": null, "translations": [] },
  "status": "success",
  "translatedVideoUrl": "https://storage.voicecheap.ai/translated/...",
  "translatedAudioUrl": "https://storage.voicecheap.ai/audio/..."
}
```

### 성공 상태 (Lip Sync 완료됨)

```json theme={null}
{
  "projectId": "abc123-def456-ghi789",
  "projectName": "My Spanish Translation",
  "originalVideoUrl": "https://storage.voicecheap.ai/...",
  "originalLanguage": "en",
  "targetLanguage": "spanish",
  "duration": 125.5,
  "createdAt": 1702234567890,
  "workflow": "translation",
  "transcriptionStatus": "success",
  "transcripts": { "original": null, "translations": [] },
  "actualProgressStep": "done",
  "dubbingStep": "done",
  "dubbingProgress": 100,
  "status": "success",
  "translatedVideoUrl": "https://storage.voicecheap.ai/translated/...",
  "translatedAudioUrl": "https://storage.voicecheap.ai/audio/...",
  "lipSync": {
    "jobId": "syncjob_123",
    "status": "COMPLETED",
    "videoUrl": "https://storage.voicecheap.ai/lipsync/...",
    "errorMessage": null,
    "type": "pro"
  }
}
```

### 실패 상태

```json theme={null}
{
  "projectId": "abc123-def456-ghi789",
  "projectName": "My Spanish Translation",
  "originalVideoUrl": "https://storage.voicecheap.ai/...",
  "originalLanguage": "en",
  "targetLanguage": "spanish",
  "duration": 125.5,
  "createdAt": 1702234567890,
  "workflow": "translation",
  "transcriptionStatus": "failed",
  "transcripts": { "original": null, "translations": [] },
  "status": "failed",
  "error": {
    "code": "TRANSCRIPTION_FAILED",
    "message": "Could not transcribe the audio. Please ensure the audio quality is sufficient."
  }
}
```

## 폴링 모범 사례

<Info>
  **권장 폴링 간격:** 10-30초

  번역 시간은 비디오 길이와 복잡도에 따라 다릅니다. 일반적인 2분짜리 비디오의 경우 2-5분의 처리 시간이 소요됩니다.
</Info>

<Warning>
  **요청 제한:** 분당 30회 요청

  요청 제한을 준수하기 위해 2초에 한 번보다 더 자주 폴링하지 마십시오.
</Warning>

## 오류

| 상태  | 코드                    | 설명                       |
| --- | --------------------- | ------------------------ |
| 401 | `INVALID_API_KEY`     | 제공된 API 키가 유효하지 않습니다     |
| 403 | `FORBIDDEN`           | 이 프로젝트에 대한 액세스 권한이 없습니다  |
| 404 | `PROJECT_NOT_FOUND`   | 지정된 프로젝트가 존재하지 않습니다      |
| 429 | `RATE_LIMIT_EXCEEDED` | 너무 많은 요청 (제한: 분당 30회 요청) |
