X 게시물과 프로필 JSON, 필드별 정리: 에이전트를 위한 레퍼런스

업데이트
UnifAPI 무료 X 엔드포인트가 게시물, 프로필, 페이지, 속도 제한, 오류에 대해 반환하는 모든 필드와 타입, 처리해야 할 예외 사례.
한마디로: 로그인 없이 무료로 쓸 수 있고 방문자별 횟수 제한이 적용되는 UnifAPI의 X 엔드포인트는 레코드 세 가지(
post,profile, 그리고 목록을 감싸는page래퍼)와 오류 모양 하나, 속도 제한 헤더 세 개를 반환합니다. 필드 이름은 camelCase이고, id는 문자열이며(2^53을 넘습니다), 날짜는 UTC 기준 ISO 8601, 수치는 정수이고, X가 보고하지 않는 값은 0이 아니라 아예 없습니다. 이 페이지는 모든 필드를 타입과 함께, 그리고 프로그램이 처리해야 할 경우들과 함께 나열합니다. 원본 게시물에repostedBy가 더해지는 리포스트, 한 단계만 중첩되는 인용, 수치는 반환하지만 게시물은 반환하지 않는 보호된 계정, 그리고 X가 대부분의 게시물에는 보고하지만 전부는 아닌views입니다.
모델은 본 적 있는 JSON을 잘 읽습니다. 데이터 소스를 올바르게 쓰는 에이전트와 필드 이름을 추측하는 에이전트의 차이는, 레퍼런스가 컨텍스트에 있었는지 학습 데이터에 있었는지입니다. 이 글이 그 레퍼런스입니다. 사람용 뷰어 페이지가 렌더링하는 것과 같은 모델이므로, /tools/x-profile-viewer에서 보는 것이 정확히 /tools/x/profile?handle=…이 반환하는 것입니다.
규약
- id는 문자열입니다. X의 id는 64비트이고, JSON 숫자는 2^53을 넘으면 정밀도를 잃습니다. URL과 커서 안을 포함해 모든
id는 문자열입니다. - 날짜는 ISO 8601, UTC입니다.
createdAt과joinedAt은2026-10-05T22:33:56.000Z처럼 생겼습니다. - 수치는 정수입니다. 서식이 적용된 문자열도, null도 아닙니다. X가 보고하지 않은 수치는 0으로 설정되지 않고 빠집니다.
- 선택 필드는 없을 수 있습니다. 선택으로 표시된 필드는 값이 없으면 객체에서 빠집니다.
=== 0이 아니라"views" in post.metrics로 검사하세요. - URL은 정식 x.com URL입니다.
post.url은https://x.com/<handle>/status/<id>이고, 미디어 URL은 X의 CDN을 가리키며 절대 다시 호스팅되지 않습니다.
post
/tools/x/post에서 post로, 그리고 profile, media, search의 posts.items와 replies의 replies.items의 각 항목으로 반환됩니다.
| 필드 | 타입 | 의미 |
|---|---|---|
id | string | Post ID |
text | string | 전체 텍스트. 긴 게시물(노트)은 전부 반환되고, 미디어가 첨부된 경우 끝의 미디어 단축 링크는 제거됩니다 |
createdAt | string (ISO 8601) | 게시 시각 |
language | string, 선택 | X가 감지한 BCP-47 코드 (en, ja, …) |
conversationId | string, 선택 | 이 게시물이 속한 스레드의 루트 게시물 id |
inReplyToPostId | string, 선택 | 답글인 경우, 이 게시물이 답하는 게시물 |
possiblySensitive | boolean | X 자체 플래그 |
url | string | 정식 x.com URL |
author | profile 또는 null | 작성자. X가 숨긴 경우에만 null |
metrics | object | 아래 참조 |
media | media 배열 | 없으면 빈 배열 |
repostedBy | object 또는 null | 이 항목이 리포스트일 때 설정됩니다. 리포스트한 계정의 { id, handle, name }. 레코드의 나머지는 원본 게시물입니다 |
quoted | post 또는 null | 인용 게시물인 경우 인용된 게시물. 한 단계만 중첩됩니다(인용 안의 인용은 펼치지 않습니다) |
metrics
| 필드 | 타입 | 의미 |
|---|---|---|
likes | integer | |
reposts | integer | 리포스트(리트윗). 인용은 세지 않습니다 |
replies | integer | X의 답글 수. 읽을 수 있는 공개 답글 수보다 클 수 있습니다 |
quotes | integer | 인용 게시물 |
bookmarks | integer | |
views | integer, 선택 | 노출 수. X가 대부분의 게시물에 보고하며, 보고하지 않으면 없습니다 |
media
| 필드 | 타입 | 의미 |
|---|---|---|
kind | photo | video | animated_gif | |
url | string, 선택 | 원본 크기 이미지, 또는 동영상과 GIF의 포스터 프레임 |
previewUrl | string, 선택 | 현재는 url과 같습니다. 더 작은 렌디션용으로 예약 |
width, height | integer, 선택 | 원본 크기 |
durationMs | integer, 선택 | 동영상 길이 |
videoUrl | string, 선택 | X가 노출하는 최고 비트레이트 MP4, X의 CDN에 있음 |
profile
/tools/x/profile, /tools/x/media, /tools/x/followers에서 profile로, post.author로, 그리고 followers의 users.items의 각 항목으로 반환됩니다.
| 필드 | 타입 | 의미 |
|---|---|---|
id | string | User ID |
handle | string | @ 없이, X의 대소문자 그대로 (NASA) |
name | string | 표시 이름 |
verified | boolean | 모든 종류의 인증 (블루, 정부, 비즈니스) |
verifiedType | string, 선택 | X가 명시하는 경우 Government, Business, … |
protected | boolean | 게시물이 공개되지 않음. 엔드포인트는 수치는 반환하지만 게시물은 반환하지 않습니다 |
bio | string | 비어 있을 수 있음 |
location | string | 자유 텍스트, 비어 있을 수 있음 |
website | string, 선택 | t.co 단축 링크가 아닌 펼쳐진 URL |
joinedAt | string (ISO 8601) | |
avatarUrl | string, 선택 | 400×400 렌디션 |
bannerUrl | string, 선택 | |
followers | integer | |
following | integer | |
posts | integer | 누적 게시물 수 |
listed | integer, 선택 | 보고되는 경우, 계정이 포함된 리스트 수 |
pinnedPostId | string, 선택 | |
url | string | https://x.com/<handle> |
/tools/x/profile은 engagementRate를 더합니다. 페이지의 원본 게시물당 좋아요, 답글, 리포스트, 인용의 평균을 팔로워 수로 나눈 값을 유효 숫자 네 자리의 백분율로 나타낸 것입니다. 리포스트는 평균에서 제외됩니다. 팔로워 9천만 명인 계정에서는 작은 숫자(0.0058)가 나오는데, 버그가 아니라 정확한 값입니다.
page
모든 목록은 감싸여 있습니다.
{ "items": [ … ], "hasMore": true, "nextCursor": "DAAHCgABHUAq3YL__-wLAAIAAAATMj…" }
| 필드 | 타입 | 의미 |
|---|---|---|
items | array | post 레코드, followers에서는 profile 레코드, 또는 community 레코드 |
hasMore | boolean | 정확히 nextCursor가 설정된 경우에만 true |
nextCursor | string 또는 null | 불투명. cursor=로 되돌려 보내면 다음 페이지를 받습니다 |
페이지 크기: profile은 게시물 10건, media는 12건, replies, search, communities는 20건, followers는 50건, 트렌드는 30건입니다. 커서는 불투명합니다. 20@처럼 생긴 것도 있고 X 자체의 DAAH… 문자열처럼 생긴 것도 있습니다. 절대 파싱하지 마세요.
트렌드와 커뮤니티
/tools/x/trends는 { woeid, locationSlug, generatedAt, trends: [ { rank, name, postCount?, url } ] }를 반환합니다. postCount는 X가 볼륨을 보고하는 트렌드에만 있습니다. 오늘날 대부분의 지역에서는 하나도 없으므로 선택 필드로 취급하세요. url은 x.com의 실시간 검색입니다.
/tools/x/communities는 { query, communities: page }를 반환하며, 각 항목은 { id, name, description, memberCount?, access?, joinPolicy?, createdAt?, url }입니다.
속도 제한 헤더
성공이든 오류든 모든 응답에 실립니다.
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 17
X-RateLimit-Reset: 1791357984
Reset은 현재 10분 윈도가 끝나는 시점의 Unix 초입니다. 검색은 2유닛, 나머지는 모두 1유닛을 씁니다. 429에는 초 단위의 Retry-After가 더해집니다.
오류
2xx가 아닌 모든 응답은 다음과 같습니다.
{ "error": { "type": "not_found", "message": "X user not found: @nobody_here_123" } }
type | HTTP | 언제 |
|---|---|---|
validation_error | 400 | 입력이 없거나 형식이 잘못됨 (Missing handle: pass ?handle=…) |
not_found | 404 | 계정이나 게시물이 없음. 정지된 계정은 메시지에 그렇게 표시됨 |
rate_limited | 429 | 무료 제한 도달. 본문에 retryAfterSeconds와 upgrade가 더해짐 |
upstream_error | 502 | X의 데이터 레이어가 오류로 응답함 |
upstream_unavailable | 503 | 타임아웃이거나 연결할 수 없음 |
not_configured | 503 | 배포에 업스트림 자격 증명이 없음 |
type으로 분기하세요. message는 사람을 위한 것이고 바뀔 수 있습니다.
Markdown 모드
어느 엔드포인트에든 format=md를 붙이거나 Accept: text/markdown을 보내면 같은 레코드가 Markdown으로 렌더링됩니다. 작성자가 담긴 제목, 텍스트, 지표 한 줄, 미디어 링크, URL, 그리고 다음 커서를 알려주는 푸터입니다. 작성자 객체 안의 원시 id를 제외한 위의 모든 필드를 담고 있으며, 프로필이나 스레드를 모델의 컨텍스트에 넣는 가장 저렴한 방법입니다.
샘플이 있는 곳
각 도구의 Markdown 페이지에는 해당 엔드포인트의 캡처된 실제 응답이 몇 항목으로 잘라 담겨 있습니다. /tools/x-profile-viewer/index.md, /tools/x-post-viewer/index.md, /tools/x-search/index.md 등입니다. /llms.txt는 여덟 개 모두를 예시 URL과 함께 나열합니다. 종량제 X / Twitter API는 워크스페이스 키 뒤에서 같은 필드 이름을 쓰므로, 무료 엔드포인트 기준으로 작성한 코드는 수정 없이 옮겨 갑니다.