문서API 레퍼런스
API 레퍼런스
모든 /v1 엔드포인트의 파라미터와 호출 예제를 확인하세요. OpenAPI 명세도 JSON 또는 YAML로 다운로드할 수 있어요.
API 소개
https://api.transcript1.com/v1API를 호출할 때는 https://api.transcript1.com/v1을 기본 주소로 사용하세요.
- 요청·응답 본문은 JSON이며, 필드 이름은
snake_case예요. - 날짜와 시각은
2026-10-13T09:30:00.000Z처럼 UTC 기준 ISO 8601 형식으로 표시해요. - 재생 시간과 스크립트의 타임스탬프는 초 단위예요.
- 오류 응답은 모두 같은 형식이에요. 필드와 코드별 의미는 오류 처리에서 확인하세요.
- 인증을 거친 요청의 응답에는 항상
X-RateLimit-Limit,X-RateLimit-Remaining헤더가 있어요. 자세한 기준은 요금제와 사용 한도를 확인하세요. source=auto에서는 YouTube, X, TikTok, Bluesky, Dailymotion, LinkedIn의 영상 원본 언어 자막을 먼저 확인해요. 사용할 자막이 없으면 음성 텍스트 변환(STT)을 실행해요.source=captions인captions(자막만) 모드는 오디오를 다운로드하거나 STT로 전환하지 않아요. 사용할 트랙이 없으면no_captions로 실패해요.source=stt를 지정하거나 화자 분리를 켜면 처음부터 자막을 건너뛰어요. 플랫폼별 자막 지원 범위와 세그먼트·단어별 타임스탬프의 차이는 지원 링크와 변환 옵션에 정리돼 있어요.
도구에서 사용할 OpenAPI 명세는 아래 형식으로 다운로드할 수 있어요.
인증
curl https://api.transcript1.com/v1/account \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"API 키나 OAuth 토큰을 Authorization 헤더에 넣으세요. 형식은 Authorization: Bearer <token>이에요.
- API 키는
t1_로 시작하며 모든 엔드포인트를 호출할 수 있어요. 대시보드의 “API keys” 페이지에서 발급받으세요. - OAuth 액세스 토큰은
t1o_로 시작해요. Claude나 ChatGPT 같은 MCP 클라이언트를 연결하면 발급돼요. 권한 범위는transcriptions:read,transcriptions:write로 나눠요.
| 권한 범위 | 호출할 수 있는 엔드포인트 |
|---|---|
transcriptions:read | GET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload, GET /v1/account |
transcriptions:write | POST /v1/transcriptions, DELETE /v1/transcriptions/:id, POST /v1/transcriptions/:id/upload/complete |
- 토큰에 호출에 필요한 권한이 없으면
403 insufficient_scope를 반환해요. - 키나 토큰이 없거나 유효하지 않으면
401 unauthorized를 반환해요.
청크를 보내는 PUT /v1/transcriptions/:id/upload/chunks/:index는 X-Upload-Token으로 인증해요. 업로드 예약 시 받은 토큰을 사용하세요. 자세한 흐름은 파일 업로드에 있어요.
멱등성
curl -X POST https://api.transcript1.com/v1/transcriptions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
-H "Idempotency-Key: 5f1c2b0e-8d4a-4c3e-9b7a-2e6f1d0c9a8b" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'POST /v1/transcriptions를 다시 보내도 작업이 중복되지 않게 하려면 Idempotency-Key 헤더를 사용하세요.
키는 1~255자로 지정하세요. 쿼리 파라미터에 넣지 말고 요청 헤더로 보내야 해요.
- 같은 키에 같은 파라미터를 보내면 기존 작업을 반환해요. 추가로 크레딧을 차감하지 않아요.
- 이미 사용한 키에 다른 파라미터를 보내면
409 idempotency_conflict를 반환해요. - 청크 업로드 생성 요청을 같은 키로 다시 보내면 새 업로드 토큰을 받을 수 있어요.
오류
{
"error": {
"code": "insufficient_minutes",
"message": "Not enough minutes",
"retryable": false,
"action_url": "https://transcript1.com/dashboard/billing",
"minutes_needed": 20,
"minutes_remaining": 3
}
}- 요청 오류가 나면 API가 호출을 받아들이지 않은 거예요.
- 작업 실패는 요청을 접수한 뒤 변환 도중에 생긴 오류예요. 작업의
status가failed로 바뀌어요.
오류 응답 읽기
/v1 API의 요청 오류는 error 객체로 반환해요. 이 객체에는 code, message, retryable이 있어요.
- 오류를 분기할 때는
code를 사용하세요. message는 사용자에게 보여 줄 설명이에요. 문구가 바뀔 수 있으니 분기 조건으로 쓰지 마세요.retryable이true이면 잠시 후 같은 요청을 다시 시도할 수 있어요.
오류 종류에 따라 retryable 외에 다음 필드를 함께 반환해요.
| 필드 | 포함되는 오류 | 설명 |
|---|---|---|
action_url | insufficient_minutes | 요금제를 구매하거나 분량을 충전하는 페이지 주소 |
minutes_needed, minutes_remaining | insufficient_minutes | 요청에 필요한 분량과 남은 분량. 필요한 분량을 아직 모르면 null이에요. |
supported_platforms | unsupported_platform | 지원 플랫폼 목록: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"] |
missing_chunks | upload_incomplete | 아직 받지 못한 청크의 인덱스 목록 |
max_bytes | media_too_large | 현재 허용되는 최대 파일 크기. 바이트 단위예요. |
params | invalid_request | 본문·쿼리 검증에서 문제가 된 필드와 이유를 담은 객체 |
요청 단계에서 발생하는 오류
| HTTP 상태 | code | 그대로 재시도 | 원인과 대응 |
|---|---|---|---|
| 400, 422 | invalid_request | 불가 | 필수 파라미터나 본문 필드가 빠졌거나 값이 잘못됐어요. diarize와 source: "captions", 또는 reuse_recent와 webhook_url을 함께 보낸 경우도 해당해요. 본문이 올바른 JSON인지도 확인하세요. |
| 422 | invalid_url | 불가 | YouTube, X, TikTok, Instagram, Facebook, Bluesky 또는 지원 목록에 있는 플랫폼의 영상 링크인지 확인하세요. 지정한 platform과 링크가 달라도 이 오류가 나요. |
| 422 | unsupported_platform | 불가 | 지정한 platform을 지원하지 않아요. |
| 422 | unsupported_media | 불가 | 파일 확장자가 지원 목록에 없어요. |
| 401 | unauthorized | 불가 | 인증 정보가 없거나 잘못됐거나 만료·폐기됐어요. 업로드 토큰이 잘못됐거나 만료된 경우도 해당해요. |
| 403 | insufficient_scope | 불가 | OAuth 토큰에 필요한 transcriptions:read 또는 transcriptions:write 권한이 없어요. |
| 403 | email_not_verified | 불가 | 변환 작업을 시작하려면 먼저 이메일 인증을 마쳐야 해요. |
| 402 | insufficient_minutes | 불가 | 사용 가능한 분량이 0이거나 이번 변환에 필요한 분량보다 적어요. |
| 402 | no_active_plan | 불가 | 이용 중인 유료 요금제 없이 충전을 시도했어요. 변환 API가 아니라 대시보드 결제 엔드포인트에서 반환하는 오류예요. |
| 404 | not_found | 불가 | 해당 작업이 없거나 다른 팀에 속해 있어요. |
| 409 | upload_incomplete | 불가 | 누락된 청크를 보낸 뒤 업로드 완료 요청을 다시 보내세요. |
| 409 | invalid_state | 불가 | 업로드 대기 상태가 아니에요. 더 이상 청크를 전송하거나 업로드를 완료할 수 없어요. |
| 409 | idempotency_conflict | 불가 | 이미 사용한 Idempotency-Key에 다른 파라미터를 보냈어요. |
| 413 | media_too_large | 불가 | 파일이 허용 크기를 넘었어요. 파일 업로드에서 한도를 확인하세요. |
| 413 | invalid_request | 불가 | JSON 본문이 1MB를 넘었어요. 파일 크기 초과는 media_too_large로 구분해요. |
| 429 | rate_limited | 가능 | 분당 요청 한도를 넘었어요. Retry-After에 지정된 초만큼 기다리세요. |
| 429 | too_many_active_jobs | 가능 | 진행 중인 변환 작업이나 완료 전 업로드가 한도를 넘었어요. Retry-After에 지정된 초만큼 기다리세요. |
| 500 | internal_error | 가능 | transcript1 서버에서 오류가 났어요. 잠시 후 다시 요청하세요. |
| 503 | service_unavailable | 가능 | transcript1에 일시적인 장애가 있어요. 잠시 후 다시 요청하세요. |
브라우저에서 로그인 쿠키로 인증하면 요청 출처도 검사해요. 다른 사이트에서 보낸 요청에는 403 forbidden_origin을 반환해요.
Authorization 헤더로 인증하는 API 호출에는 이 검사를 적용하지 않아요.
429 응답에는 항상 Retry-After 헤더가 있어요. 값은 다시 요청하기까지 기다릴 시간이며 초 단위예요. 실제 한도는 요금제와 사용 한도에서 확인하세요.
안전하게 재시도하기
- 같은 요청을 그대로 다시 보낼 때는
retryable이true인지 확인하세요.rate_limited,too_many_active_jobs,internal_error,service_unavailable이 해당해요. Retry-After가 있으면 지정한 시간만큼 기다리고, 없으면 재시도 간격을 늘리세요.POST /v1/transcriptions를 다시 보낼 때는 처음과 같은Idempotency-Key헤더를 사용하세요. 첫 요청이 이미 성공했더라도 중복 차감을 막을 수 있어요.- 다른 오류는 원인을 해결하고 요청을 수정한 뒤 다시 보내세요.
접수된 작업이 실패한 경우
작업의 status가 failed이면 error 객체의 code와 message로 원인을 확인하세요.
생성 요청에 응답한 뒤에도 작업이 실패할 수 있어요. 상태를 조회하거나 transcription.failed 웹훅을 받았을 때 error를 확인하세요.
error.code | 원인 | 다시 시도하려면 |
|---|---|---|
media_unavailable | 영상이 없거나 더 이상 접근할 수 없어요. | 그대로 재시도하지 말고 URL을 확인하세요. |
media_private | 비공개 영상이에요. | 공개 게시물이나 공개·일부 공개 YouTube 영상으로 바꾸세요. |
login_required | 영상을 보려면 플랫폼 로그인이 필요해요. | 자동 재시도하지 않는 영구 오류예요. |
no_audio | 영상에 오디오 트랙이 없어요. | 자동 재시도하지 않는 영구 오류예요. |
age_restricted | 연령 제한이 걸려 있어요. | 링크 대신 파일을 업로드하세요. |
live_stream | 방송 중이거나 아직 시작하지 않은 라이브예요. | 방송이 끝난 뒤 다시 시도하세요. |
no_captions | source가 captions인데 사용할 자막 트랙이 없어요. | source: "auto" 또는 "stt"로 바꾸세요. |
media_too_long | 음성 텍스트 변환(STT)의 최대 길이인 5시간을 넘었어요. | 파일을 나눠서 보내세요. |
unsupported_media | 파일을 읽지 못했거나 오디오 트랙이 없어요. | 지원 형식의 파일로 바꿔 보내세요. |
no_speech | 음성이 감지되지 않았어요. | 그대로 재시도하지 마세요. |
unsupported_language | 지원하지 않는 언어예요. | 그대로 재시도하지 말고 language를 지원하는 코드나 "auto"로 바꾸세요. |
insufficient_minutes | 차감 시점에 사용 가능한 분량이 부족해졌어요. | 분량을 충전한 뒤 다시 시도하세요. |
fetch_failed | 미디어 다운로드를 여러 번 시도했지만 실패했어요. | 잠시 후 다시 시도할 수 있어요. |
transcription_failed | 음성 텍스트 변환(STT)을 여러 번 시도했지만 실패했어요. | 잠시 후 다시 시도할 수 있어요. |
작업이 실패하거나 취소되면 비용이 들지 않아요. 이미 차감한 분량은 돌려드려요.
Transcription 객체
{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "completed",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": {
"title": "Example video",
"author": "Example channel",
"author_url": "https://www.youtube.com/@examplechannel",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"duration_seconds": 212
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": "youtube_captions",
"caption_kind": "auto_generated",
"language": "en",
"billed_minutes": 4,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": "2026-10-13T09:30:04.000Z",
"deleted_at": null,
"transcript": {
"text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
"speakers": [],
"segments": [
{
"start": 0,
"end": 4.2,
"speaker": null,
"language": "en",
"text": "Welcome back to the channel."
},
{
"start": 4.2,
"end": 9.8,
"speaker": null,
"language": "en",
"text": "Today we are looking at how transcripts are made."
}
]
}
}속성
- idstring필수
- objectstring필수
사용 가능한 값
transcription - statusstring필수
사용 가능한 값
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - sourceobject필수
하위 속성 펼치기하위 속성 접기
With
url- platformstring필수
- urlstring필수null 허용
- media_idstring필수null 허용
With
filename- platformstring필수
사용 가능한 값
upload - filenamestring필수null 허용
- sizenumber필수null 허용
- mediaobject필수null 허용
하위 속성 펼치기하위 속성 접기
- titlestring선택null 허용
- authorstring선택null 허용
- author_urlstring선택null 허용
- thumbnail_urlstring선택null 허용
- filenamestring선택
- mime_typestring선택
- sizenumber선택
- duration_secondsnumber선택null 허용
- optionsobject필수
하위 속성 펼치기하위 속성 접기
- sourcestring필수
사용 가능한 값
autocaptionsstt - languagestring필수
- diarizeboolean필수
- transcript_sourcestring필수null 허용
사용 가능한 값
youtube_captionsx_captionsspeech_to_text - caption_kindstring필수null 허용
사용 가능한 값
manualauto_generated - languagestring필수null 허용
- billed_minutesinteger필수null 허용
- cachedboolean필수
- errorobject필수null 허용
하위 속성 펼치기하위 속성 접기
- codestring필수
- messagestring필수null 허용
- created_atstring · date-time필수null 허용
- completed_atstring · date-time필수null 허용
- deleted_atstring · date-time필수null 허용
When the transcription was deleted. Always
nullunless the request includesinclude_deleted=true. - transcriptobject선택
Included in
GETresponses for completed transcriptions.하위 속성 펼치기하위 속성 접기
- textstring필수
- speakersarray of strings필수
- segmentsarray of objects필수
하위 속성 펼치기하위 속성 접기
- startnumber필수
- endnumber필수
- speakerstring필수null 허용
- languagestring필수null 허용
- textstring필수
- wordsarray of objects선택null 허용
하위 속성 펼치기하위 속성 접기
- startnumber필수
- endnumber필수
- textstring필수
- confidencenumber선택null 허용
- speakerstring필수null 허용
Transcriptions
Create, read, list and delete transcriptions of video links and uploaded files.
Create a transcription
POST /v1/transcriptionsTranscribes an uploaded file or a YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat link. Social posts must be public; only the first video is used.
Twitch accepts clips and completed VODs; Pinterest accepts native video pins; LinkedIn accepts public video posts; Snapchat accepts Spotlight. New platforms require public recorded native media without supplied account cookies; no live streams or external embeds.
platform is optional and detected from the link. Captions are YouTube/X-only. All sources use the same per-minute price.
Returns 200 for a final result, 201 when awaiting an upload and 202 while processing. Pass wait to wait up to 60 seconds for a final status.
Pass reuse_recent=true with a platform URL to reuse a transcription of the same video and options from the last 24 hours. The response includes reused: true, with no additional charge. Short links may not be matched. An Idempotency-Key replay takes precedence.
curl -X POST https://api.transcript1.com/v1/transcriptions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "queued",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": null,
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": null,
"caption_kind": null,
"language": null,
"billed_minutes": null,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": null,
"deleted_at": null,
"reused": false
}쿼리 파라미터
- waitinteger선택null 허용기본값: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- reuse_recentstring선택기본값: false
For platform URLs only. Cannot be combined with
webhook_url. Returns the team's most recent transcription of the same video with the samesource,languageanddiarize. It must have been created in the last 24 hours and must not befailedorcanceled. Reuses that transcription without creating or charging for a new one.사용 가능한 값
truefalse
요청 헤더
- Idempotency-Keystring선택
Returns the original transcription when you repeat a request with the same key and parameters.
요청 본문
With url
- platformstring선택
Optional; detected from the link. If supplied, must match the link.
사용 가능한 값
youtubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat - urlstring필수
YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat video link. Social posts must be public; only the first video is used. New platforms support public recorded native video without supplied account cookies, not live streams or external embeds.
- sourcestring선택
"auto" uses YouTube or X captions when available, otherwise speech-to-text. "captions" is YouTube/X-only. "stt" always uses speech-to-text.
사용 가능한 값
autocaptionsstt - languagestring선택
- diarizeboolean선택
- webhook_urlstring · uri선택
With upload
- uploadobject필수
하위 속성 펼치기하위 속성 접기
- filenamestring필수
- sizeinteger필수
- mime_typestring선택
- sourcestring선택
사용 가능한 값
autostt - languagestring선택
- diarizeboolean선택
- webhook_urlstring · uri선택
multipart/form-data
- filestring · binary필수
- sourcestring선택
사용 가능한 값
autostt - languagestring선택
- diarizeboolean선택
- webhook_urlstring · uri선택
반환값
200 OK— The transcription reached a final status201 Created— The transcription is awaiting a chunked upload.uploaddescribes where to send each chunk.202 Accepted— The transcription is processing
다른 상태 코드
400 Bad Request— The JSON request body is malformed401 Unauthorized— Missing or invalid credential402 Payment Required— Not enough minutes. Applies to every request body format.403 Forbidden— Insufficient scope, unverified email, cross-site request or a single-member team plan409 Conflict—Idempotency-Keywas reused with different parameters.413 Content Too Large— The upload or the JSON request body is too large422 Unprocessable Content— Invalid request, URL, platform or file type429 Too Many Requests— Too many requests or too many active transcriptions. Wait for the number of seconds inRetry-After.
Get a transcription
GET /v1/transcriptions/{id}Returns the transcription object. Completed transcriptions include the transcript. Use format to render it as text, SRT or WebVTT.
If you request a rendered format before completion, the response is JSON. It returns 202 while processing or 200 if the transcription failed or was canceled.
With include_deleted=true, a deleted transcription is returned as a record without a transcript. For deleted records, wait is ignored and rendered formats return 404.
curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "completed",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": {
"title": "Example video",
"author": "Example channel",
"author_url": "https://www.youtube.com/@examplechannel",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"duration_seconds": 212
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": "youtube_captions",
"caption_kind": "auto_generated",
"language": "en",
"billed_minutes": 4,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": "2026-10-13T09:30:04.000Z",
"deleted_at": null,
"transcript": {
"text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
"speakers": [],
"segments": [
{
"start": 0,
"end": 4.2,
"speaker": null,
"language": "en",
"text": "Welcome back to the channel."
},
{
"start": 4.2,
"end": 9.8,
"speaker": null,
"language": "en",
"text": "Today we are looking at how transcripts are made."
}
]
}
}경로 파라미터
- idstring필수
쿼리 파라미터
- waitinteger선택null 허용기본값: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- formatstring선택기본값: json
사용 가능한 값
jsontextsrtvtt - includestring선택
사용 가능한 값
words - timestampsstring선택기본값: false
사용 가능한 값
truefalse - include_deletedstring선택기본값: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.사용 가능한 값
truefalse
반환값
200 OK— The transcription, or the rendered transcript202 Accepted— A rendered format was requested while the transcription is still processing
다른 상태 코드
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— Transcription not found422 Unprocessable Content— Invalid query parameters429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
List transcriptions
GET /v1/transcriptionsLists your team's transcriptions, newest first, without transcripts. Pass next_cursor as cursor to get the next page.
Deleted transcriptions are hidden unless you pass include_deleted=true.
curl https://api.transcript1.com/v1/transcriptions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"object": "list",
"data": [
{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "completed",
"source": {
"platform": "youtube",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"media_id": "dQw4w9WgXcQ"
},
"media": {
"title": "Example video",
"author": "Example channel",
"author_url": "https://www.youtube.com/@examplechannel",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"duration_seconds": 212
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": "youtube_captions",
"caption_kind": "auto_generated",
"language": "en",
"billed_minutes": 4,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": "2026-10-13T09:30:04.000Z",
"deleted_at": null
}
],
"next_cursor": "eyJjcmVhdGVkQXQiOiIyMDI2LTEwLTEzVDA5OjMwOjAwLjAwMFoiLCJpZCI6IjViMGU2YzFjLTBkNmYtNGMxZi05YjVlLThmM2Y0ZDBhN2ExMSJ9"
}쿼리 파라미터
- limitinteger선택기본값: 20
- cursorstring선택
- statusstring선택
사용 가능한 값
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled - platformstring선택
- include_deletedstring선택기본값: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.사용 가능한 값
truefalse
반환값
200 OK— A page of transcriptions
다른 상태 코드
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan422 Unprocessable Content— Invalid query parameters or cursor429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Cancel or delete a transcription
DELETE /v1/transcriptions/{id}Cancels an active transcription or one awaiting an upload. Any charged minutes are refunded.
Deletes a completed, failed or canceled transcription together with its stored transcript.
curl -X DELETE https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"응답 본문 없음
경로 파라미터
- idstring필수
반환값
204 No Content— Canceled or deleted
다른 상태 코드
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope, cross-site request or a single-member team plan404 Not Found— Transcription not found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Uploads
Send a large file in chunks. Start with Create a transcription using an upload body. Send each chunk, then complete the upload.
Upload one chunk
PUT /v1/transcriptions/{id}/upload/chunks/{index}Sends one chunk of a chunked upload as application/octet-stream.
Authenticate with the X-Upload-Token from the create response. This endpoint does not use a bearer credential.
The body must match the planned chunk size exactly. Sending a chunk again replaces it.
curl -X PUT https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/chunks/0 \
-H "X-Upload-Token: t1u_Zk3vYb8Nw1ZrT5xLpD0aHcE7sUj9GfMiO2yXtVnR4Bw" \
-H "Content-Type: application/octet-stream" \
--data-binary @chunk-0.bin{
"object": "upload_chunk",
"index": 0,
"size": 838860
}경로 파라미터
- idstring필수
- indexinteger선택null 허용
Zero-based chunk number from the upload plan
요청 헤더
- X-Upload-Tokenstring필수
요청 본문
- (원본 본문)string · binary필수
반환값
200 OK— The chunk was stored
다른 상태 코드
401 Unauthorized— Missing, invalid or expired upload token404 Not Found— Transcription not found409 Conflict— The transcription is no longer awaiting an upload422 Unprocessable Content— Index outside the chunk plan or wrong body size
Get upload progress
GET /v1/transcriptions/{id}/uploadLists the received chunks of a chunked upload so an interrupted upload can resume.
curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"object": "upload",
"status": "awaiting_upload",
"total_bytes": 48210334,
"uploaded_bytes": 4194300,
"received_chunks": [
0,
1,
2,
3,
4
],
"chunks": [
{
"index": 0,
"offset": 0,
"size": 838860
},
{
"index": 1,
"offset": 838860,
"size": 838860
},
{
"index": 2,
"offset": 1677720,
"size": 838860
},
{
"index": 3,
"offset": 2516580,
"size": 838860
},
{
"index": 4,
"offset": 3355440,
"size": 838860
},
{
"index": 5,
"offset": 4194300,
"size": 8388608
},
{
"index": 6,
"offset": 12582908,
"size": 8388608
},
{
"index": 7,
"offset": 20971516,
"size": 8388608
},
{
"index": 8,
"offset": 29360124,
"size": 8388608
},
{
"index": 9,
"offset": 37748732,
"size": 8388608
},
{
"index": 10,
"offset": 46137340,
"size": 2072994
}
],
"expires_at": "2026-10-13T10:30:00.000Z"
}경로 파라미터
- idstring필수
반환값
200 OK— Upload progress
다른 상태 코드
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— Transcription or upload not found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Complete a chunked upload
POST /v1/transcriptions/{id}/upload/completeChecks that every planned chunk arrived with the planned size. Then assembles the file and queues the transcription.
curl -X POST https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/complete \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"object": "transcription",
"status": "queued",
"source": {
"platform": "upload",
"filename": "interview.m4a",
"size": 48210334
},
"media": {
"filename": "interview.m4a",
"mime_type": "audio/mp4",
"size": 48210334
},
"options": {
"source": "auto",
"language": "auto",
"diarize": false
},
"transcript_source": null,
"caption_kind": null,
"language": null,
"billed_minutes": null,
"cached": false,
"error": null,
"created_at": "2026-10-13T09:30:00.000Z",
"completed_at": null,
"deleted_at": null
}경로 파라미터
- idstring필수
반환값
202 Accepted— The transcription is queued
다른 상태 코드
401 Unauthorized— Missing or invalid credential402 Payment Required— Not enough minutes to queue the transcription. The upload stays awaiting completion.403 Forbidden— Insufficient scope, cross-site request or a single-member team plan404 Not Found— Transcription or upload not found409 Conflict—upload_incompletemeans chunks are missing.missing_chunkslists them.invalid_statemeans the transcription is not awaiting an upload.429 Too Many Requests— Too many requests or too many active transcriptions. Wait for the number of seconds inRetry-After.
Account
Your plan, remaining minutes and rate limits.
Get your account
GET /v1/accountReturns the authenticated user, current plan and remaining transcription minutes.
curl https://api.transcript1.com/v1/account \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"object": "account",
"user": {
"id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
"email": "[email protected]"
},
"plan": {
"id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
"name": "Monthly"
},
"minutes": {
"remaining": 1180,
"cycle": 980,
"topup": 200,
"topup_usable": true,
"resets_at": "2026-11-13T09:30:00.000Z"
},
"renews_at": "2026-11-13T09:30:00.000Z",
"limits": {
"requests_per_minute": 200,
"max_active_jobs": 10
}
}반환값
200 OK— The authenticated user's account
다른 상태 코드
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— No account found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Account 객체
{
"object": "account",
"user": {
"id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
"email": "[email protected]"
},
"plan": {
"id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
"name": "Monthly"
},
"minutes": {
"remaining": 1180,
"cycle": 980,
"topup": 200,
"topup_usable": true,
"resets_at": "2026-11-13T09:30:00.000Z"
},
"renews_at": "2026-11-13T09:30:00.000Z",
"limits": {
"requests_per_minute": 200,
"max_active_jobs": 10
}
}속성
- objectstring필수
사용 가능한 값
account - userobject필수
하위 속성 펼치기하위 속성 접기
- idstring필수
- emailstring필수
- planobject필수null 허용
하위 속성 펼치기하위 속성 접기
- idstring필수
- namestring필수
- minutesobject필수
하위 속성 펼치기하위 속성 접기
- remainingnumber필수
- cyclenumber필수
- topupnumber필수
- topup_usableboolean필수
- resets_atstring · date-time필수null 허용
- renews_atstring · date-time필수null 허용
- limitsobject필수
하위 속성 펼치기하위 속성 접기
- requests_per_minutenumber필수
- max_active_jobsnumber필수
웹훅
import { Buffer } from 'node:buffer';
import crypto from 'node:crypto';
// header: X-Transcript1-Signature, rawBody: the request body exactly as received,
// secret: your full signing secret including the whsec_ prefix.
export const verifySignature = (secret, header, rawBody, nowSeconds = Math.floor(Date.now() / 1000)) => {
const fields = Object.fromEntries(header.split(',').map(part => part.trim().split('=')));
const t = Number(fields.t);
if (!Number.isInteger(t) || !fields.v1 || Math.abs(nowSeconds - t) > 300) {
return false;
}
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return fields.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(fields.v1), Buffer.from(expected));
};{
"type": "transcription.completed",
"created_at": "2026-10-13T09:30:04.000Z",
"data": {
"id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
"object": "transcription",
"status": "completed",
"transcript_source": "youtube_captions",
"billed_minutes": 4,
"error": null
}
}완료 알림을 받으려면 작업 생성 시 webhook_url을 지정하세요. 변환이 완료되거나 실패하면 transcript1이 해당 주소로 서명된 POST 요청을 보내요.
웹훅은 REST API 전용 기능이에요. MCP 서버에서는 웹훅 파라미터를 지원하지 않아요.
알림을 받을 URL 지정하기
POST /v1/transcriptions를 호출할 때 다음 위치에 webhook_url을 넣으세요.
- 영상 링크를 보내거나 청크 업로드를 예약할 때는 JSON 본문에 넣으세요.
- 파일을 요청 한 번으로 업로드할 때는 폼 필드로 보내세요.
웹훅 URL에는 다음 조건이 적용돼요.
- HTTPS URL이어야 해요.
- URL 안에 사용자 이름이나 비밀번호를 넣으면 안 돼요.
- 도메인을 조회하면 공개 IP 주소로 연결돼야 해요.
사설, 루프백, 링크 로컬 등 비공개 주소를 가리키면 422 invalid_request를 반환해요. 작업 생성 시 확인하고, 웹훅을 전송할 때마다 다시 검사해요.
reuse_recent=true를 사용할 때는 webhook_url을 지정할 수 없어요.
알림이 오는 시점
type | 발생 조건 |
|---|---|
transcription.completed | 작업이 completed 상태로 끝났을 때 |
transcription.failed | 작업이 failed 상태로 끝났을 때. 원인은 data.error의 code, message에서 확인해요. |
작업을 취소한 경우에는 웹훅을 보내지 않아요.
웹훅에 담기는 데이터
웹훅 본문은 JSON이며 다음 정보를 포함해요.
created_at: 이벤트가 발생한 시각이에요.data: 변환 작업 객체예요.transcript본문은 제외해요.
스크립트는 data에 있는 id로 GET /v1/transcriptions/:id를 호출해 가져오세요.
함께 보내는 요청 헤더예요.
Content-Type: application/jsonUser-Agent: transcript1-webhooks/1.0- 서명 검증에 쓰는
X-Transcript1-Signature
요청 서명 검증하기
웹훅을 보낼 때마다 X-Transcript1-Signature 헤더를 넣어요. 값의 형식은 t=<unix seconds>,v1=<hex digest>예요.
t: 요청에 서명한 시각을 Unix 시간의 초 단위로 나타내요.v1:"<t>.<raw body>"를 HMAC-SHA256으로 서명한 16진수 값이에요.t값 뒤에 점을 붙이고, 그 뒤에 받은 요청 본문을 그대로 이어 붙여 계산해요. 키에는whsec_접두어까지 포함한 서명 시크릿 전체를 사용하세요.
서명은 JSON을 파싱하기 전에 받은 본문 바이트 그대로 검증하세요. 파싱 후 다시 JSON으로 만들면 공백이나 키 순서가 달라져 검증에 실패할 수 있어요.
- 서명값은 상수 시간으로 비교하세요.
t가 현재 시각에서 너무 벗어난 요청은 거부하세요. 예제에서는 5분 이내만 허용해요.- Express에서는 JSON 파서보다 먼저 원문 텍스트를 읽도록 설정하세요.
- Flask를 쓴다면
request.get_data(as_text=True)로 읽은 값을raw_body에 넘기세요.
서명 시크릿 확인과 교체
팀마다 whsec_로 시작하는 서명 시크릿이 있어요. 해당 팀에서 만든 모든 변환 작업의 웹훅에 같은 시크릿을 사용해요.
“Webhooks” 페이지에서 시크릿을 확인하거나 새로 발급받을 수 있어요.
교체는 즉시 적용돼요. 이후 전송하는 웹훅부터 새 시크릿으로 서명해요.
전송과 재시도
- 서버가
2xx로 응답하면 전송 성공으로 처리해요. - 다른 상태 코드, 연결 오류, 10초 내 응답 없음은 전송 실패로 처리해요.
- 실패하면 대기 시간을 늘려 다시 보내요. 첫 전송을 포함해 총 6번까지 시도하며, 각 시도 사이는 2, 4, 8, 16, 32분이에요. 마지막 시도는 첫 전송에서 약 한 시간 뒤예요.
- 리다이렉트는 따라가지 않아요.
3xx도 전송 실패이므로 최종 도착 URL을 직접 지정하세요. - 서버가 제한 시간 이후에 응답하는 경우처럼 같은 알림을 여러 번 받을 수 있어요.
data.id와type을 함께 확인해 중복 처리를 막으세요.