메인 콘텐츠로 건너뛰기

V2 Account Activity API

이제 Pro에서 Account Activity API v2를 이용할 수 있습니다!

개요

Account Activity API(AAA)는 웹훅을 통해 X 사용자 계정과 관련된 실시간 이벤트를 수신할 수 있도록 합니다. 미리 구성한 웹훅에 특정 사용자 계정을 구독시키면, 단일 연결로 보유하거나 구독 중인 하나 이상의 계정에서 발생하는 게시물, 다이렉트 메시지, 좋아요, 팔로우, 차단 등 다양한 활동에 대한 알림을 애플리케이션이 받을 수 있습니다.
이 API는 사용자 행동에 즉시 반응하거나 사용자 활동을 기반으로 최신 상태를 유지해야 하는 애플리케이션을 구축할 때 주로 사용됩니다. 웹훅 등록 시 각 사용자 구독에 대해 아래에 나열된 관련 모든 활동을 수신합니다:

활동 유형

  • 게시물 (사용자가 작성)
  • 게시물 삭제 (사용자가 수행)
  • @멘션 (사용자에 대한)
  • 답글 (사용자에게 또는 사용자로부터)
  • 리포스트 (사용자가 수행 또는 사용자에 대한)
  • 인용 게시물 (사용자가 작성 또는 사용자에 대한)
  • 인용 게시물의 리포스트 (사용자가 수행 또는 사용자에 대한)
  • 좋아요 (사용자가 누름 또는 사용자에 대한)
  • 팔로우 (사용자가 함 또는 사용자에 대한)
  • 언팔로우 (사용자가 함 또는 사용자에 대한)
  • 차단 (사용자가 함 또는 사용자에 대한)
  • 차단 해제 (사용자가 함 또는 사용자에 대한)
  • 뮤트 (사용자가 함 또는 사용자에 대한)
  • 뮤트 해제 (사용자가 함 또는 사용자에 대한)
  • 다이렉트 메시지 전송 (사용자가 보냄)
  • 다이렉트 메시지 수신 (사용자가 받음)
  • 입력 중 표시 (사용자에게)
  • 읽음 확인 (사용자에게)
  • 구독 철회 (사용자가 수행)
참고: Account Activity API에서는 홈 타임라인 데이터를 제공하지 않습니다. 이 데이터를 가져오려면 User ID 기반 사용자 게시물 타임라인 엔드포인트를 사용하세요.

기능 요약

등급가격고유 구독 수웹훅 수
Pro월 $5,00031
Enterprise영업팀에 문의5,000+5+
이 문서는 v2 Account Activity API 엔드포인트를 사용해 웹훅에 연결된 사용자 구독을 관리하는 방법에 중점을 둡니다.

구독 관리

Account Activity API는 귀하의 서비스에 구독된 X 계정과 관련된 이벤트가 발생할 때마다 웹후크 기반 JSON 메시지를 제공합니다. X는 해당 활동을 등록한 웹후크로 전달합니다. 다음 단계에서는 사용자 계정에 대한 구독을 설정하고 관리하는 방법을 안내합니다.
Account Activity API v2는 현재 User Auth 2.0을 지원하지 않습니다. 이 문제를 해결 중이며, OAuth 2.0 지원 시 문서를 업데이트하겠습니다.

1. X 앱 만들기

개발자 포털에서 승인된 개발자 계정으로 X 앱을 만듭니다. 회사 명의로 앱을 만드는 경우 회사의 X 계정을 사용하세요.
  • 앱 페이지의 권한 탭에서 “Read, Write, and Access direct messages”를 활성화합니다.
  • “Keys and Access Tokens” 탭에서 앱의 Consumer Key (API Key), Consumer Token (API Secret), Bearer Token 값을 기록해 둡니다.
  • 앱의 액세스 토큰Access Token Secret을 생성합니다. 이는 사용자 계정 구독에 필요합니다.
  • X 로그인과 사용자 컨텍스트에 익숙하지 않다면 액세스 토큰 받기를 참고하세요.
  • 개발자 포털의 “Apps” 페이지에서 앱의 숫자형 id를 기록해 둡니다. 이는 Account Activity API 액세스를 신청할 때 필요합니다.

2. Account Activity API 액세스 얻기

Account Activity API는 Enterprise 티어에서만 제공됩니다. 개발자 포털에서 Enterprise 액세스를 신청하세요.

3. 웹훅 등록

Account Activity 이벤트를 수신하려면 공개적으로 접근 가능한 HTTPS URL을 사용하는 웹훅을 등록해야 합니다. 웹훅 소비자 앱 개발, 웹훅 등록, 보안 강화, Challenge-Response Check(CRC) 처리에 대한 자세한 내용은 V2 Webhooks API 문서를 참조하세요.
  • 웹훅이 JSON으로 인코딩된 이벤트 페이로드를 포함하는 POST 요청을 처리하도록 구성되어 있는지 확인하세요.
  • 구독 관리를 위해 필요하므로 웹훅 등록 응답에서 webhook_id를 확인하세요.

4. 설정 검증

앱과 webhook이 올바르게 구성되었는지 확인하려면:
  • 사용자 계정을 webhook에 구독하세요(아래 “구독 추가” 참조).
  • 앱이 구독한 X 계정 중 하나가 올린 게시물을 좋아요하세요.
  • POST 요청을 통해 webhook URL로 favorite_events 페이로드가 전달되어야 합니다.
  • 참고: 구독을 추가한 후 이벤트가 전달되기 시작하기까지 최대 10초가 걸릴 수 있습니다.

중요 참고 사항

  • 인증: 사용자를 구독할 때 해당 사용자의 계정에 대한 consumer key, consumer secret, access token, access token secret을 사용하십시오.
  • 다이렉트 메시지: 모든 수신 및 발신 다이렉트 메시지(POST /2/dm_conversations/with/:participant_id/messages로 전송됨)는 웹훅을 통해 전달되어 앱이 모든 DM 활동을 파악할 수 있도록 합니다.
  • 이벤트 중복:
    • 두 명의 구독된 사용자가 같은 DM 대화에 있는 경우 웹훅은 중복 이벤트를 수신합니다(사용자당 1개). 이를 구분하려면 for_user_id 필드를 사용하십시오.
    • 여러 앱이 동일한 웹훅 URL과 사용자를 공유하는 경우 이벤트가 여러 번 전송됩니다(앱당 1회).
    • 앱은 간헐적으로 발생하는 중복을 처리하기 위해 event ID를 사용해 이벤트를 중복 제거해야 합니다.
  • 예제 코드: 웹훅 이벤트를 표시하는 웹 앱은 Account Activity API 설정을 참고하십시오.

구독 사용자 관리 (v2 API)

유효한 webhook_id로 등록된 웹후크가 있으면 사용자의 계정 활동을 수신하기 위한 사용자 구독을 관리할 수 있습니다. 다음 엔드포인트를 사용해 구독을 추가, 조회 또는 제거하세요. 엔드포인트: POST /2/account_activity/webhooks/:webhook_id/subscriptions/all
설명: 지정된 webhook을 통해 이벤트를 수신하도록 인증 사용자에게 구독을 추가합니다.
인증: OAuthUser(구독 대상 사용자를 나타내며, 3-legged OAuth 플로우 필요).
  • Consumer Key: 예: xvz1evFS…
  • 액세스 토큰: 예: 370773112-GmHxMAgYyLbN…
경로 매개변수:
매개변수설명
webhook_id구독을 연결할 webhook의 ID.
요청:
bash
curl --request POST --url 'https://api.twitter.com/2/account_activity/webhooks/:WEBHOOK_ID/subscriptions/all' \
--header 'authorization: OAuth oauth_consumer_key="<CONSUMER_KEY>", oauth_nonce="GENERATED", oauth_signature="GENERATED", oauth_signature_method="HMAC-SHA1", oauth_timestamp="GENERATED", oauth_token="<ACCESS_TOKEN>", oauth_version="1.0"'
응답:
  • 성공(200 OK):
  • JSON
{
  "data": {
    "subscribed": true
  }
}
  • 실패 (400 Bad Request):
이유설명
WebhookIdInvalid제공된 webhook_id를 찾을 수 없거나 앱과 연동되어 있지 않습니다.
DuplicateSubscriptionFailed해당 사용자에 대한 구독이 지정된 webhook_id에 이미 존재합니다.
SubscriptionLimitExceeded애플리케이션이 모든 webhook 전반에서 구독 한도에 도달했습니다.

엔드포인트: GET /2/account_activity/webhooks/:webhook_id/subscriptions/all
설명: 인증 중인 사용자가 지정된 웹훅에 구독되어 있는지 확인합니다.
인증: OAuthUser(3-legged OAuth 플로우 필요).
경로 매개변수:
매개변수설명
webhook_id확인할 웹훅의 id.
요청:
bash
curl --request GET --url 'https://api.twitter.com/2/account_activity/webhooks/:WEBHOOK_ID/subscriptions/all' \
--header 'authorization: OAuth oauth_consumer_key="<CONSUMER_KEY>", oauth_nonce="GENERATED", oauth_signature="GENERATED", oauth_signature_method="HMAC-SHA1", oauth_timestamp="GENERATED", oauth_token="<ACCESS_TOKEN>", oauth_version="1.0"'
응답:
  • 성공(200 OK):
  • JSON
{
  "data": {
    "subscribed": true // 또는 false
  }
}
  • 실패(400 Bad Request):
이유설명
WebhookIdInvalid제공된 webhook_id를 찾을 수 없거나 앱과 연동되어 있지 않습니다.

엔드포인트: DELETE /2/account_activity/webhooks/:webhook_id/subscriptions/:user_id/all
설명: 특정 사용자 ID의 구독을 비활성화하여 웹훅으로의 이벤트 전송을 중지합니다.
인증: OAuth2 앱 전용 베어러 토큰.
  • 베어러 토큰: 예: AAAAAAAAAAAA0%2EUifi76ZC9Ub0wn…
경로 매개변수:
매개변수설명
webhook_id구독이 포함된 웹훅의 ID입니다.
user_id구독을 해지할 사용자의 숫자 ID입니다.
요청:
bash
curl --request DELETE --url 'https://api.twitter.com/2/account_activity/webhooks/:WEBHOOK_ID/subscriptions/:USER_ID/all' \
--header 'authorization: Bearer <BEARER_TOKEN>'
응답:
  • 성공(200 OK):
  • JSON
{
  "data": {
    "subscribed": false
  }
}
  • 실패(400 Bad Request):
이유설명
SubscriptionNotFound지정된 user_id와 주어진 webhook_id에 해당하는 구독이 존재하지 않습니다.
WebhookIdInvalid제공된 webhook_id를 찾을 수 없거나 앱과 연결되어 있지 않습니다.
엔드포인트: GET /2/account_activity/webhooks/:webhook_id/subscriptions/all/list
설명: 지정된 webhook에 현재 구독된 모든 사용자 ID의 목록을 가져옵니다.
인증: OAuth2 앱 전용 베어러 토큰
경로 매개변수:
매개변수설명
webhook_id구독을 조회할 대상 webhook의 ID
요청:
bash
curl --request GET --url 'https://api.twitter.com/2/account_activity/webhooks/:WEBHOOK_ID/subscriptions/all/list' \
--header 'authorization: Bearer <BEARER_TOKEN>'
응답:
  • 성공(200 OK):
  • JSON
{
  "data": {
    "application_id": "<앱 id>",
    "webhook_id": "<webhook&#95;id>",
    "webhook_url": "<웹훅 콜백 URL>",
    "subscriptions": [
      { "user_id": "<user_id_1>" },
      { "user_id": "<user_id_2>" }
    ]
  }
}
  • 실패 (400 Bad Request):
이유설명
WebhookIdInvalid제공된 webhook_id를 찾을 수 없거나 앱과 연결되어 있지 않습니다.

엔드포인트: GET /2/account_activity/subscriptions/count
설명: 활성 구독의 총 개수와 인증 애플리케이션에 대해 프로비저닝된 한도를 반환합니다.
인증: OAuth2 앱 전용 베어러 토큰
요청:
bash
curl --request GET --url 'https://api.twitter.com/2/account_activity/subscriptions/count' \
--header 'authorization: Bearer <베어러 토큰>'
응답:
  • 성공(200 OK):
  • JSON
{
  "data": {
    "account_name": "<애플리케이션 이름>",
    "provisioned_count": "<할당된 구독 한도>",
    "subscriptions_count_all": "<현재 활성 구독 수>",
    "subscriptions_count_direct_messages": "0" // DM 전용 구독은 더 이상 지원되지 않음
  }
}
AAAv2는 지정한 시간 범위의 과거 이벤트를 조회하여 웹훅으로 다시 전달할 수 있는 재생 기능을 제공합니다. 이는 다운타임으로 놓친 이벤트를 복구하는 데 유용합니다. 엔드포인트: POST /2/account_activity/replay/webhooks/:webhook_id/subscriptions/all 설명: 재생 작업을 시작합니다 인증: OAuth2 앱 전용 베어러 토큰 경로 매개변수:
매개변수설명
webhook_id재생을 시작할 웹훅의 id
쿼리 매개변수:
매개변수설명
from_date이벤트 제공을 시작할 가장 이른 UTC 타임스탬프로, ‘yyyymmddhhmm’ 형식이어야 합니다. 타임스탬프는 분 단위 정밀도이며 포함적입니다(예: 12:00은 00분을 포함). 유효한 시간은 UTC 기준 최근 24시간 이내여야 하며, 현재 시점보다 최소 31분 이전이어야 합니다. from_date와 to_date는 약 2시간 이내 범위로 지정하는 것을 권장합니다.
to_date이벤트 제공을 종료할 가장 늦은 UTC 타임스탬프로, ‘yyyymmddhhmm’ 형식이어야 합니다. 타임스탬프는 분 단위 정밀도이며 배타적입니다(예: 12:30은 해당 시의 30분을 포함하지 않음). 유효한 시간은 UTC 기준 최근 24시간 이내여야 하며, 현재 시점보다 최대 10분 이전이어야 합니다.
응답: 성공:
200

{
  "for&#95;user&#95;id": "<USER_ID>"
  "replay_event": {
    "job_id": <REPLAY_JOB_ID>",
    "created_at: "yyyy-mm-ddThh:mm:ss.000Z"
  }
}
Failures:
ReasonDescription
QueryParamInvalidfrom_date가 현재 시각 기준으로 24시간을 초과해 과거입니다.
QueryParamInvalidfrom_date가 to_date보다 더 최근입니다.
QueryParamInvalidfrom_date가 미래 시점입니다.
QueryParamInvalidto_date가 미래 시점입니다.
QueryParamInvalidfrom_date 또는 to_date의 형식이 올바르지 않습니다.
CrcValidationFailedCRC 검증 중 webhook URL에서 잘못된 응답이 반환되었습니다.
ReplayConflictError지정된 webhook에 대한 재실행 작업이 이미 진행 중입니다.
WebhookIdInvalid제공된 webhook_id가 유효하지 않거나 앱과 연결되어 있지 않습니다.

작업 완료 메시지

재생 작업이 성공적으로 완료되면 X에서 다음과 같은 작업 완료 이벤트를 전송합니다. 이 이벤트를 수신하면 해당 작업이 종료된 것이므로 다른 작업을 제출할 수 있습니다.
{
  "replay_job_status": {
    "webhook_id": "<WEBHOOK_ID>",
    "job_state": "완료됨",
    "job_state_description": "작업이 성공적으로 완료되었습니다.",
    "job_id": "<JOB_ID>"
  }
}
작업이 성공적으로 완료되지 않으면 Replay Job을 다시 시도하라는 다음 메시지를 반환합니다. 이 이벤트를 수신하면 해당 작업의 실행이 종료된 것이므로, 새 작업을 제출할 수 있습니다.
{
  "replay_job_status": {
    "webhook_id": "<WEBHOOK_ID>",
    "job_state": "미완료",
    "job_state_description": "작업이 모든 이벤트를 전달하지 못했습니다. 재생 작업을 다시 시도하세요.",
    "job_id": "<JOB_ID>"
  }
}

계정 활동 데이터 객체 구조

객체상세
for_user_id이벤트와 관련된 사용자 구독을 식별합니다.
is_blocked_by(조건부) 언급한 사용자가 구독된 사용자에게 차단된 경우에만 게시물 언급 이벤트에서 표시됩니다.
source활동을 수행하는 사용자(예: 팔로우, 차단, 음소거를 하는 사용자).
target활동의 대상 사용자(예: 팔로우되거나 차단되거나 음소거되는 사용자).

사용 가능한 활동

메시지 유형세부 정보
tweet_create_events게시물, 리포스트, 답글, @멘션, 인용 게시물 또는 인용 게시물의 리포스트에 대한 게시 상태.
favorite_events사용자와 대상이 포함된 좋아요 이벤트.
follow_events사용자와 대상이 포함된 팔로우 이벤트.
unfollow_events사용자와 대상이 포함된 언팔로우 이벤트.
block_events사용자와 대상이 포함된 차단 이벤트.
unblock_events사용자와 대상이 포함된 차단 해제 이벤트.
mute_events사용자와 대상이 포함된 음소거 이벤트.
unmute_events사용자와 대상이 포함된 음소거 해제 이벤트.
user_event사용자가 앱 권한을 제거할 때 발생하는 해지 이벤트(구독 자동 삭제).
direct_message_events보낸/받은 메시지에 대한 DM 상태.
direct_message_indicate_typing_events사용자와 대상이 포함된 DM 입력 중 이벤트.
direct_message_mark_read_events사용자와 대상이 포함된 DM 읽음 이벤트.
tweet_delete_events규정 준수를 위한 삭제된 게시물 알림.
spaces_events현재는 지원되지 않습니다. 곧 제공될 예정입니다.

페이로드 예시

아래는 각 Account Activity 이벤트별 페이로드 예시입니다.

tweet_create_events (게시물, 리포스트, 답글, 인용 게시물)

{
  "for_user_id": "2244994945",
  "tweet_create_events": [
    {
      <Tweet 객체>
    }
  ]
}

tweet_create_events (@멘션)

{
  "for_user_id": "2244994945",
  "user_has_blocked": "false",
  "tweet_create_events": [
    {
      <트윗 오브젝트>
    }
  ]
}

favorite_events

{
  "for_user_id": "2244994945",
  "favorite_events": [{
    "id": "a7ba59eab0bfcba386f7acedac279542",
    "created_at": "Mon Mar 26 16:33:26 +0000 2018",
    "timestamp_ms": 1522082006140,
    "favorited_status": {
      <트윗 객체>
    },
    "user": {
      <사용자 객체>
    }
  }]
}

follow_events

{
  "for_user_id": "2244994945",
  "follow_events": [{
    "type": "follow",
    "created_timestamp": "1517588749178",
    "target": {
      <User Object>
    },
    "source": {
      <User Object>
    }
  }]
}

언팔로우_이벤트

{
  "for_user_id": "2244994945",
  "follow_events": [{
    "type": "unfollow",
    "created_timestamp": "1517588749178",
    "target": {
      <사용자 객체>
    },
    "source": {
      <사용자 객체>
    }
  }]
}

block_events

{
  "for_user_id": "2244994945",
  "block_events": [{
    "type": "block",
    "created_timestamp": "1518127020304",
    "source": {
      <사용자 객체>
    },
    "target": {
      <사용자 객체>
    }
  }]
}

차단 해제_이벤트

{
  "for_user_id": "2244994945",
  "block_events": [{
    "type": "unblock",
    "created_timestamp": "1518127020304",
    "source": {
      <사용자 오브젝트>
    },
    "target": {
      <사용자 오브젝트>
    }
  }]
}

mute_events

{
  "for_user_id": "2244994945",
  "mute_events": [
    {
      "type": "mute",
      "created_timestamp": "1518127020304",
      "source": {
        <User 객체>
      },
      "target": {
        <User 객체>
      }
    }
  ]
}

unmute_events

{
  "for_user_id": "2244994945",
  "mute_events": [
    {
      "type": "unmute",
      "created_timestamp": "1518127020304",
      "source": {
        <사용자 오브젝트>
      },
      "target": {
        <사용자 오브젝트>
      }
    }
  ]
}

user_event

{
  "user_event": {
    "revoke": {
      "date_time": "2018-05-24T09:48:12+00:00",
      "target": {
        "app_id": "13090192"
      },
      "source": {
        "user_id": "63046977"
      }
    }
  }
}

direct_message_events

{
  "for_user_id": "4337869213",
  "direct_message_events": [{
    "type": "message_create",
    "id": "954491830116155396",
    "created_timestamp": "1516403560557",
    "message_create": {
      "target": {
        "recipient_id": "4337869213"
      },
      "sender_id": "3001969357",
      "source_app_id": "13090192",
      "message_data": {
        "text": "안녕, 월드!",
        "entities": {
          "hashtags": [],
          "symbols": [],
          "user_mentions": [],
          "urls": []
        }
      }
    }
  }],
  "apps": {
    "13090192": {
      "id": "13090192",
      "name": "FuriousCamperTestApp1",
      "url": "https://x.com/furiouscamper"
    }
  },
  "users": {
    "3001969357": {
      "id": "3001969357",
      "created_timestamp": "1422556069340",
      "name": "Jordan Brinks",
      "screen_name": "furiouscamper",
      "location": "볼더, 콜로라도",
      "description": "또 다른 자아 - X PE 개인 의견",
      "url": "https://t.co/SnxaA15ZuY",
      "protected": false,
      "verified": false,
      "followers_count": 22,
      "friends_count": 45,
      "statuses_count": 494,
      "profile_image_url_https": "https://pbs.twimg.com/profile_images/851526626785480705/cW4WTi7C_normal.jpg"
    },
    "4337869213": {
      "id": "4337869213",
      "created_timestamp": "1448312972328",
      "name": "Harrison Test",
      "screen_name": "Harris_0ff",
      "location": "벌링턴, 매사추세츠"
      "protected": false,
      "verified": false,
      "followers_count": 8,
      "friends_count": 8,
      "statuses_count": 240,
      "profile_image_url_https": "https://abs.twimg.com/sticky/default_profile_images/default_profile_normal.png"
    }
  }
}

direct_message_indicate_typing_events

{
  "for_user_id": "4337869213",
  "direct_message_indicate_typing_events": [{
    "created_timestamp": "1518127183443",
    "sender_id": "3284025577",
    "target": {
      "recipient_id": "3001969357"
    }
  }],
  "users": {
    "3001969357": {
      "id": "3001969357",
      "created_timestamp": "1422556069340",
      "name": "Jordan Brinks",
      "screen_name": "furiouscamper",
      "location": "Boulder, CO",
      "description": "또 다른 자아 - X PE 개인적인 의견입니다"
      "url": "https://t.co/SnxaA15ZuY",
      "protected": false,
      "verified": false,
      "followers_count": 23,
      "friends_count": 47,
      "statuses_count": 509,
      "profile_image_url_https": "https://pbs.twimg.com/profile_images/851526626785480705/cW4WTi7C_normal.jpg"
    },
    "3284025577": {
      "id": "3284025577",
      "created_timestamp": "1437281176085",
      "name": "Bogus Bogart",
      "screen_name": "bogusbogart",
      "protected": true,
      "verified": false,
      "followers_count": 1,
      "friends_count": 4,
      "statuses_count": 35,
      "profile_image_url_https": "https://pbs.twimg.com/profile_images/763383202857779200/ndvZ96mE_normal.jpg"
    }
  }
}

direct_message_mark_read_events

{
  "for_user_id": "4337869213",
  "direct_message_mark_read_events": [{
    "created_timestamp": "1518452444662",
    "sender_id": "199566737",
    "target": {
      "recipient_id": "3001969357"
    },
    "last_read_event_id": "963085315333238788"
  }],
  "users": {
    "199566737": {
      "id": "199566737",
      "created_timestamp": "1286429788000",
      "name": "Le Braat",
      "screen_name": "LeBraat",
      "location": "Denver, CO",
      "description": "@X에서는 낮에는 데이터, 해질녘에는 디자인",
      "protected": false,
      "verified": false,
      "followers_count": 299,
      "friends_count": 336,
      "statuses_count": 752,
      "profile_image_url_https": "https://pbs.twimg.com/profile_images/936652894371119105/YHEozVAg_normal.jpg"
    },
    "3001969357": {
      "id": "3001969357",
      "created_timestamp": "1422556069340",
      "name": "Jordan Brinks",
      "screen_name": "furiouscamper",
      "location": "Boulder, CO",
      "description": "분신 - X PE 개인적 견해입니다"
      "url": "https://t.co/SnxaA15ZuY",
      "protected": false,
      "verified": false,
      "followers_count": 23,
      "friends_count": 48,
      "statuses_count": 510,
      "profile_image_url_https": "https://pbs.twimg.com/profile_images/851526626785480705/cW4WTi7C_normal.jpg"
    }
  }
}

tweet_delete_events

{
  "for_user_id": "930524282358325248",
  "tweet_delete_events": [
    {
      "status": {
        "id": "1045405559317569537",
        "user_id": "930524282358325248"
      },
      "timestamp_ms": "1432228155593"
    }
  ]
}

장문 게시물 지원

V2 Account Activity API에서 이제 280자를 초과하는 장문 게시물을 지원합니다. 장문 게시물이 tweet_create_events 페이로드에 포함되면 text 필드에는 처음 140자(또는 그 이하)만 들어가며, truncated 필드는 true로 설정됩니다. 전체 게시물 내용은 extended_tweet 객체로 전달되며, 다음을 포함합니다:
  • full_text: 280자 제한을 넘는 모든 문자를 포함한 게시물의 전체 텍스트
  • entities: 전체 텍스트에 나타나는 모든 엔터티(예: 해시태그, URL, 사용자 멘션, 심볼). 280번째 문자 이후의 항목도 포함됨
  • display_text_range: 전체 텍스트를 고려한 표시 문자 범위
이를 통해 애플리케이션은 텍스트 후반부에 등장하는 멘션이나 기타 엔터티를 포함해 장문 게시물의 전체 내용을 처리할 수 있습니다. 아래는 장문 게시물의 tweet_create_events 페이로드 예시입니다:
{
  "for_user_id": "1603419180975409153",
  "tweet_create_events": [
    {
      "created_at": "Mon May 19 14:01:46 +0000 2025",
      "id": 1924465506158879000,
      "id_str": "1924465506158878979",
      "text": "안티키테라 메커니즘: 고대의 기지를 들여다보는 창 1901년 그리스 안티키테라 섬 앞바다에서 난파된 로마 선박의 잔해 속에서 발견된 안티키테라 메커니즘은 종종 세계 최초의 아날로그 컴퓨터로 불립니다. 기원전 약 100년경으로 거슬러 올라가는 이 정교한 청동 장치는 세련된 설계와 신비로운 용도로 역사가, 고고학자, 과학자들을 매료시켜 왔습니다. 이 장치는 정밀하게 제작된 복잡한 기어 시스템으로 구성되어, 그 시대를 훨씬 앞선 계산을 수행했습니다. 그 발견은 고대 세계의 기술적 역량에 대한 오랜 통념에 도전했습니다. 안티키테라 메커니즘의 주된 기능은 천문 관측이었던 것으로 보입니다. 태양, 달, 어쩌면 행성의 위치를 예측하고, 일식과 월식을 추적하며, 올림픽 개최일까지 계산할 수 있었습니다. 고대 그리스어 비문과 함께한 다이얼과 포인터는 이 장치가 과학적 탐구와 문화적 행사 모두를 위한 도구였음을 시사합니다. 19세기 시계 제작에 비견되는 정밀도는 헬레니즘 세계의 창작자들이 지닌 놀라운 기지를 여실히 보여 줍니다. 수십 년의 연구에도 불구하고 여전히 많은 의문이 남습니다. 누가 만들었으며 누구를 위해 만들었을까요? 단 하나의 걸작이었을까요, 아니면 역사 속에 사라진 더 넓은 기계 전통의 일부였을까요? X선 영상과 현대 계산 기법을 통해 내부 구조가 많이 밝혀졌지만, 그 역량의 전모는 아직 논쟁 중입니다. 점성술적 예측에 사용되었다고 보기도 하고, 천문학 교육용 도구로 보기도 합니다. 안티키테라 메커니즘은 인간의 호기심과 혁신을 증명하는 유산으로, 고대와 현대를 잇습니다. 이는 고대에도 오늘날 기술에 버금가는 복잡한 도구로 우주를 이해하려 했음을 일깨워 줍니다. 그 끝나지 않은 미스터리는 여전히 경외와 탐구심을 불러일으키며, 발굴된 유물 가운데 가장 놀라운 것 중 하나로 남아 있습니다.\n@xai\n@HistoryInPics",
      "display_text_range": [
        0,
        140
      ],
      ...
      "user": {
        ...
      },
      ...
      "extended_tweet": {
        "full_text": "The Antikythera Mechanism: A Window into Ancient Ingenuity Discovered in 1901 among the wreckage of a Roman ship off the Greek island of Antikythera, the Antikythera Mechanism is often hailed as the world's first analog computer. This intricate bronze device, dating back to around 100 BCE, has captivated historians, archaeologists, and scientists with its sophisticated design and mysterious purpose. The mechanism consists of a complex system of gears, meticulously crafted to perform calculations that remain astonishingly advanced for its time. Its discovery challenged long-held assumptions about the technological capabilities of the ancient world. The primary function of the Antikythera Mechanism appears to have been astronomical. It could predict the positions of the sun, moon, and possibly planets, track lunar and solar eclipses, and even calculate the dates of the Olympic Games. The device’s dials and pointers, coupled with inscriptions in ancient Greek, suggest it was a tool for both scientific inquiry and cultural events. Its level of precision, comparable to that of 19th-century clockmaking, underscores the remarkable ingenuity of its creators, likely from the Hellenistic world. Despite decades of study, many questions remain. Who built it, and for whom? Was it a singular masterpiece or part of a broader tradition of mechanical devices lost to history? X-ray imaging and modern computational techniques have revealed much about its inner workings, yet the full scope of its capabilities is still debated. Some propose it was used for astrological predictions, while others see it as a teaching tool for astronomers. The Antikythera Mechanism stands as a testament to human curiosity and innovation, bridging the ancient and modern worlds. It reminds us that even in antiquity, people sought to understand the cosmos with tools that rival the complexity of today’s technology. Its enduring mystery continues to inspire awe and investigation, making it one of the most remarkable artifacts ever unearthed.\n@xai\n@HistoryInPics",
        "display_text_range": [
          0,
          2051
        ],
        "entities": {
          "hashtags": [],
          "urls": [],
          "user_mentions": [
            {
              "screen_name": "xai",
              "name": "xAI",
              "id": 1661523610111193000,
              "id_str": "1661523610111193088",
              "indices": [
                2032,
                2036
              ]
            },
            {
              "screen_name": "HistoryInPics",
              "name": "History Photographed",
              "id": 1582853809,
              "id_str": "1582853809",
              "indices": [
                2037,
                2051
              ]
            }
          ],
          "symbols": []
        }
      },
      ...
      "entities": {
        "hashtags": [],
        "urls": [
          {
            "url": "https://t.co/bzbEKj8cd8",
            "expanded_url": "https://twitter.com/i/web/status/1924465506158878979",
            "display_url": "twitter.com/i/web/status/1…",
            "indices": [
              117,
              140
            ]
          }
        ],
        "user_mentions": [],
        "symbols": []
      },
      ...
    }
  ]
}

Account Activity API 레거시 엔터프라이즈에서 v2로 마이그레이션하기

마이그레이션 가이드를 확인하세요!

자주 묻는 질문

Account Activity API를 사용하면 어떤 이점이 있나요? Account Activity API는 웹훅을 사용해 스트리밍 API처럼 지속 연결을 유지하거나 REST API처럼 빈번히 폴링할 필요 없이 실시간으로 데이터를 전달합니다. 주요 이점은 다음과 같습니다:
  • 속도: X의 속도로 데이터를 전달합니다.
  • 단순성: 단일 웹훅 연결로 게시물, @멘션, 답글, 리포스트, 인용 트윗, 좋아요, DM, 팔로우, 차단, 뮤트 등 모든 계정 이벤트를 제공합니다.
  • 확장성: 관리 중인 계정의 모든 활동을 요청 한도나 이벤트 상한 없이 지원합니다(Enterprise 티어).
Account Activity API용 개발, 스테이징, 프로덕션 환경이 필요합니다. 가능한가요? 예! 여러 웹훅 URL을 등록하고 V2 Webhooks API로 구독을 각각 관리할 수 있습니다. Account Activity API를 설정하는 단계별 가이드가 있나요? 예! 웹훅 시작하기 가이드Account Activity API 샘플 애플리케이션을 참고하세요. Account Activity API에는 어떤 인증을 사용해야 하나요? 인증 요건은 엔드포인트별로 지정됩니다. 자세한 내용은 Authentication 섹션을 확인하세요. 서로 상호작용하는 사용자들을 구독하면 중복 활동이 수신되나요? 예. 앱이 사용자 A와 사용자 B 모두를 구독하고 있고, 사용자 A가 게시물에서 사용자 B를 멘션한 경우 웹훅은 두 개의 이벤트(사용자당 하나)를 받습니다. 구독을 식별하려면 for_user_id 필드를 사용하세요. 웹훅을 구독할 때, 전달되는 활동을 제한하려고 엔드포인트의 /all/ 부분을 다른 계정 활동 데이터 객체로 바꿀 수 있나요? 아니요. /all/만 사용할 수 있으며, 지원되는 모든 이벤트 유형을 전달합니다. 세 개의 웹훅에 접근할 수 있다면, 엔터프라이즈 용도로 등록한 각 앱마다 세 개의 웹훅을 사용할 수 있나요? 웹훅 한도는 앱 단위가 아닌 계정 단위로 설정됩니다. 예를 들어 웹훅이 세 개이고 앱이 두 개인 경우, 한 앱에는 두 개, 다른 앱에는 한 개를 사용할 수는 있지만 앱당 세 개씩은 사용할 수 없습니다.

Account Activity API 참조 색인

목적V2 엔드포인트
애플리케이션을 계정 이벤트에 구독시킵니다POST /2/account_activity/webhooks/:webhook_id/subscriptions/all
현재 활성화된 구독 수를 반환합니다GET /2/account_activity/subscriptions/count
웹훅이 특정 계정에 구독되어 있는지 확인합니다GET /2/account_activity/webhooks/:webhook_id/subscriptions/all
현재 활성화된 구독 목록을 반환합니다GET /2/account_activity/webhooks/:webhook_id/subscriptions/all/list
앱 전용 OAuth를 사용해 구독을 비활성화합니다DELETE /2/account_activity/webhooks/:webhook_id/subscriptions/:user_id/all
재생 작업을 생성합니다POST /2/account_activity/replay/webhooks/:webhook_id/subscriptions/all
웹훅 관리 엔드포인트(등록, 보기, 검증, 삭제)는 V2 Webhooks API 문서를 참조하세요