X 최종 결정 반영(2026-07-29) · 코드 반영 전

Social Fetcher URL Structure Audit — 2026-07-19
관련 코드: social-fetcher.router.ts(classifyX) · fetchers/x.fetcher.ts · fetchers/x-community.fetcher.ts
관련 데이터: samples/x_profile.json · samples/x_tweet.json · samples/x_community.json
소스 채택 권장도 adopt-x_profile+x_tweet / narrow-adopt-x_community
X는 밈 모멘텀이 실제로 사는 소스다 — createdAt(러그), engagement velocity(모멘텀), author 신원(KOL 여부), 바이오/텍스트(진정성·서사)까지 트레이더의 4대 판단축을 모두 직접 커버하는 필드가 다수 actionable로 나옴. 반면 x_community는 개념적으로는 창설자 이력·개설일 등 러그/진정성 축에 유효하나, 20 credits/호출 비용과 관측된 skipped_paid 패턴 때문에 정작 필요할 때 채워지지 않는 경우가 흔해 '있으면 유용, 없을 때가 잦다'는 한계가 있음. 또 URL패턴 감사 결과 x_community 관측 137건 중 다수가 유료라 스킵되는 것으로 보여, 이 소스는 상시 파이프라인보다 러그 의심 시 온디맨드 보강용으로 좁혀 채택하는 게 트레이더 관점에서 합리적.
트레이더 관점: 핸들/트윗 텍스트/참여수/작성자 계정나이 조합만으로도 5초 안에 '이 계정 방금 만들어졌고 봇 리트윗만 도는' 패턴을 잡아낼 수 있어 X profile+tweet은 명백히 actionable 소스다. 반면 X community는 개설자 추적처럼 진짜 유용한 축이 있음에도 실제로는 비용 게이트에 걸려 자주 비어있고, description/rules 같은 나머지 필드 절반은 아카이브성이라 밈 모멘텀이 사는 자리라기보다 '의심될 때 한번 확인하는 보조 자료'에 가깝다. github은 이 감사 범위 밖이지만 컨텍스트 원칙대로 진위판별 전용이지 모멘텀원은 아니라는 판단과 같은 결로, x_community도 비슷한 위상(모멘텀 소스가 아니라 러그체크 보조)으로 본다.
✅ 최종 결정 (수기 audit 반영)
원칙: sourceKey는 unique & immutable & linkable. X의 userName(handle)은 변경 가능하고 재할당도 되므로 키 부적격 → 숫자 id로 완전 통일(audit: "creator Id 결정나는 걸로 완전 통일할 필요가 있음 어떤 type이든 하나로"). handle은 alias/표시용으로 병행 기록.
조인 키
객체비고
Creator (프로필)id (숫자 유저ID)audit의 조건("contents에서 id 매핑이 가능하면 id, 불가하면 userName")을 실측으로 충족 — 트윗 응답의 author.id가 10/10 전부 존재(예: 35203319). 따라서 audit의 "profile 제거대상: id ← 무엇을 키로 쓸지 확정되면"도 해제: id는 제거가 아니라 키로 승격. userName은 alias(router sourceKey)로 병행 기록하되 조인·dedup 금지.
Content (트윗)id (tweetId) + author.id (creator 링크)tweetId=dedup 키(라우터 sourceKey와 실측 일치). creator 링크는 응답에 동봉된 author.id.
Venue (커뮤니티)community_info.id + creator.id (개설자 링크)sourceKey가 이미 canonical numeric ID라 실측에서 요청 파라미터와 정확히 일치. 개설자 연결은 creator.id(숫자)로 — 응답 키가 userName/screen_name으로 불안정해 handle 폴백은 위험.
sourceType 통합 맵
최종 타입포함 패턴
x_profile/{handle}, twitter.com/{handle}, /i/user/{numericId}, /{handle}/with_replies·/media·/likes — audit 지시대로 전부 x_profile로 모으고 키 체계를 숫자 id로 통일. handle 경로는 alias→id resolve, /i/user/는 이미 id
x_tweet/{handle}/status/{tweetId}[/photo|/video|/quotes 등 접미사] — key=tweetId
x_community/i/communities/{communityId} — key=communityId
x_tweet_search (신규)audit override: /search?q=..., /hashtag/{tag} — 기존 문서는 둘 다 unknown 유지 제안이었으나 audit이 "검색류는 그냥 tweet_search로 전부(hash, search 모두)"로 지시 → 단일 타입 통합(key=쿼리/태그)
x_intent (신규 · 의심 플래그)audit override: /intent/post?text=..., /intent/tweet — 계정·콘텐츠가 아니라 "미리 채워진 트윗 작성 화면" 링크. 정상 프로젝트가 공식 소셜로 등록할 이유가 없음 → 별도 타입으로 분리해 의심 플래그. 단 상류 추출 오류(공유버튼 링크를 긁음)일 수도 있어 "스캠 확정"이 아닌 flag로 취급
unknown 유지/i/trending/{id}(audit 질문에 대한 답: 별도 관리 안 함 — 시점별 트렌드 집계 페이지라 토큰과 안정적으로 연결되는 식별자가 아니고 시간이 지나면 재현 불가), /i/{tool}?param=(Grok 등 도구 페이지)
구조 결정 — 필드 단위 key/keep/remove로 표현할 수 없는 결정
항목내용
quote/retweet 재귀 전개 (fan-out) — 깊이 2단계 제한audit notes: "X 컨텐츠의 핵심은 quote. quote는 quote contents뿐 아니라 quote profile도 연결. 특히 token → quote도 연결되어야 함. 결국 quote가 본 컨텐츠인 경우가 많아서". 실측으로 추가 fetch 없이 충족 가능함을 확인: quoted_tweet의 키 집합이 최상위 트윗과 100% 동일하고, quoted_tweet.author도 완전한 프로필 객체(id·userName·followers·createdAt·description·isBlueVerified)이며 참여수도 전부 포함(실측 like15/view2903/quote2/bookmark6). → 1회 응답을 Content·Creator 레코드 N개로 전개: 인용된 트윗 = 동등한 1급 Content 레코드, 그 작성자 = 1급 Creator 레코드. 토큰이 관측한 트윗의 인용 대상까지 따라가므로 token→quote 링크도 성립. 스키마가 동일해 파서 재사용 가능. quoted_tweet.quoted_tweet 필드도 존재하므로 순환·무한재귀 방지를 위해 깊이 2단계로 제한. retweeted_tweet도 동일 처리.
트윗 응답의 author로 Creator 레코드 생성실측 신규 발견: 트윗 응답의 author는 문서가 기록한 3개 필드(userName/createdAt/followers)가 아니라 33개 키의 완전한 프로필 객체(id·name·description·location·statusesCount·mediaCount·favouritesCount·isBlueVerified·profilePicture·pinnedTweetIds·profile_bio·status …). → 트윗 1콜로 작성자 Creator 레코드가 완성되므로 별도 x_profile 유료 호출이 상당 부분 불필요 (커버리지↑·비용↓). ⚠ 단 이 스냅샷의 시각은 "프로필 조회 시점"이 아니라 "트윗 조회 시점"이므로 as-of 기록 필수(mutable 지표를 다른 시점 값과 섞으면 안 됨).
이 문서에서 쓰는 pill 범례
관측 source-data에서 실제 발견됨 웹리서치/실측 검색·curl·실제 호출로 확인 정상/유지 차단·버그·갭·drop 신규/분리 타입 fixed / mutable confidence: high medium low
목차   1a. 개념 글로서리 · 1b. URL 패턴 · 2. sourceType/sourceKey 판정 · 3. Fetch 테스트 + 획득 방법 · 4. 필드 통합 (변동성·파싱·판정) · 다음 액션

1a. 플랫폼 개념 글로서리

이 플랫폼을 이해하는 데 필요한 도메인 개념. URL/필드 설명에서 참조.

개념설명
x.com / twitter.com 도메인 동의어동일 플랫폼을 가리키는 두 호스트명. twitter.com은 구 브랜드명 URL로 여전히 유효하게 리졸브됨(x.com으로 리다이렉트). audit에서는 별개 template로 집계되지만 동일 sourceType(x_profile/x_tweet/x_community)으로 정규화되어야 함 관측
프로필 URL (x_profile)x.com/{userName} 형태. userName(핸들) 하나로 계정을 가리킴. status/i/ 등의 예약 세그먼트가 붙지 않은 최상위 경로 관측
트윗/포스트 URL (x_tweet)x.com/{userName}/status/{tweetId} 형태. status 세그먼트 뒤에 숫자형 tweetId(snowflake ID)가 옴 관측
커뮤니티 URL (x_community)x.com/i/communities/{communityId} 형태. 유저명 없이 i/(internal) 네임스페이스 하위에 커뮤니티 숫자 ID가 옴. 표본에서 status는 전부 skipped_paid — 유료/제한 리소스라 미조회 관측
article 서브패스x.com/{userName}/article/{articleId} — X Articles(장문 게시물) 개별 글 URL. tweet과 별개 콘텐츠 타입이지만 표본에서는 sourceType이 x_profile로 분류되어 프로필 fetch 결과만 담김(article 본문 자체는 미조회) 관측
photo/{n}, video/{n} 서브패스트윗 URL 뒤에 붙는 미디어 인덱스 세그먼트. 트윗에 첨부된 여러 이미지/영상 중 n번째를 직접 가리키는 딥링크. 트윗 자체(sourceKey=tweetId)는 동일하며 상세 미디어 위치만 특정 관측
quotes 서브패스x.com/{userName}/status/{tweetId}/quotes — 해당 트윗을 인용(quote)한 트윗들의 목록 페이지. 원본 트윗 자체는 sourceKey로 식별되나, 실제 대상은 원 트윗이 아니라 '이 트윗을 인용한 글 모음' 뷰 관측
쿼리 스트링 트래킹 파라미터 (?s=46 등)공유 시 자동 부착되는 X 자체 트래킹 파라미터. URL 정규화 시 무시해도 동일 리소스를 가리킴(표본: video/1?s=46) 관측
sourceKey 정규화 규칙(대소문자)userName은 대소문자 섞여 fetch되지만(userName: 'UntaxedSolana') sourceKey는 소문자로 정규화됨('untaxedsolana'). tweetId/communityId 등 숫자 ID는 원문 그대로 사용 관측
userName vs id vs nameX 계정은 3개의 별개 식별자를 가짐 — userName(=핸들, @없이, 변경 가능하지만 표본에서 fixed로 분류), id(내부 숫자 스노우플레이크 ID, 영구불변), name(표시 이름/닉네임, 이모지·비ASCII 허용, 자유 변경 가능). 표본 valueNature는 userName을 fixed로 취급하지만 실제 X 정책상 유저는 핸들을 변경할 수 있어 장기적으로는 mutable에 가까움 관측
팔로워/팔로잉 수 (followers/following)계정의 구독자 수 / 구독 중인 계정 수. 표본에서 mutable 지표로 분류(시간에 따라 계속 변함) 관측
statusesCount계정이 작성한 전체 트윗(리트윗 포함) 누적 개수. mutable 지표 관측
isVerified / isBlueVerifiedisVerified=레거시 인증배지(기관/유명인 등 X 자체 심사), isBlueVerified=유료 구독(X Premium/Blue) 기반 배지. 별개 불리언이며 둘 다 true/false 조합 가능(표본에서 verified=false, blueVerified=true 다수 관측) 관측
createdAt (계정)계정 최초 생성 시각. 표본에서 fixed(불변) 지표로 분류 — 계정 나이 추정에 사용 가능 관측
likeCount / retweetCount / replyCount / viewCount트윗 단위 참여 지표. like=좋아요, retweet=리트윗+quote 합산 여부는 불명(X API 관례상 retweet_count는 순수 리트윗), reply=답글 수, view=노출(조회) 수. 모두 mutable(시간에 따라 계속 누적) 관측
isQuote / isRetweet해당 트윗이 다른 트윗을 인용(quote tweet)했는지 / 순수 리트윗(재게시, 자체 텍스트 없음)인지 나타내는 불리언. 표본에서는 fixed로 분류(트윗 생성 시 결정되고 이후 안 바뀜) 관측
quoted / retweeted 중첩 객체isQuote/isRetweet가 true일 때 채워지는 원본 트윗 정보(text, createdAt, authorUserName 등). quoted는 인용된 원본 트윗, retweeted는 리트윗 대상 원본 트윗. 둘 다 false면 null 관측
status 필드 값(fetch 결과 상태)ok=정상 조회, error=요청 실패(예: 존재하지 않는 계정), not_found=대상 리소스(트윗 등) 삭제/비공개, skipped_paid=X 유료 API 등급이 필요해 조회 자체를 건너뜀(커뮤니티 상세는 전량 이 상태) 관측
fixed vs mutable (valueNature)이 audit 파이프라인이 각 필드에 부여한 자체 분류. fixed=생성 시점에 결정되고 이후 변하지 않는 값(트윗 텍스트, 작성일, 인용여부), mutable=시간에 따라 계속 갱신되는 값(팔로워, 좋아요, 조회수). 재조회 캐싱 전략 판단에 사용되는 것으로 추정 관측
Quote Tweet(인용)다른 트윗을 링크로 포함하며 자신의 코멘트를 덧붙여 새로 게시하는 기능. 원본 트윗 위에 자기 발언을 얹는 구조라 순수 Retweet과 구분됨 웹리서치/실측
Retweet(리포스트)타인의 트윗을 자기 코멘트 없이 그대로 자신의 타임라인에 재게시하는 기능. X는 2023년 UI상 'Repost'로 명칭을 바꿨으나 API/데이터 필드명은 여전히 retweet 계열 유지 웹리서치/실측
Community(X 커뮤니티)특정 주제/관심사로 묶인 그룹 공간. 공개 커뮤니티도 있으나 다수가 비공개/승인제이며, 콘텐츠 접근에 로그인+가입이 필요해 일반 크롤링/무료 API로는 접근 제한(표본의 skipped_paid와 부합) 웹리서치/실측
X Article(아티클)280자 제한을 넘는 장문 콘텐츠를 게시하는 기능(구 Twitter Longform/Notes 후신). URL 패턴이 트윗과 달리 /article/{id}이며 별도 콘텐츠 엔티티로 취급됨 웹리서치/실측
핸들(username) 변경 가능성X 유저는 언제든 @handle(userName)을 변경할 수 있음(계정 고유 id는 불변). 과거 URL(x.com/oldhandle)은 핸들 변경 후 해당 유저를 가리키지 못하게 되며, 변경 전 핸들을 다른 유저가 재점유(username squatting)할 수 있어 audit 시 URL만으로 계정 동일성을 보장할 수 없음 웹리서치/실측
snowflake ID (트윗/커뮤니티/계정 숫자 ID)X(Twitter)가 사용하는 64비트 시계열 정렬 가능 고유 ID 생성 방식. ID 값 자체에 타임스탬프가 인코딩되어 있어 생성 시각을 역산할 수 있음. 표본의 id, tweetId, communityId 모두 이 체계 웹리서치/실측
x.com/i/ 네임스페이스'i'는 internal의 약자로, 특정 유저 핸들에 속하지 않는 플랫폼 레벨 리소스(커뮤니티, 스페이스, 웹 인텐트 등)에 쓰이는 예약 경로 세그먼트 웹리서치/실측
웹 인텐트 URL (x.com/intent/tweet 등)특정 액션(트윗 작성, 팔로우, 좋아요 등)을 미리 채운 상태로 여는 공유용 URL 패턴. 프로필/트윗을 직접 가리키지 않으므로 audit에서 별도 식별 필요(표본에는 미등장하나 실무에서 흔한 오분류 원인) 웹리서치/실측
X Premium(Blue) 구독월 구독형 유료 등급으로 파란색 체크마크(isBlueVerified)를 부여. 과거 무료 레거시 인증(isVerified)과는 심사 기준이 달라 계정 신뢰도 판단 시 혼동 주의 웹리서치/실측

1b. URL 패턴

x.com{handle}
x_profile: x.com/{handle}
x.com{handle}status{tweetId}
x_tweet: x.com/{handle}/status/{tweetId}
x.comicommunities{communityId}
x_community: x.com/i/communities/{communityId}
x.comhashtag{tag}
unknown(예약어): x.com/hashtag/{tag} — hashtag가 X_RESERVED_ROOTS라 handle 판정에서 제외됨
URL 패턴의미ID 후보관측출처/비고
x.com/{handle} 또는 twitter.com/{handle}프로필 페이지핸들(userName, 소문자 정규화됨) — 프로필 고유식별은 twitterapi.io 반환 data.id(숫자 유저ID)가 진짜지만 라우터는 핸들을 sourceKey로 사용관측 1118건x_profile 표본 1118건 중 1112(x.com/{value}) + twitter.com 변형 5건. classifyX: segments[0]이 예약어(X_RESERVED_ROOTS)가 아니면 x_profile, sourceKey=lc(handle)
x.com/{handle}/status/{tweetId}[/photo|video|quotes 등 접미사]트윗(게시물) 상세tweetId(status 뒤 숫자, 원형 유지) — handle은 작성자 참고용일 뿐 트윗 자체 식별자 아님관측 3879건x_tweet 3879건 전부. classifyX는 segments.indexOf('status')로 위치무관 탐색하므로 /photo/1, /quotes, /video/1 등 뒤 접미사는 무시되고 동일 sourceKey(tweetId)로 수렴 — fetcher는 getTweetsByIds(tweetId) 호출
x.com/i/communities/{communityId}커뮤니티 페이지communityId(숫자, 원형)관측 137건x_community 137건(twitter.com 변형 1건 포함). classifyX: segments[0]==='i' && segments[1]==='communities' → sourceKey=segments[2]. fetcher는 유료(20 credits/call)라 status='skipped_paid'로 실제 fetch 생략 관측
x.com/search?q=...검색 결과 페이지(특정 계정/트윗 아님)없음 — q 쿼리파라미터는 자유텍스트라 안정적 식별자 아님관측 196건unknown 표본 196건. classifyX에서 segments[0]='search'가 X_RESERVED_ROOTS에 포함되어 handle 판정 실패 → sourceType unknown, sourceKey null (라우터 설계상 의도된 unknown)
x.com/i/trending/{id} (x.com/i/{sub}/{value} 형태)트렌드/기타 i-네임스페이스 하위페이지(트윗/유저 아님)없음 — classifyX의 i-분기는 communities와 status만 처리, trending 등은 미대응관측 82건unknown 표본 82건. classifyX: segments[0]==='i' 이지만 segments[1]이 'communities'도 아니고 경로에 'status'도 없어 최종 return {unknown,null}
x.com/hashtag/{tag}해시태그 검색 페이지태그명 자체는 후보가 될 수 있으나 라우터가 'hashtag'를 예약어로 취급해 미채택관측 4건unknown 4건. segments[0]='hashtag'가 X_RESERVED_ROOTS에 있어 handle() 결과가 예약어로 걸러짐 → unknown. 웹지식상 실제 X는 /hashtag/{tag} 정식 라우트를 지원하지만 이 서비스는 계정/트윗 정보가 아니므로 의도적으로 미분류
x.com/intent/post?text=... (또는 /intent/tweet)트윗 작성 인텐트 링크(공유 버튼), 실제 콘텐츠 아님없음관측 1건unknown 1건. 'intent'가 예약어라 unknown 처리
x.com/i/{tool}?param=... (예: /i/grok?conversation=...)X 내장 도구/대화 링크(Grok 등)없음(현재 라우터 미대응)관측 1건unknown 1건. i-분기에서 communities/status 아니므로 unknown
x.com/i/user/{numericId} (웹지식 보완, 표본 미관측)숫자 유저ID로 프로필 접근하는 X 정식 URL 패턴numericId(twitterapi.io data.id 와 대응)웹리서치/실측관측 안 됨. 현재 classifyX의 i-분기(communities/status만 처리)에서는 unknown으로 빠짐 — 보완 필요 시 라우터 확장 대상
x.com/{handle}/with_replies, /media, /likes (웹지식 보완, 표본 미관측)프로필 하위탭(답글/미디어/좋아요 목록)handle(프로필과 동일 식별자, 하위탭 구분값은 별도 저장 안 함)웹리서치/실측관측 안 됨. 현재 classifyX 로직상 segments[0]=handle이 예약어가 아니므로 x_profile로 분류되어 하위탭 정보 손실(의도적으로 프로필과 동일 취급되는 것으로 추정, 코드상 명시적 분기 없음)

2. sourceType / sourceKey 판정 👤 판단 반영

2.1 sourceType 적합도 👤 판단 반영

패턴현재 type판정/제안sourceKeyconf근거사용자 결정
x.com/{handle} 또는 twitter.com/{handle} (프로필)
x_profilex_profilelc(handle)highclassifyX가 segments[0]을 X_RESERVED_ROOTS와 대조해 예약어가 아니면 x_profile, sourceKey=lc(handle)로 정확히 분류. XFetcher.fetchProfile은 이 sourceKey를 그대로 sdk.getUserByUserName(handle)에 넘기므로 라우팅→fetch 계약이 일치. idCandidate로 언급된 twitterapi.io의 숫자 data.id는 fetch 결과(profileData의 id 필드)로 이미 획득되고 있어 핸들을 sourceKey로 쓰는 현재 설계에 문제 없음(핸들이 fetch 진입점, 숫자ID는 결과 필드).✅ x_profile 유지. 단 sourceKey=handle은 alias이고 canonical 조인키는 숫자 id — audit "creator Id 결정나는 걸로 완전 통일" 반영. fetch 후 data.id로 resolve해 연결.
x.com/{handle}/status/{tweetId}[/photo|video|quotes 등 접미사]
x_tweetx_tweettweetId(원형)highsegments.indexOf('status')로 위치 무관 탐색 후 다음 세그먼트를 tweetId로 채택, 뒤따르는 /photo,/video,/quotes 접미사는 자동 무시됨. XFetcher.fetchTweet은 sourceKey를 그대로 getTweetsByIds([tweetId])에 전달하므로 동일 트윗의 여러 URL 변형이 동일 sourceKey로 정확히 수렴(중복 fetch 방지 관점에서도 올바름).✅ x_tweet 유지(key=tweetId). 접미사 무시하고 tweetId만 추출하는 현행이 정확. 기존 제안 유지.
x.com/i/communities/{communityId}
x_communityx_communitycommunityId(원형)highsegments[0]==='i' && segments[1]==='communities' 분기가 정확히 communityId를 추출하고, 전용 XCommunityFetcher가 sdk.getCommunityInfo(sourceKey)를 호출해 계약이 맞음. 다만 XFetcher.ts 상단 주석('x_community 는 라우터에서 unsupported 로 단락되어 여기 안 옴')은 실제로는 전용 fetcher가 별도로 이 sourceType을 처리하는 현재 구조와 어긋나는 stale 문서 — 라우팅 로직 자체의 결함은 아니므로 verdict에는 영향 없음(코드 주석 정리 권장 사항으로만 별도 플래그).✅ x_community 유지(key=communityId). audit "community도 명확함"과 일치. 기존 제안 유지.
x.com/search?q=...
unknownunknownnullhigh'search'가 X_RESERVED_ROOTS에 포함되어 unknown/null로 떨어짐. q는 자유텍스트라 안정적 join 키가 될 수 없고, 검색결과 페이지는 특정 계정/트윗 실체가 아니므로 트레이더 관점에서도 actionable 정보(진위/모멘텀)를 담지 않음 — 의도된 설계와 일치.결정 변경(audit override) — 기존 제안은 unknown 유지였으나 audit이 "검색류는 그냥 tweet_search로 전부(hash, search 모두)"로 지시 → 신규 x_tweet_search로 편입(key=쿼리).
x.com/i/trending/{id} (i-네임스페이스 기타 하위페이지)
unknownunknownnullmediumi-분기는 communities/status만 명시 처리하고 나머지는 unknown으로 폴백. trending 페이지는 특정 토큰/계정에 귀속된 엔티티가 아니라 플랫폼 전역 집계이므로 우리 목적(콘텐츠 진위·모멘텀 tell)에 쓸 sourceKey가 애초에 없음 — unknown이 오분류가 아니라 올바른 결과. 다만 i-네임스페이스가 향후 다른 신규 서브패스로 계속 늘어날 여지가 있어 완전 커버리지 보장은 아님(신규 등장 시 재검토 필요).✅ audit 질문("trending은 별도로 관리해야하나?")에 대한 답: 별도 관리 안 함, unknown 유지. 시점별 트렌드 집계 페이지라 토큰과 안정적으로 연결되는 식별자가 아니고, 시간이 지나면 내용이 바뀌어 재현 불가.
x.com/hashtag/{tag}
unknownunknownnullmedium'hashtag'가 예약어라 unknown 처리. 해시태그 자체는 여러 계정/트윗의 집합체라 단일 진위/모멘텀 엔티티로 fetch할 수 없어(twitterapi.io도 해시태그 단건 조회 엔드포인트 없음) 라우터 설계와 일치. 다만 트레이더 관점에서 해시태그가 내러티브 확산(모멘텀)의 약한 신호일 수 있어, 향후 트윗 검색 API로 해시태그 볼륨을 집계하는 신규 sourceType 후보로는 남겨둘 만함(현재 관측 4건으로 낮은 우선순위).결정 변경(audit override) — unknown 유지가 아니라 x_tweet_search로 통합(key=태그). audit이 hash/search를 하나로 지시.
x.com/intent/post?text=... (또는 /intent/tweet)
unknownunknownnullhigh'intent'가 예약어라 unknown. 공유 버튼용 작성 인텐트 링크로 실제 콘텐츠를 가리키지 않으므로 unknown이 정확. 관측 1건으로 무시 가능.결정 변경(audit override) — audit: "이건 뭔가 스캠임을 명확히 했으면". unknown에 묻어두지 않고 신규 x_intent 타입으로 분리 + 의심 플래그. intent/post는 계정·콘텐츠가 아니라 "미리 채워진 트윗 작성 화면" 링크라 정상 프로젝트가 공식 소셜로 등록할 이유가 없음. ⚠ 다만 상류 추출이 페이지의 공유버튼 링크를 긁었을 가능성도 있어 "스캠 확정"이 아니라 flag로 취급.
x.com/i/{tool}?param=... (예: /i/grok?conversation=...)
unknownunknownnullhighi-분기가 communities/status만 처리하므로 grok 등 내장 도구 경로는 unknown으로 빠짐. 대화 링크는 프로필/트윗도 아니고 fetch 가능한 안정 엔티티가 없어 unknown이 타당. 관측 1건으로 무시 가능.✅ unknown 유지(도구 페이지, 계정·콘텐츠 아님). 기존 제안 유지.
x.com/i/user/{numericId} (웹지식 보완, 표본 미관측)
unknownx_profile (신규 key-mode: numericId)numericId(원형, twitterapi.io data.id와 대응)low정식 X URL 패턴이지만 classifyX의 i-분기가 communities/status만 처리해 unknown으로 빠짐 — 실제 프로필을 가리키는 링크가 신호 없이 버려지는 커버리지 갭. 단, 표본에서 0건 관측(observed=null)이라 실사용 임팩트는 검증 안 됨. 채택 시 라우터뿐 아니라 fetcher도 확장 필요: 현재 XFetcher.fetchProfile은 getUserByUserName(handle)만 호출하고 twitterapi.io 유저ID 기반 조회 메서드가 SDK에 있는지 별도 확인 필요(코드상 미확인) — 라우터 변경만으로 끝나지 않는 gap. 관측 0건이라 우선순위는 낮음, 실제 데이터에서 이 패턴이 등장하는지 먼저 확인 권장.✅ x_profile로 편입 확정 — audit "creator Id 결정나는 걸로 완전 통일"의 직접 근거가 되는 경로. 이미 숫자 id라 resolve 불필요하고, handle 경로와 같은 키 공간으로 수렴.
x.com/{handle}/with_replies, /media, /likes (웹지식 보완, 표본 미관측)
x_profilex_profilelc(handle)mediumclassifyX에서 segments[0]=handle이 예약어가 아니므로 서브탭 접미사(with_replies 등)와 무관하게 x_profile로 수렴 — status 트윗과 동일하게 '위치 무관 탐색' 철학이 handle에도 일관 적용됨. XFetcher.fetchProfile이 어차피 프로필 전체(팔로워/계정나이/인증여부)만 조회하고 특정 서브탭 콘텐츠(답글 목록 등)는 가져오지 않으므로, 서브탭 구분을 버리고 프로필과 병합하는 현재 동작이 fetch 계약과 일치 — merge가 아니라 원래부터 정확한 keep. 다만 명시적 분기가 아니라 '예약어 미포함=프로필' 폴백의 부수효과라 코드 의도가 아닌 우연한 정합(주석에 명시 없음)이라는 점에서 confidence는 medium.✅ x_profile 유지(하위탭 무시하고 handle만 추출) + 키는 숫자 id로 통일. 기존 제안 유지.

2.2 id 추출 적합도 👤 판단 반영

패턴추출 idresolve?판정conf근거사용자 결정
x.com/{handle} 또는 twitter.com/{handle} — x_profilesourceKey=lc('UntaxedSolana') → sdk.getUserByUserName('UntaxedSolana') 실제 200 응답, data.id=2020402210912509952 등 XFetcher.profileData()가 쓰는 필드 전부 일치okhightwitterapi.io의 user/info 엔드포인트 자체가 userName 조회용이라 handle이 API 계약상 valid key. 다만 진짜 불변 식별자는 data.id(숫자)이고 handle은 개명 가능한 mutable 값 — 시점 T1에 저장한 handle로 시점 T2에 재조회 시 개명되었으면 not_found 될 위험이 있음(단, 이 파이프라인은 토큰 관측 시점 1회성 resolve라 현재 유즈케이스에선 문제 없음). 별개로 live-test에서 발견: twitterapi.io는 정지 계정을 data:null이 아니라 data:{unavailable:true,...} 쉐입으로 반환하는데, XFetcher.fetchProfile의 if (!user) return not_found 가드는 이 쉐입을 통과시켜 profileData()가 user.createdAt.toISOString()에서 TypeError를 던질 가능성이 있음(catch에서 status='error'로 흡수되긴 하나 not_found 대신 error로 분류돼 재시도/집계 로직에 혼선 가능) — id 추출 자체의 결함은 아니고 다운스트림 파서 방어 누락.✅ handle 추출은 정확하나 키는 숫자 id로 통일 — userName은 변경 가능해 alias로 격하. 실측에서 profile 응답의 data.id / 트윗 응답의 author.id 양쪽 확보 확인.
x.com/{handle}/status/{tweetId}[/photo|video|quotes 등] — x_tweetsourceKey='2070103975295131919' → getTweetsByIds(['2070103975295131919']) 실제 200 응답, url이 https://x.com/EvanKirstel/status/2070103975295131919 로 정확히 back-resolveokhightweetId는 X 자체의 canonical 불변 식별자(재작성/개명 영향 없음). 라우터의 indexOf('status') 위치무관 탐색이 /photo/1, /quotes 등 모든 접미사 변형을 동일 sourceKey로 수렴시키는 설계도 실측상 문제없이 동작(fetcher는 tweetId만으로 조회하므로 접미사 손실이 기능 손실 아님).✅ tweetId 추출 정확(실측 sourceKey=응답 id 일치). key=tweetId 유지 + author.id로 creator 연결.
x.com/i/communities/{communityId} — x_communitysourceKey='1952999882635264271' → community/info?community_id=1952999882635264271 200 응답, community_info.id가 curl 요청 파라미터와 정확히 일치okhighcommunityId는 URL 경로에서 직접 원형 추출되는 X의 canonical 숫자 ID. 유료(20 credits) 게이트로 실운영에서는 skipped_paid가 많아 관측 137건 중 실제 fetch가 드물지만, 게이트를 통과했을 때 id resolve 자체는 완전히 정상.✅ communityId가 canonical numeric ID임을 실측 확인(요청 파라미터와 정확히 일치). 유지.
x.com/search?q=... — unknownokhighq는 자유텍스트라 안정 식별자가 될 수 없음 — 'search'를 X_RESERVED_ROOTS에 넣어 sourceKey=null로 떨어뜨리는 현재 설계가 id 오추출을 막는 올바른 선택. wrong이 아니라 의도된 non-extraction.✅ audit 지시로 x_tweet_search 신규 타입에 편입 — 쿼리/태그를 key로. 단일 콘텐츠 식별자는 아님(집계 페이지).
x.com/i/trending/{id} — unknownokmediumclassifyX의 i-분기는 communities/status만 처리하므로 trending은 unknown으로 빠짐 — 잘못된 id를 뽑는 버그는 없고 단순 미대응(coverage gap)일 뿐. trending 페이지는 애초에 밈토큰 진위/모멘텀 신호로 쓸 안정 엔터티가 아니라(우리 목적 기준 무가치) 추가 구현 우선순위 낮음.✅ 시점별 집계라 재현 가능한 식별자 아님 → unknown 유지(별도 관리 안 함).
x.com/hashtag/{tag} — unknownokmediumhashtag를 예약어 처리해 unknown 처리하는 건 의도적. 웹지식상 실제 X가 이 라우트를 지원하지만, 해시태그 페이지는 계정/트윗 단일 엔터티가 아니라 집계 뷰라 '고유 식별자'로 쓰기엔 애매하고, 우리 목적(원본 vs 재활용, 진위 판별)에 직접 기여하는 필드가 아니므로 미분류가 합리적.✅ audit 지시로 x_tweet_search 신규 타입에 편입 — 쿼리/태그를 key로. 단일 콘텐츠 식별자는 아님(집계 페이지).
x.com/intent/post?text=... — unknownokhigh공유 버튼 인텐트 링크는 콘텐츠 자체가 아니라 작성 UI 진입점이므로 식별자 후보가 없는 게 맞음. 'intent' 예약어 처리로 올바르게 unknown.✅ 식별자 없음이 맞음. 다만 unknown에 묻지 않고 x_intent로 분리해 의심 플래그.
x.com/i/{tool}?param=... (예: Grok) — unknownokhighi-분기가 communities/status 외엔 전부 unknown으로 떨어지는 게 현재 설계이고, 이런 도구 링크는 계정/트윗/커뮤니티 어느 것도 아니라 sourceKey 후보 자체가 없음. 올바른 미분류.✅ 도구 페이지라 식별자 없음 — unknown 유지. 기존 제안 유지.
x.com/i/user/{numericId} — 프로필(웹지식 보완, 관측 0건)wronglow라이브 테스트로 검증 불가(표본에 없음, 별도 요청 생략 — 관측 0건이라 실사용 영향 낮다고 판단). 코드 확인 결과 classifyX의 i-분기는 communities/status만 매칭하므로 이 패턴은 unknown으로 떨어져 유효한 프로필 URL의 numericId를 놓침. 다만 numericId는 x_profile이 이미 쓰는 handle-lookup 엔드포인트(getUserByUserName)와 다른 엔드포인트가 필요해 단순 라우팅 추가로는 안 되고 fetcher 확장이 동반돼야 함 — 관측 0건이므로 우선순위는 낮되, 향후 실데이터에 나타나면 놓친 채로 unknown 누적될 gap으로 플래그.✅ numericId가 곧 canonical 조인키 — 오히려 이 경로가 표준형. x_profile로 편입.
x.com/{handle}/with_replies, /media, /likes — 프로필 하위탭(웹지식 보완, 관측 0건)okmedium라이브 테스트로 직접 검증하지 않았으나 코드 로직상 명확: classifyX는 segments[0]만 handle로 보고 segments[1](with_replies 등)은 status 위치탐색에도 안 걸리므로 무시되어 x_profile/handle로 정확히 수렴 — 이미 검증된 x_profile 패턴과 동일 코드경로이므로 id resolve는 안전. 하위탭 구분값 자체가 손실되지만, sourceKey(핸들)가 잘못된 식별자가 되는 것은 아니므로 id-fit 관점에서는 ok. (정보손실은 field-necessity 판정 대상이지 id-fit 결함 아님)✅ 하위탭 무시하고 handle 추출 후 숫자 id로 resolve. 기존 제안 유지.
커버리지 갭 — x.com/i/user/{numericId} — 정식 프로필 접근 패턴이 i-분기 미대응으로 unknown 처리됨(관측 0건, 실사용 여부 미확인 상태에서 우선순위 낮음). 채택 시 라우터 확장뿐 아니라 SDK/fetcher의 숫자ID 기반 유저 조회 지원 여부부터 확인 필요.
커버리지 갭 — XFetcher.ts 상단 주석이 'x_community 는 여기 안 옴'이라 되어 있으나 실제로는 전용 XCommunityFetcher가 별도 처리 — 라우팅 자체는 정상이나 문서 drift로 향후 코드 리더 혼선 소지.

3. Fetch 테스트 + 획득 방법

3.1 획득 사다리 (공짜 → 유료)

방법비용설명트레이드오프conf
확정 ✅ (2026-07-21)사용자 선택 완료 — x_profile→vendor:twitterapi.io ($0.18/1000 profile (벤더 공지 기준, 2026 재검색으로 재확인됨)) · x_tweet→vendor:twitterapi.io ($0.15/1000 tweet (벤더 공지, 2026 재검색 재확인)) · x_community→vendor:twitterapi.io (20 credits/호출 = $0.20/1000 community (twitterapi.io 크레딧 환산, tweet/profile 대비 훨씬 비쌈 — fetcher 주석에도 '20 credits + 느림' 명시)) adopted$0.18/1000 profile (벤더 공지 기준, 2026 재검색으로 재확인됨) / $0.15/1000 tweet (벤더 공지, 2026 재검색 재확인) / 20 credits/호출 = $0.20/1000 community (twitterapi.io 크레딧 환산, tweet/profile 대비 훨씬 비쌈 — fetcher 주석에도 '20 credits + 느림' 명시)Phase A 후보 사다리에서 사람이 선택한 확정 source. §4 통합 필드 뷰는 이 source의 실제 스키마 기준.신규 채택 source는 현행 코드 미구현분이 §4에서 parseStatus=dropped(구현 필요)로 표시됨. 실제 코드 전환·스키마 검증은 반영 단계에서.high
1 (무료/공식-경량)X oEmbed (publish.x.com/oembed?url=...) 인증 불필요, 완전 무료 — 그러나 우리 필요 필드(팔로워수/계정생성일/인증여부/좋아요·리트윗 카운트) 자체가 응답에 없음. 반환은 HTML embed snippet + author_name 정도뿐이라 프로필/참여수 통계 목적에는 사실상 무용$0field 부재로 우리 요구사항(팔로워·생성일·인증, 참여수)을 채울 수 없음 → 후보 탈락에 가까움. 2026년 들어 embed 안정성 저하 보고도 있음(벤더 페이지 출처라 신뢰도 낮게 볼 것)low (요구 필드 커버리지 자체가 부족해 실사용 불가 판정)
2 (무료/비공식)비공식 guest-token GraphQL 스크래핑 (snscrape/Twint/Nitter 계열) 2026-01-15자 X ToS가 '어떤 형태·목적이든 크롤링/스크래핑 금지'를 명문화했고, snscrape/Twint/Nitter는 이미 죽은 프로젝트로 다수 소스가 보고. guest token은 브라우저 핑거프린트 바인딩 + 데이터센터 IP 영구밴, doc_id/rate-limit이 2~4주 단위로 회전해 유지보수 비용이 큼명목상 $0이나 ToS 위반 시 100만 게시물당 $15,000 청산손해배상 조항 + 계정/IP 밴 리스크 존재(민사, 형사 아님)무료지만 저볼륨 온디맨드 백엔드 용도로도 권장 불가 — 법적/운영 리스크가 절감액 대비 과도. 채택 비권장low (다수 독립 소스가 '2026 현재 사실상 소멸' 로 수렴, 벤더 아닌 3rd-party 기술 블로그 교차 확인됨)
3 (공식 유료)X API v2 (공식, pay-per-use) 2026-02 기준 무료 티어 폐지, 신규 가입자는 구독 티어(Basic $200/mo, Pro $5,000/mo) 가입 불가 — pay-per-use 또는 Enterprise($42k/mo~)만 가능. 신규 백엔드라면 pay-per-use가 유일한 현실적 옵션게시물 읽기 ~$0.005/read(월 2백만 read 상한), 게시물 작성 $0.015~0.020, 사용자 프로필 조회 ~$0.010/request공식이라 스키마·SLA 신뢰도는 최고지만, 우리가 필요한 팔로워/생성일(GET users)과 게시물 참여수(GET tweets)를 합치면 twitterapi.io 대비 수십 배 비쌈. 저볼륨 온디맨드엔 감당 가능한 수준이나, x_community 같은 비공식 엔드포인트(커뮤니티 info)는 공식 API에 아예 없음 — 현재 x-community.fetcher.ts 기능은 공식 API로 대체 불가medium (가격대는 여러 소스 수렴하나 원 소스 직접 대조 필요, community info 등 세부 엔드포인트 커버리지 미검증)
4 (서드파티 유료 — 현재 구현)twitterapi.io (현재 XFetcher/XCommunityFetcher가 실제로 쓰는 SDK, TWITTER_API_SDK) 이미 프로덕션에서 사용 중. /twitter/user/info(x_profile: userName·createdAt·followers·following·statusesCount·isVerified·isBlueVerified 등), /twitter/tweets(x_tweet: text·createdAt·likeCount·retweetCount·replyCount·viewCount·author.followers 등 + quoted/retweeted 중첩), /twitter/community/info(x_community: memberCount·creatorUserName·createdAt, 20 credits/호출로 명시적으로 비쌈+느림, XCommunityFetcher 주석에 '20 credits/호출 + 느림' 언급). API-Key 헤더 인증(X-API-Key), 공식 X API 아님 — twitterapi.io가 백엔드에서 어떤 방식으로 데이터를 확보하는지는 비공개(자체 인프라로 X 접근을 대행)벤더 공지 기준 tweet $0.00015(1000건당 $0.15), profile $0.00018(1000건당 $0.18), followers/following $0.00001~0.00003/건 — 공식 API 대비 훨씬 저렴하나 이는 twitterapi.io 자체 블로그/가격표 출처라 비영리적으로 검증 불가이미 통합돼 있어 전환비용 없음, 우리가 필요한 3개 항목(프로필/트윗/커뮤니티)을 모두 단일 SDK로 커버, x_community처럼 공식 API엔 없는 엔드포인트도 제공. 반면 X 공식 파트너가 아닌 제3자 재판매 서비스라 (a) X 측 정책 변경/차단 시 서비스 중단 리스크, (b) 데이터 최신성·정확성이 X 공식 SLA 없이 벤더 자체 신뢰도에 의존, (c) 트윗 entities 필드 생략 등 실제로 코드 내 방어 파싱 주석(twitter-api.sdk.ts:362-363)이 존재 — 응답 스키마 불안정성이 실사용에서 이미 관측됨high for '현재 실제로 이렇게 동작 중'(코드로 직접 확인), medium for 가격/신뢰성(벤더 공지 의존, 제3자 검증 부족)
5 (서드파티 유료 — 대안)Apify X/Twitter 스크레이퍼 actor군 (예: apidojo/tweet-scraper, xquik/x-tweet-scraper, danek/twitter-scraper-ppr) 미채택. actor별로 pay-per-result 과금이며 프로필+트윗 필드를 함께 제공하는 actor도 존재(apidojo/tweet-scraper-lite 등)$0.15~$0.40/1000 tweets (actor마다 상이, xquik $0.15/1000 row, apidojo Tweet Scraper V2 $0.40/1000)twitterapi.io와 유사하게 X 비공식 스크래핑을 대행하는 구조라 ToS 그레이존 리스크는 동일하게 존재. Apify 마켓플레이스라 actor 품질/유지보수가 개인 개발자별로 편차 큼(스키마 변경 시 대응 속도 불확실), community info 같은 특수 엔드포인트 지원 여부는 actor마다 다르므로 개별 검증 필요. 현재 twitterapi.io 대비 구조적 이점이 뚜렷하지 않아(가격 비슷~더 비쌈, 통합 재작업 필요) 전환 근거 약함low-medium (가격은 Apify 마켓플레이스 페이지 직접 확인이나, 실제 반환 필드 커버리지·안정성은 미검증)

3.2 공식 API 상세

확신도 medium — 가격 수치는 다수 3rd-party 블로그가 수렴하나 X 공식 pricing 페이지(docs.x.com/x-api/getting-started/pricing) 원문 대조가 필요, 벤더(twitterapi.io) 자체 비교 주장은 배제하고 판단함 — 공식 X API v2는 무료 티어가 2026년 폐지되어 신규 백엔드는 pay-per-use만 선택 가능하며, 현재 우리가 x_community에 쓰는 '커뮤니티 정보' 조회는 공식 API에 대응 엔드포인트가 없어 twitterapi.io 계열 서드파티 없이는 대체 불가
단계내용conf
개발자 계정 생성 및 프로젝트/앱 등록 (developer.x.com)OAuth 2.0 App 생성, Bearer Token 발급high
pay-per-use 과금 등록 (구독 티어 Basic/Pro는 신규 가입 불가, legacy만 유지)결제수단 등록 후 크레딧 기반 과금 시작. 무료 크레딧/트라이얼 유무는 원문 미확인medium
GET /2/users/by/username/:username 로 프로필(팔로워·생성일·인증) 조회약 $0.010/request로 추정(3rd-party 집계), 정확한 endpoint별 단가는 X 공식 pricing表 대조 필요medium
GET /2/tweets/:id (public_metrics 확장) 으로 게시물(작성자·게시일·참여수) 조회약 $0.005/read, 월 2백만 read 상한 언급되나 상한 초과 시 정책 미확인medium
x_community(커뮤니티 info)에 해당하는 공식 엔드포인트 없음 → 이 부분만은 서드파티(twitterapi.io 등) 유지 필요XCommunityFetcher 기능을 공식 API로 완전 대체 불가high

3.3 라이브 테스트 실측

대상방법과금결과비고
x_profile (X/Twitter user profile) — handle UntaxedSolanaGET https://api.twitterapi.io/twitter/user/info?userName=UntaxedSolana (twitterapi.io, X-API-Key header) — exact endpoint used by XFetcher.fetchProfile via TwitterApiSdk.getUserByUserNamesuccess (HTTP 200, status:success)먼저 samples/x_profile.json 1위 handle(nvidiamoonbsc)로 시도했으나 계정 정지(Suspended)로 data.unavailable=true 반환 → 동일 샘플의 2번째 handle(UntaxedSolana)로 재시도해 성공. XFetcher.profileData()가 실제 사용하는 필드(userName/name/id/createdAt/followers/following/statusesCount/isVerified/isBlueVerified/description)는 모두 원문에 존재·일치. followers(836)/statusesCount(836)는 샘플 스냅샷(followers 812) 대비 증가 — mutable 표시와 일치. 원문에 entities/protected/verifiedType/favouritesCount/mediaCount/coverPicture/profilePicture/canDm/pinnedTweetIds 등 fetcher가 버리는 필드 다수 존재.
x_profile — handle nvidiamoonbsc (동일 endpoint, suspended 케이스)GET https://api.twitterapi.io/twitter/user/info?userName=nvidiamoonbscsuccess (HTTP 200) but data는 unavailable 페이로드 — 계정 정지response.data.status==='success'이고 data 자체는 non-null(unavailable 객체)이라, XFetcher.fetchProfile()의 not_found 분기(!user)를 안 타고 profileData()가 이 unavailable 객체를 TwitterUser로 강제 캐스팅해 그대로 진행할 위험 있음(user.createdAt이 undefined라 .toISOString() 호출 시 TypeError → catch에서 status='error'로 흡수될 가능성). 즉 '탈퇴/정지 계정'은 twitterapi.io가 data:null이 아니라 별도 unavailable 쉐입으로 반환 — SDK/파서가 이 쉐입을 감지하지 않음(코드 확인: transformUser는 raw.createdAt 존재를 가정).
x_tweet — id 2070103975295131919 (samples/x_tweet.json 1위)GET https://api.twitterapi.io/twitter/tweets?tweet_ids=2070103975295131919 — XFetcher.fetchTweet → TwitterApiSdk.getTweetsByIdssuccess (HTTP 200, status:success)createdAt은 twitterapi.io RFC2822류 문자열('Thu Jun 25 11:17:03 +0000 2026')로 반환되며 SDK가 new Date(raw.createdAt)로 파싱 — 정상 파싱됨(fixed 필드 확인). viewCount(1157) > 샘플 스냅샷(1003), likeCount(1)=스냅샷과 동일 → mutable/fixed 표기와 부합. entities.hashtags가 최상위에 없고 raw.entities에 hashtags 키가 아예 빠져있는 경우가 실측됨(코드 주석 x-community.fetcher.ts:362-363의 방어적 파싱 근거가 실물 응답에서도 재현). quoted_tweet/retweeted_tweet=null(이 트윗은 단순 reply)이라 nestedTweetData 분기는 이번 호출로는 검증 못함(다른 트윗 필요).
x_community — id 1952999882635264271 (samples/x_community.json 1위, 원 샘플은 status:'skipped_paid'로 무료 스냅샷 자체가 없었음)GET https://api.twitterapi.io/twitter/community/info?community_id=1952999882635264271 — XCommunityFetcher.fetch → TwitterApiSdk.getCommunityInfo (fetcher 주석상 20 credits/호출)success (HTTP 200, status:success)sourceKey(1952999882635264271, 라우터 classifyX의 /i/communities/<id> 규칙으로 추출)가 curl 요청의 community_id와 정확히 일치 — id-probe 불필요(이미 canonical numeric ID, 302 리다이렉트 없음: curl -sI 로 twitter.com/wendysbeefcom 및 x.com/i/communities/<id> 둘 다 200 직응답 확인, X 자체가 로그인월로 리다이렉트 대신 200+HTML 반환). 응답 최상위가 'community_info' 키(SDK가 community_info??data 양쪽 방어 중 community_info 분기 실사용 확인됨). description 필드에 토큰 컨트랙트 주소가 그대로 노출(21Xp...pump) — description 자체는 fetcher가 저장함(XCommunityFetcher.data()에 description 포함, 단 valueNature에는 없음 — nature 미분류 필드로 존재).
x_tweet 일괄 12건 — author.id 존재 여부 + quote 중첩 구조 확인 (문서의 기존 raw 덤프가 "..."로 절단돼 있어 재검증)GET https://api.twitterapi.io/twitter/tweets?tweet_ids=<12개 콤마구분> (X-API-Key)success (HTTP 200, 10건 반환 — 2건은 삭제/비공개 추정)3가지 확정: ①author.id가 추가 쿼리 없이 제공 → audit의 조건부 조인키가 id로 확정quoted_tweet의 키 집합이 최상위와 100% 동일하고 author도 완전 프로필 → audit이 요구한 "quote profile 연결"이 추가 fetch 0회로 성립 ③트윗 응답의 author가 3필드가 아니라 33키 완전 프로필 → 별도 x_profile 호출 상당 부분 불필요. 기존 문서 덤프가 "..."로 절단돼 있어 ①③을 놓치고 있었음(IG alt·TikTok isSlideshow와 동일 패턴).

x_profile (X/Twitter user profile) — handle UntaxedSolana 실측 응답 원문:

{"status":"success","msg":"success","data":{"id":"2020402210912509952","name":"Untaxed Wallet","userName":"UntaxedSolana","location":"Solana","url":"https://t.co/PL4ISRF61C","description":"The Zero-Fee Solana Trading Terminal...","entities":{...},"protected":false,"isVerified":false,"isBlueVerified":true,"verifiedType":null,"followers":836,"following":148,"favouritesCount":2499,"statusesCount":836,"mediaCount":212,"createdAt":"2026-02-08T07:40:06.000000Z","coverPicture":"https://pbs.twimg.com/profile_banners/...","profilePicture":"https://pbs.twimg.com/profile_images/...","canDm":true,"affiliatesHighlightedLabel":{},"isAutomated":false,"automatedBy":null,"pinnedTweetIds":["2069843870503035217"]}}

x_profile — handle nvidiamoonbsc (동일 endpoint, suspended 케이스) 실측 응답 원문:

{"status":"success","msg":"success","data":{"unavailable":true,"message":"User is suspended","unavailableReason":"Suspended"}}

x_tweet — id 2070103975295131919 (samples/x_tweet.json 1위) 실측 응답 원문:

{"tweets":[{"type":"tweet","id":"2070103975295131919","url":"https://x.com/EvanKirstel/status/2070103975295131919","text":"@hokkokushimbun https://t.co/TzLGKwDbyW","retweetCount":0,"replyCount":0,"likeCount":1,"quoteCount":0,"viewCount":1157,"createdAt":"Thu Jun 25 11:17:03 +0000 2026","lang":"qme","isReply":true,"author":{"userName":"EvanKirstel","followers":379783,"createdAt":"Sat Apr 25 12:45:16 +0000 2009",...},"extendedEntities":{"media":[...]},"entities":{"user_mentions":[...]},"isRetweet":false,"isQuote":false,"quoted_tweet":null,"retweeted_tweet":null,"communityInfo":null}],"status":"success","msg":"success","code":0}

x_community — id 1952999882635264271 (samples/x_community.json 1위, 원 샘플은 status:'skipped_paid'로 무료 스냅샷 자체가 없었음) 실측 응답 원문:

{"community_info":{"id":"1952999882635264271","name":"Sheep Wif Hat","description":"Sheep wif hat $SWIF (36M ATH) relaunch on USDC 21Xp1ukfzY6dWFYFRJmp59iCgKDJ61CHMJ8dmG9npump","member_count":4720,"moderator_count":11,"created_at":"Wed Aug 06 07:47:11 +0000 2025","join_policy":"Open","is_nsfw":false,"primary_topic":{"id":null,"name":null},"creator":{"id":"1283966981297680384","screen_name":"jayxbt2012","followers_count":14171,"created_at":"Fri Jul 17 03:31:02 +0000 2020",...},"admin":{...},"members_preview":[...]},"status":"success","msg":"success"}

x_tweet 일괄 12건 — author.id 존재 여부 + quote 중첩 구조 확인 (문서의 기존 raw 덤프가 "..."로 절단돼 있어 재검증) 실측 응답 원문:

author.id 10/10 존재: 35203319(EvanKirstel), 15438913(DailyMail), 973261472(blknoiz06), 1863645172669595648(jncquant), 1720665183188922368(grok), 1812513621903306752(jupiter_trade) …
quote 사례 id=2070140594496626759 (isQuote=true) → quoted_tweet.id=2070130818232787327, quoted_tweet.author={id:1534395199958306816, userName:trenchdiver, followers:8717, createdAt:"Wed Jun 08 04:41:45 +0000 2022"}, quoted 참여수 like15/view2903/quote2/bookmark6
author 객체 키 33개: id,userName,name,description,location,createdAt,followers,following,statusesCount,mediaCount,favouritesCount,isVerified,isBlueVerified,verifiedType,profilePicture,coverPicture,pinnedTweetIds,profile_bio,status,canDm,isAutomated,automatedBy,entities,…
트윗 최상위 신규 키: conversationId, inReplyToId/UserId/Username, card(2/10), communityInfo(0/10 non-null), place, isPinned, twitterUrl, type
entities.urls[].expanded_url 확인: "https://x.com/trenchdiver0x/status/2070130818232787327"
twitterapi.io는 정지/탈퇴 계정을 GET /twitter/user/info 에서 HTTP 200 + status:'success' + data:{unavailable:true, message, unavailableReason} 형태로 반환한다(정상 유저 필드는 전부 없음). XFetcher.fetchProfile()의 not_found 분기는 !user(즉 data가 null/undefined)만 체크하므로 이 케이스를 통과시켜 profileData(user)가 unavailable 객체를 정상 TwitterUser로 취급, user.createdAt.toISOString() 호출 시 TypeError가 발생할 개연성이 높다(실제 catch에서 status='error'로 흡수되긴 하지만, not_found가 더 적확한 상태이며 로그 상 원인 파악이 어려워짐). raw JSON payload를 실측 확보했으므로 필요시 회귀 재현 가능.
twitterapi.io 응답 원문에 fetcher가 저장하지 않는 필드가 다수 존재(x_profile: entities/protected/verifiedType/favouritesCount/mediaCount/coverPicture/profilePicture/canDm/pinnedTweetIds/location/url, x_tweet: entities/extendedEntities/lang/source/url/twitterUrl/quoteCount/bookmarkCount, x_community: question/invites_policy/is_pinned/role/banner_url/search_tags/rules/members_preview). 현재 스키마 설계 범위 밖일 수 있으나, 요구사항 확장 시(예: 미디어 유무, 언어 판별) 별도 fetch 없이 이미 응답에 있는 값을 놓치고 있을 가능성 — 기능 요청 시 재확인 권장.

4. 객체별 필드 통합 (변동성 · 파싱상태 · 트레이딩 유용성) 👤 판단 반영

Creator/Content 객체별로 필드를 한 표에 통합. 값 변동성 = 값이 시간에 따라 변하나(immutable 역사적 사실 / mutable 드리프트 / derivable 계산값) → look-ahead 안전성이 여기서 도출(immutable=safe · mutable=conditional/as-of · derivable=unsafe). 파싱 상태 = 현재 파이프라인이 실제로 저장하나.

4.1 · Creator (프로필) — x (profile)

source: twitterapi.io GET /twitter/user/info (XFetcher.fetchProfile, 현행 프로덕션 파싱 중) · mutable 값은 twitterapi.io 조회 시점 스냅샷 — 진입시점 값과 다를 수 있어 as-of 재계산 필요(raw+timestamp 저장). immutable(createdAt 등)은 언제 재조회해도 동일값이라 look-ahead safe. userName은 X 정책상 개명 가능해 장기적으론 mutable에 가까움(현 코드는 fixed로 취급 중 — 갭).
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
userName핸들(@ 없이). 예: 'UntaxedSolana'mutable✅ 캡처keepcontext — 팀진정성 축, 사칭계정 대조·다른 필드 조회용 join key. 개명 가능해 영구 anchor 아님
id (숫자 유저ID)계정 불변 내부 식별자. 예: '2020402210912509952'immutable✅ 캡처🔑 keynoise — 화면에 비노출 join키. 단 userName 개명 대비 안정 anchor로서 내부적으로 유용
name (표시이름)자유 변경 가능한 닉네임. 예: 'Untaxed Wallet'mutable✅ 캡처keepcontext — 팀진정성. 유명 프로젝트/인플루언서 이름 도용 탐지 보조
createdAt (계정 생성일)계정 최초 생성 시각. 예: '2026-02-08T07:40:06.000000Z'immutable✅ 캡처keepactionable — 러그리스크. 런칭 직전 급조 계정 vs 수년된 계정을 이진으로 가르는 1차 tell
followers팔로워 수. 예: 836mutable✅ 캡처keepactionable — 모멘텀, 도달 규모. 팔로워 매입 흔해 단독신뢰 낮으나 10 vs 10만은 즉시 판단을 바꿈(as-of 주의)
following팔로잉 수. 예: 148mutable✅ 캡처keepcontext — 러그리스크. followers 대비 비율로 봇팜 의심 정도의 배경정보
statusesCount누적 트윗 수(리트윗 포함). 예: 836mutable✅ 캡처keepcontext — 러그리스크. createdAt과 결합해 '신규+게시물 0~수건' 빈 계정 탐지
isVerified (레거시 인증)구 X 자체 심사 배지. 예: falsemutable✅ 캡처keepnoise — 사실상 폐지된 개념, 현역 트레이더 참고 안 함
isBlueVerified (구독 배지)X Premium 유료 구독 배지. 예: truemutable✅ 캡처keepcontext — 팀진정성. 돈으로 사는 배지라 약한 신호, 없는 것보단 낫다는 정도
description (바이오)소개글. 예: 'The Zero-Fee Solana Trading Terminal...'mutable✅ 캡처keepactionable — 러그리스크. CA 복붙·과거 다수 프로젝트 홍보 흔적이면 즉시 경계, 일관된 서사면 진정성 가점
unavailable / message / unavailableReason정지·탈퇴 계정일 때만 등장하는 별도 응답 쉐입(정상 필드 전부 부재). 예: {unavailable:true, unavailableReason:'Suspended'}mutable⚠️ actor제공·미파싱keepactionable — 러그리스크. 런칭 후 계정 정지=팀 이탈 강신호. 단 현재 코드가 이 쉐입을 감지 못해 정상유저로 캐스팅 시도(TypeError→error로 흡수) — 신호 신뢰의 전제조건이 코드 갭
entities / protected / verifiedType / favouritesCount / mediaCount / coverPicture / profilePicture / canDm / pinnedTweetIds / location / url응답에 존재하나 fetcher 미사용. profilePicture(재사용 아바타 역추적)·pinnedTweetIds(고정 CA 홍보글)는 트레이더가 실제 확인하는 항목mutable⚠️ actor제공·미파싱skipcontext — 대부분 UI 메타(noise)이나 profilePicture/pinnedTweetIds는 actionable 잠재력(현재 미채택이라 활용 불가)
(accountAgeHours, engagementRate 등 파생값)createdAt/followers 등 raw로부터 우리가 계산할 파생 지표derivable❌ 없음skipnoise — 저장 금지, raw+timestamp만 저장 후 as-of 재계산(look-ahead 함정)

4.2 · Content (트윗) — x (tweet)

source: twitterapi.io GET /twitter/tweets (XFetcher.fetchTweet, 현행 프로덕션 파싱 중, quote/retweet 중첩 재귀 파싱 포함) · engagement(mutable)는 조회 시점 스냅샷(as-of 주의, raw+timestamp 저장 후 재계산). text/createdAt/isQuote 등 immutable은 언제 재조회해도 동일해 look-ahead safe. quoted_tweet/retweeted_tweet은 실측 샘플에서 둘 다 null이라 실제 파싱 신뢰도 미검증.
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
id (tweetId)트윗 숫자 ID. 라우터 sourceKey(URL의 status/{tweetId})와 실측 일치 — 문서 objectFields에 행 자체가 누락돼 있었음(완료 rubric 9b에서 발견)immutable✅ 캡처🔑 keycontext — 콘텐츠 dedup/조인 키, 그 자체로 매매결정 안 바꿈
text트윗 본문. 예: '@hokkokushimbun https://t.co/...'immutable✅ 캡처keepactionable — 모멘텀/타이밍. 내러티브·톤(밈 vs 스캠문구)·CA 언급이 콘텐츠재사용 탐지의 원본 텍스트
createdAt (게시 시각)트윗 작성일. 예: 'Thu Jun 25 11:17:03 +0000 2026'immutable✅ 캡처keepactionable — 타이밍. 원본(1st-mover) vs 며칠 지난 카피캣 재활용 구분의 핵심 축
likeCount / retweetCount / replyCount / viewCount참여 지표. 예: likeCount:1, viewCount:1157mutable✅ 캡처keepactionable — 모멘텀. engagement velocity는 실시간 하이프 게이지, view대비 like 비율로 봇확산 판별
quoteCount / bookmarkCount인용 수/북마크 수. 응답엔 있으나 fetcher 미사용mutable⚠️ actor제공·미파싱keepcontext — 모멘텀. '조용히 지켜보는 관심' 보조지표 잠재력, 현재 미수집이라 실사용 불가
author.userName / author.createdAt / author.followers작성자 핸들/계정나이/팔로워 스냅샷. 예: 'EvanKirstel' / 2009년 / 379783mutable✅ 캡처keepactionable — 모멘텀/러그리스크. KOL발 vs 방금만든 무명계정 스팸을 즉시 가르는 1차 필터
isQuote / isRetweet인용/순수리트윗 여부 플래그immutable✅ 캡처keepcontext — 타이밍. 능동(인용) vs 수동(단순RT) 확산 성격 구분, 원본 추적 스위치 역할
quoted_tweet / retweeted_tweet (중첩 원본)인용/리트윗 대상 원본 트윗(text/createdAt/authorUserName 등 재귀 포함, 둘 다 null 가능)immutable✅ 캡처keepactionable — 타이밍. 카피캣 체인의 진짜 root 추적(as-of 카피=상한 신호)에 직접 쓰이나 실측 샘플에서 둘 다 null이라 파싱 신뢰도 미검증
entities.hashtags / entities.urls / entities.user_mentions해시태그/링크/멘션 배열. 일부 응답은 hashtags 키 자체 생략(방어파싱 근거)immutable⚠️ actor제공·미파싱keepcontext — 러그리스크. urls 안에 드레이너 링크·CA 숨어있을 수 있으나 fetcher 미사용이라 현재 활용 불가
extendedEntities.media / lang / source / url / twitterUrl첨부 미디어/언어/클라이언트/트윗URL(url·twitterUrl은 라우터 sourceUrl과 중복)immutable⚠️ actor제공·미파싱skipcontext — 모멘텀. 밈코인은 이미지/짤 바이럴이 텍스트만큼 중요한데 media가 완전히 버려짐(캡처되면 actionable급)
(engagementRate, copyDistanceRank 등 파생값)likeCount/viewCount 비율, 원본 대비 재사용 순번 등 우리가 계산할 파생 지표derivable❌ 없음skipnoise — 저장 금지, raw+timestamp만 저장 후 as-of 재계산(look-ahead 함정, 카피캣 원칙 §content-reuse-direction 참고)
author.id (creator 링크)트윗 응답에 동봉된 작성자 숫자 유저ID — 조인 성립의 필수 조건. 실측 10/10 전부 존재immutable⚠️ actor제공·미파싱🔑 keycontext — 조인키 용도, 그 자체로 매매결정 안 바꿈
author (프로필 전체 객체)2026-07-29 실측 신규 확인 — 문서가 3개 필드만 기록했으나 실제로는 33키 완전 프로필(name/description/location/statusesCount/mediaCount/favouritesCount/isBlueVerified/profilePicture/pinnedTweetIds/profile_bio/status). 트윗 1콜로 Creator 레코드 완성 가능mutable⚠️ actor제공·미파싱keepactionable — 별도 프로필 유료 호출 없이 러그tell(계정연령·바이오·팔로워)을 확보. ⚠ 스냅샷 시각이 "트윗 조회 시점"이라 as-of 기록 필수
entities.urls[].expanded_url2026-07-29 실측 신규 확인. t.co 축약링크의 원본 URL(실측: 인용 대상 트윗 URL로 확장됨)immutable⚠️ actor제공·미파싱keepactionable — 드레이너 도메인·CA·크로스플랫폼 링크가 여기 드러남. 문서가 "urls 안에 드레이너 링크·CA 숨어있을 수 있다"고 본 바로 그 지점
communityInfo2026-07-29 실측 신규 확인. 트윗이 속한 커뮤니티 정보(실측 10건은 전부 null이나 필드 존재)immutable⚠️ actor제공·미파싱keepcontext — Content↔Venue 직접 링크. 현재 Venue(커뮤니티)는 자기 자신 레코드만 있고 콘텐츠와 이어지는 경로가 없었음
conversationId / inReplyToId / inReplyToUserId / inReplyToUsername2026-07-29 실측 신규 확인. 스레드 root id와 답글 체인(실측 10건 중 4건이 답글)immutable⚠️ actor제공·미파싱keepcontext — 여러 계정이 같은 스레드에 몰리는 조율 패턴 탐지 + 원본/파생 구분
card / place / isPinned / twitterUrl / type2026-07-29 실측 신규 확인. card=링크 프리뷰 카드(2/10 존재), place=지오태그(0/10), isPinned=고정 트윗, twitterUrl=canonical URLimmutable⚠️ actor제공·미파싱skipnoise — 약한 보조 신호. card는 앱스토어/외부링크 메타이나 expanded_url과 상당 부분 중복

4.3 · Venue (커뮤니티) — x (community)

source: twitterapi.io GET /twitter/community/info (XCommunityFetcher, 20 credits/호출 — 상시 파이프라인 아닌 러그 의심 시 온디맨드 보강용으로 좁혀 채택) · member_count 등 mutable은 조회 시점 스냅샷(as-of 주의). created_at·creator 등 immutable은 look-ahead safe. 관측 137건 중 다수가 skipped_paid — '신호가 있어도 실제로 못 얻는 경우'가 흔해, 필드값 존재 자체가 이미 조건부.
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
id커뮤니티 숫자ID. 예: '1952999882635264271'immutable✅ 캡처🔑 keynoise — 내부 join키, 트레이더가 직접 보는 값 아님
name커뮤니티명. 예: 'Sheep Wif Hat'immutable✅ 캡처keepcontext — 팀진정성. 토큰명/티커와 브랜딩 일치 여부 대조
member_count멤버 수. 예: 4720mutable✅ 캡처keepactionable — 모멘텀. followers와 동일 역할의 도달 규모 지표(as-of 주의). 단 유료게이트로 값 자체 확보율 낮음
moderator_count모더레이터 수. 예: 11mutable✅ 캡처keepcontext — 팀진정성. 운영진 규모는 활동성 배경정보, 단독 결정력 낮음
created_at (커뮤니티 개설일)커뮤니티 생성 시각. 예: 'Wed Aug 06 07:47:11 +0000 2025'immutable✅ 캡처keepactionable — 러그리스크. 계정 생성일과 동일 논리, 런칭 직전 급조 커뮤니티 경계신호
creator.id커뮤니티 개설자 숫자 유저ID — Venue↔Creator 조인키. 응답 키가 userName/screen_name으로 불안정해 handle 폴백은 위험(실측: userName 키 부재, screen_name만 존재)immutable✅ 캡처🔑 keyactionable — 팀진정성. 개설자의 과거 러그 이력 추적 진입점. 응답 키 불안정(fallback 의존)이 신뢰도 마이너스
creator.userName / creator.screen_name개설자 handle — 응답에 따라 키 이름이 달라져 fallback 필요(실측 확인). 표시·대조용mutable✅ 캡처keepcontext — 개설자 과거 러그 이력 추적의 표시값(조인은 creator.id로)
admin.screen_name현재 관리자 핸들. 예: 'jayxbt2012'mutable✅ 캡처keepcontext — 팀진정성. 운영자 교체 이력=탈취/방치 판별 보조, creator보다 약함
description커뮤니티 소개글. CA(컨트랙트주소)가 그대로 노출되는 경우 관측됨mutable✅ 캡처keepactionable — 러그리스크. 토큰-커뮤니티 매칭·사칭 커뮤니티 판별 직접 원료(단 코드상 look-ahead 안전성 미분류 갭)
join_policy / is_nsfw / primary_topic가입정책(Open 등) / 성인콘텐츠 플래그 / 주제분류. fetcher가 저장하나 valueNature 미선언(구현 갭)mutable✅ 캡처removenoise — join_policy만 약한 진정성 보조, is_nsfw/primary_topic은 매매판단축과 무관
question / invites_policy / is_pinned / role / banner_url / search_tags / rules / members_preview가입질문·초대정책·고정여부·조회자role·배너·태그·규칙·상위멤버 미리보기mutable⚠️ actor제공·미파싱removenoise — 아카이브성 UI 메타데이터, 몇 초 내 진입/패스를 결정하는 밈 트레이더 워크플로우엔 거의 안 쓰임
(communityAgeHours, memberGrowthRate 등 파생값)created_at/member_count로부터 우리가 계산할 파생 지표derivable❌ 없음skipnoise — 저장 금지, raw+timestamp만 저장 후 as-of 재계산(look-ahead 함정)

4-부록. actor raw 응답 스키마 (ground truth)

위 통합 표의 원천 — actor가 실제 반환하는 원본 필드 전체.

dataType: "x_profile (twitterapi.io GET /twitter/user/info)"

필드예시값설명
data.userNameUntaxedSolana핸들(fixed, fetcher 사용)
data.nameUntaxed Wallet표시이름(fetcher 사용, nature 미표기)
data.id2020402210912509952숫자 유저ID(fetcher 사용, nature 미표기)
data.createdAt2026-02-08T07:40:06.000000Z계정 생성일. ISO-like 문자열(마이크로초 포함, .000000Z), SDK가 new Date()로 파싱(fixed)
data.followers836팔로워 수(mutable, fetcher 사용)
data.following148팔로잉 수(mutable, fetcher 사용)
data.statusesCount836누적 트윗 수(mutable, fetcher 사용)
data.isVerifiedfalse레거시 인증(mutable, fetcher 사용)
data.isBlueVerifiedtrue블루체크(mutable, fetcher 사용)
data.descriptionThe Zero-Fee Solana Trading Terminal...바이오(mutable, fetcher 사용)
data.unavailable / data.message / data.unavailableReason{unavailable:true, message:'User is suspended', unavailableReason:'Suspended'}정지/탈퇴 계정일 때만 등장하는 별도 쉐입 — 정상 필드(userName 등) 전부 부재. SDK/fetcher가 이 쉐입을 감지하지 않고 정상 유저로 캐스팅 시도할 위험
data.entities / protected / verifiedType / favouritesCount / mediaCount / coverPicture / profilePicture / canDm / pinnedTweetIds / location / urlfavouritesCount:2499원문에 존재하나 fetcher(profileData)가 버리는 필드

dataType: "x_tweet (twitterapi.io GET /twitter/tweets)"

필드예시값설명
tweets[0].text@hokkokushimbun https://t.co/TzLGKwDbyW본문(fixed, fetcher 사용)
tweets[0].createdAtThu Jun 25 11:17:03 +0000 2026작성일. Twitter 레거시 포맷 문자열(RFC2822류), new Date()로 파싱(fixed)
tweets[0].likeCount / retweetCount / replyCount / viewCount / quoteCount / bookmarkCountlikeCount:1, viewCount:1157참여수(mutable). quoteCount/bookmarkCount는 원문에 있으나 fetcher 미사용
tweets[0].author.userName / author.createdAt / author.followersEvanKirstel / Sat Apr 25 12:45:16 +0000 2009 / 379783작성자 핸들/계정생성일(fixed)/팔로워(mutable) — fetcher가 authorUserName/authorCreatedAt/authorFollowers로 매핑
tweets[0].isQuote / isRetweetfalse / falsefixed, fetcher 사용
tweets[0].quoted_tweet / retweeted_tweetnull중첩 원본 트윗(null 가능). 존재 시 재귀적으로 text/createdAt/authorUserName/authorCreatedAt/authorFollowers 추출(fetcher.nestedTweetData) — 이번 호출 샘플은 둘 다 null이라 실물 미검증
tweets[0].entities.hashtags / entities.urls / entities.user_mentionsentities:{user_mentions:[...]} (hashtags 없음)일부 트윗에서 하위 배열 자체가 생략됨(SDK 주석에 명시된 방어 파싱 근거, 이번 응답도 hashtags 키 자체 부재) — fetcher는 entities를 아예 사용 안 함
tweets[0].extendedEntities.media / lang / source / url / twitterUrlextendedEntities.media[0].type:'photo'원문에 있으나 fetcher(tweetData) 미사용
tweets[0].id2070140594496626759트윗 숫자 ID — 라우터 sourceKey와 일치
tweets[0].author.id1863645172669595648작성자 숫자 유저ID — creator 조인키(추가 쿼리 불필요)
tweets[0].author (33키 완전 프로필)description, statusesCount …name/description/location/statusesCount/mediaCount/favouritesCount/isBlueVerified/profilePicture/pinnedTweetIds/profile_bio/status 등 — 문서가 3필드만 기록했던 부분
tweets[0].quoted_tweet.* (최상위와 동일 스키마)id=2070130818232787327, author.id=1534395199958306816인용 트윗 — id/author(완전 프로필)/참여수 전부 포함, quoted_tweet 필드도 재귀 존재
tweets[0].entities.urls[].expanded_urlhttps://x.com/trenchdiver0x/status/2070130818232787327t.co 축약링크의 원본 URL
tweets[0].conversationId / inReplyToId / inReplyToUserId / inReplyToUsername10건 중 4건이 답글스레드 root + 답글 체인
tweets[0].communityInfonull (10/10)트윗이 속한 커뮤니티 — Content↔Venue 링크
tweets[0].card / place / isPinned / twitterUrl / typecard 2/10 존재2026-07-29 실측 신규 확인(미채택)

dataType: "x_community (twitterapi.io GET /twitter/community/info)"

필드예시값설명
community_info.id1952999882635264271커뮤니티 숫자ID, nature 미표기(fetcher.data()엔 포함되나 valueNature 목록엔 없음)
community_info.nameSheep Wif Hatfixed(fetcher 사용)
community_info.member_count4720mutable(fetcher: memberCount)
community_info.moderator_count11mutable(fetcher: moderatorCount)
community_info.created_atWed Aug 06 07:47:11 +0000 2025fixed(fetcher: createdAt). 이번 응답은 레거시 문자열 포맷 — SDK의 parseFlexibleDate가 unix/ISO/문자열 모두 방어 파싱
community_info.creator.userName / creator.screen_name / creator.id / creator.id_strscreen_name:'jayxbt2012'fixed(fetcher: creatorUserName/creatorId). 이번 응답은 userName 키가 없고 screen_name만 존재 — SDK의 ?? fallback 체인이 실사용됨(raw.creator?.userName ?? raw.creator?.screen_name)
community_info.admin.screen_namejayxbt2012mutable(fetcher: adminUserName)
community_info.description / question / join_policy / invites_policy / is_nsfw / is_pinned / role / primary_topic / banner_url / search_tags / rules / members_previewdescription 안에 'CA: 21Xp...pump' 패턴 포함원문에 존재. description/joinPolicy/isNsfw/primaryTopic은 fetcher가 저장하나 valueNature 미분류. question/invites_policy/is_pinned/role/banner_url/search_tags/rules/members_preview는 fetcher 완전 미사용 — description 필드엔 토큰 컨트랙트 주소가 그대로 노출되는 경우 있음

다음 액션