메인 콘텐츠로 건너뛰기
이 가이드는 X API v2 media upload 엔드포인트를 사용해 미디어를 업로드하는 첫 요청을 보내는 과정을 안내합니다. 이 가이드에서는 분할 업로드용 POST /2/media/upload 엔드포인트로 동영상을 업로드합니다. 이는 단일 이미지 업로드와는 다른 워크플로가 필요합니다. 동영상 또는 분할 업로드의 경우 다음을 수행해야 합니다.
  1. INIT 명령으로 업로드를 초기화합니다.
  2. APPEND 명령으로 각 바이트 청크를 업로드합니다.
  3. FINALIZE 명령으로 업로드를 완료합니다.
참고: Python 예시는 이 샘플 코드를 참조하세요.

인증

이 가이드의 예제를 실행하려면 OAuth 2 인증을 사용해야 합니다. CONSUMER_KEY, CONSUMER_SECRET, ACCESS_KEY, ACCESS_TOKEN개발자 포털의 프로젝트 내 앱에서 확인할 수 있습니다.

1단계: POST media/upload (INIT)

INIT 명령 요청은 파일 업로드 세션을 시작하는 데 사용됩니다. 이 요청은 이후 모든 요청에 사용해야 하는 media_id를 반환합니다. INIT 명령이 성공적으로 처리된 후 다음 단계는 APPEND 명령입니다. 미디어 파일의 제한 사항과 요구 사항은 모범 사례를 참조하세요. 요청 예
curl --location 'https://api.x.com/2/media/upload' \
    --header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>' \
    --header 'Content-Type: multipart/form-data' \
    --form 'command=INIT' \
    --form 'media_type=video/mp4' \
    --form 'total_bytes=1024' \
    --form 'media_category=amplify_video'
참고: 원시 HTTP 요청을 보낼 때는 요청을 multipart/form-data 또는 application/x-www-form-urlencoded 형식의 POST로 보내야 합니다.
응답 예시
{
    "data":
    {
        "id":"1880028106020515840",
        "media_key":"13_1880028106020515840",
        "expires_after_secs":1295999
    }
}
응답에는 미디어 식별자인 id(string)와 media_key(string)가 포함됩니다. 전체 파일은 expires_after_secs초 내에 업로드해야 합니다.

단계 2 : POST media/upload (APPEND)

APPEND 명령은 미디어 파일을 청크(연속 바이트 범위) 단위로 업로드할 때 사용합니다. 예를 들어, 3 MB 파일을 1 MB 크기의 청크 3개로 나눈 뒤, APPEND 명령 요청 3회로 업로드할 수 있습니다. 전체 파일 업로드가 완료되면, 다음 단계로 FINALIZE 명령을 호출합니다.
미디어 파일을 작은 청크로 업로드하면 다음과 같은 이점이 있습니다:
  • 낮은 대역폭 네트워크 환경에서도 신뢰성과 성공률 향상
  • 업로드 일시 중지 및 재개 가능
  • 각 청크 단위로 개별 재시도 가능
  • 셀룰러 클라이언트 등 변화하는 네트워크 상황에 맞춰 청크 크기 조정 가능
요청 예시
curl --location 'https://api.x.com/2/media/upload' \
    --header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>' \
    --header 'Content-Type: multipart/form-data' \
    --form 'command=APPEND' \
    --form 'media_id=1880028106020515840' \
    --form 'segment_index=0' \
    --form 'media=@/path/to/your/media/file.mp4'
segment_index는 파일 청크의 순서 인덱스입니다. 0부터 999까지의 값(포함)이어야 합니다. 첫 번째 세그먼트의 인덱스는 0, 두 번째는 1이며 그 이후도 동일합니다. 모든 청크가 업로드될 때까지 파일 청크를 계속 업로드하세요.
참고: 원시 HTTP 요청을 보낼 때는 요청을 multipart/form-data POST 형식으로 해야 합니다.
참고: 업로드가 성공하면 본문이 비어 있는 HTTP 2XX가 반환됩니다.

3단계: POST media/upload (FINALIZE)

APPEND 명령으로 전체 미디어 파일을 업로드한 뒤 FINALIZE 명령을 호출해야 합니다. FINALIZE 명령의 응답에 processing_info 필드가 포함된 경우에만 STATUS 명령을 사용해 성공이 반환될 때까지 대기한 후 게시물 생성으로 진행해야 할 수도 있습니다. 요청 예시
curl --location --request POST 'https://api.x.com/2/media/upload' \
    --header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>' \
    --header 'Content-Type: multipart/form-data' \
    --form 'command=FINALIZE' \
    --form 'media_id=1880028106020515840'
참고: 원시 HTTP 요청을 보낼 때는 multipart/form-data 또는 application/x-www-form-urlencoded 형식의 POST로 전송해야 합니다.
응답 예시
{
    "data": {
        "id": "1880028106020515840",
        "media_key": "13_1880028106020515840",
        "size": 1024,
        "expires_after_secs": 86400,
        "processing_info": {
            "state": "pending",
            "check_after_secs": 1
        }
    }
}
응답에는 media_id(64비트 정수)와 media_id_string(문자열) 필드로 미디어 식별자가 포함됩니다. 긴 정수를 정확히 표현할 수 없는 JavaScript 등 일부 언어에서는 API 응답에 포함된 media_id_string을 사용하세요. 반환된 media_idexpires_after_secs초 동안만 유효합니다. 이 시간이 지나 다른 API 호출에서 mediaId를 사용하면 잘못된 요청(HTTP 4xx) 응답이 반환됩니다. 응답에 processing_info 필드가 포함되어 있다면, FINALIZE 작업의 상태를 확인하기 위해 STATUS 명령으로 폴링하세요. 비동기 최종화(async finalize) 방식은 미디어 처리에 더 많은 시간이 필요한 경우에 사용됩니다. 앞으로 모든 동영상과 애니메이티드 GIF 처리는 비동기 최종화 방식만 지원될 예정입니다. 이 동작은 업로드 세션을 media_category 매개변수로 initialized했으며 미디어 유형이 동영상 또는 애니메이티드 GIF인 경우에 활성화됩니다.
참고: 응답에 processing_info 필드가 반환되지 않았다면 media_id는 다른 API 엔드포인트에서 사용할 준비가 된 것입니다.

4단계: GET media/upload (STATUS)

STATUS 명령은 미디어 처리 작업의 진행 상태를 주기적으로 조회하는 데 사용됩니다. STATUS 명령의 응답이 succeeded로 반환되면, 일반적으로 media_id를 사용해 게시물을 생성하는 다음 단계로 진행할 수 있습니다.
참고: FINALIZE 명령 또는 이전 STATUS 명령의 응답에 processing_info 필드가 포함된 경우에만 STATUS 명령을 사용해야 합니다.

요청 예시

curl --location --request GET 'https://api.x.com/2/media/upload?command=STATUS&media_id=1880028106020515840' \
    --header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>'

예시 응답

{
    "data":{
        "id":"1880028106020515840",
        "media_key":"13_1880028106020515840",
        "processing_info":{
            "state":"uploading" // 상태 전환 흐름: pending -> in_progress -> [failed|succeeded]
        }
    }
}
응답 본문에는 현재 미디어 처리 작업 상태를 알려주는 processing_info 필드가 포함됩니다. 여기에는 state 필드가 있으며, 상태 전이는 pending -> in_progress -> [failed | succeeded] 순으로 진행됩니다. statesucceeded로 설정되기 전에는 media_id를 사용해 게시물 또는 다른 엔터티를 생성할 수 없습니다.

단계 5 : 미디어가 포함된 게시물 게시

인증된 사용자를 대신해 media_id를 포함하여 POST /2/tweets 엔드포인트로 게시물을 생성합니다. 요청 예시
curl --location 'https://api.x.com/2/tweets' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <YOUR_ACCESS_TOKEN>'
--data '{
    "text": "미디어와 함께 게시물을 만들었습니다!",
     "media": {
       "media_ids": [
            "1880028106020515840"
        ]
    }
}'

예시 답변
{
    "data": {
        "edit_history_tweet_ids": [
            "1880028106020515840"
        ],
        "id": "1880028106020515840",
        "text": "미디어가 포함된 게시물을 올렸어요! https://t.co/DqNyXX"
    }
}

문제 해결

Media API 관련 문제가 있다면 개발자 포럼의 Media API 카테고리에서 해결 방법을 찾아보세요.