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

Social Fetcher URL Structure Audit — 2026-07-19
관련 코드: social-fetcher.router.ts(classifyTiktok) · fetchers/tiktok.fetcher.ts
관련 데이터: samples/tiktok_profile.json · samples/tiktok_video.json · samples/tiktok_search.json
소스 채택 권장도 조건부 채택 — 밈 모멘텀은 여기 사는데(TikTok=주류 바이럴리티의 핵심 확산 채널) 현재 fetcher 매핑이 트레이더가 가장 원하는 두 축(러그리스크의 계정 생성일/인증여부)을 통째로 버리고 있어 반쪽짜리다.
playCount/shareCount/authorFans/isAd 등 모멘텀·광고tell 필드는 이미 확보돼 있어 실전 매매 판단에 바로 쓸 수 있다. 하지만 context.md가 명시한 러그리스크 1순위 tell(계정 신선도)과 팀진정성 tell(인증여부)이 raw response엔 존재하는데 tiktok.fetcher.ts가 authorMeta.createTime/verified를 파싱하지 않아 유실되고 있다 — 퀀트 관점의 '있는데 안 씀' 낭비가 아니라 트레이더 관점에선 '가장 쓰고 싶은 것부터 없다'는 문제. 또한 tiktok_profile 라우트가 실제 프로필 집계가 아니라 최신 영상 1건 스냅샷이라 채널 성장추세를 못 보는 구조적 약점, tiktok_search unsupported·단축링크 unresolved로 관측 273+104+49+8+7건 중 일부가 데이터 손실 상태다.
트레이더 관점: TikTok은 밈코인 판에서 X/Telegram 다음으로 실제 바이럴이 시작·증폭되는 채널이라 소스 자체는 채택할 가치가 충분하다(모멘텀 축이 명확히 존재). 다만 지금 코드 그대로면 트레이더는 '얼마나 터졌나'는 알 수 있어도 '이 계정이 진짜인지, 방금 만든 사칭 계정인지'는 알 수 없다 — 러그 판별이라는 트레이더의 1순위 질문에 답을 못 준다는 뜻. authorMeta.createTime/verified 매핑 추가가 field-necessity(퀀트)보다 trader-value 관점에서 더 시급한 우선순위다.
✅ 최종 결정 (수기 audit 반영)
원칙: sourceKey는 unique & immutable & linkable. TikTok handle은 30일마다 변경 가능하고 옛 handle이 타인에게 즉시 재할당되므로, handle을 키로 쓰면 서로 다른 두 계정이 한 키로 조용히 병합된다(분기보다 나쁜 충돌). → handle은 키 불가, 숫자 id가 canonical.
조인 키
객체비고
Creator (프로필)authorMeta.id (숫자 uid)유료 실측 3콜 전부에서 content 응답이 authorMeta.id를 추가 쿼리 없이 직접 포함 → 사용자 규칙 충족. handle(authorMeta.name)은 별도 기록하되 조인·dedup에 절대 사용 금지 — 표시용 + URL→handle→id 해석 경로용. 단 handle→id는 시점 종속(as-of): 재할당 때문에 "현재값 1건" 매핑으로 들고 있으면 과거 관측이 새 주인 id에 잘못 붙는다. content 행의 (authorHandle + createTimeISO + authorId)가 곧 handle 이력 로그이므로 별도 alias 테이블 불필요. ⚠ 유료 게이트로 미fetch 엔트리는 id 없이 handle만 남음 → resolved(실측) / inferred(handle 추정) 구분 필요.
Content (영상/포토)id (postId, 콘텐츠 자체) + authorMeta.id (creator 링크)postId=dedup 키이며 라우터 sourceKey와 실측 일치. postId가 authoritative — URL의 handle이 완전히 틀려도 apify·oEmbed 모두 정확한 게시물을 반환(실측). photo/video 병합 후 형식 구분은 isSlideshow로 복원.
sourceType 통합 맵
최종 타입포함 패턴
tiktok_profile/@{handle}, /@{handle}/live — router sourceKey=handle(alias). canonical=authorMeta.id로 resolve 후 조인
tiktok_video/@{h}/video/{id}, /@{h}/photo/{id}, /embed/v2/{id}(신규 편입) — key=postId. photo도 canonical URL이 /video/로 정규화됨(실측) = 동일 네임스페이스. embed는 fetcher가 /@x/video/{postId}로 감싸 호출(handle 무시됨을 실측 확인)
tiktok_shortlink (안전망으로 격하)vm.tiktok.com/{code}, tiktok.com/t/{code}, vt.tiktok.com/{code} — 상류 수집 단계에서 HEAD 301로 canonical URL로 펼쳐 저장하기로 결정(실측 6/6 해석 성공, 무료). 정규화가 성공하면 router엔 canonical만 도달하므로 이 타입은 쓰이지 않음. 만료·네트워크 실패로 raw shortlink가 남은 잔여분에 한해 안전망으로만 존치 (특히 vt.tiktok.com은 host whitelist에 없으면 website로 새어 sourceKey='tiktok.com' 오염)
tiktok_search/search?q, /search/video?q — 분류 유지, fetcher 미구현(unsupported) 상태 유지
tiktok_hashtag / tiktok_music (보류)/tag/{hashtag}, /music/{title}-{musicId} — 신규 타입 검토만, 우선순위 낮음. 단 사운드 축은 content의 musicMeta.musicId로 먼저 관측
unknown 유지빈 path(//), oembed?url=(빈 값), /{lang}/trending/detail/{slug} — 식별자 복원 불가하거나 극희소
이 문서에서 쓰는 pill 범례
관측 source-data에서 실제 발견됨 웹리서치/실측 검색·curl·실제 호출로 확인 정상/유지 차단·버그·갭·drop 신규/분리 타입 fixed / mutable confidence: high medium low
목차   1a. 개념 글로서리 · 1b. URL 패턴 · 2. sourceType/sourceKey 판정 · 3. Fetch 테스트 + 획득 방법 · 4. 필드 통합 (변동성·파싱·판정) · 다음 액션

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

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

개념설명
URL 종류 - 프로필(Profile)tiktok.com/@{handle} 형태. 크리에이터/브랜드 계정 페이지. 표본 273건 전량 이 단일 템플릿(프로필 URL에 항상 @핸들 포함, 부가 경로 없음). 관측
URL 종류 - 비디오(Video)tiktok.com/@{handle}/video/{videoId} 형태. 개별 영상 게시물, 표본 115건 중 104건(90%) 차지. 관측
URL 종류 - 포토(Photo/Slideshow)tiktok.com/@{handle}/photo/{photoId} 형태. TikTok 특유의 이미지 슬라이드쇼 게시물(캐러셀). video와 별도 경로지만 표본 내 fetched.sourceType은 tiktok_video로 통합 취급됨. 11/115건(9.6%). 관측
URL 종류 - 검색(Search)tiktok.com/search?q={query}&t={timestamp} (49건) 및 tiktok.com/search/video?q={query}&t={timestamp} (8건, 비디오 탭 한정 검색). 특정 계정/게시물이 아닌 쿼리 기반 URL로 status: unsupported 처리됨. 관측
식별자 - handle(@아이디)프로필/비디오/포토 URL 공통 경로 세그먼트 @{handle}. 표본상 소문자·숫자 조합만 관측(예: @pumbacaracalofficial, @espn, @knowyourmeme). TikTok 정책상 handle은 대소문자 구분 없이 고유(unique, case-insensitive)하며 영문자/숫자/밑줄(_)/마침표(.)만 허용, 최대 24자. 웹리서치/실측
식별자 - videoId/photoIdURL 마지막 세그먼트의 순수 숫자 문자열(예: 7652462228196560159, 7649280049534799137). TikTok 내부적으로 이 값은 스노우플레이크형 64비트 정수 ID이며 생성 시각 정보를 인코딩함. 웹리서치/실측
식별자 - search query(q)검색 URL의 q 파라미터는 자유 텍스트(공백 포함, URL 인코딩됨. 예: q=flock%20camera). 계정/게시물을 특정하지 못하는 비고유 식별자. 관측
식별자 - t 파라미터검색 URL에 항상 동반되는 t=epoch밀리초 타임스탬프. 검색이 수행/공유된 시점을 나타내는 TikTok 검색 URL 특유의 트래킹 파라미터로 추정. 관측
대소문자 규칙 - handlehandle 자체는 case-insensitive 고유성을 가지나(대문자로 입력해도 동일 계정 매칭), 표시상 원본 대소문자를 유지하는 경우가 있어 URL 매칭/정규화 시 소문자로 통일해 비교해야 함. 웹리서치/실측
지표 - 팔로워 수(Followers)프로필 페이지에 노출되는 핵심 소셜 지표. 표본 JSON은 모두 status: skipped_paid로 실제 값(data)이 비어 있어 관측 불가, TikTok 프로필의 표준 지표로만 알려짐. 웹리서치/실측
지표 - 좋아요 수(Total Likes/Hearts)프로필의 누적 좋아요, 비디오의 게시물별 좋아요(하트 아이콘). TikTok 특유 명칭은 'Likes'(하트 표시). 웹리서치/실측
지표 - 조회수(Views/Plays)비디오 단위 재생 횟수. TikTok에서는 'Views'로 표기. 웹리서치/실측
지표 - 공유 수(Shares)비디오의 재공유(공유 버튼) 횟수. 댓글 수와 함께 비디오 참여 지표 3종(좋아요/댓글/공유) 중 하나. 웹리서치/실측
지표 - 팔로잉 수(Following)프로필이 팔로우하는 계정 수. 웹리서치/실측
플랫폼 특유 개념 - 듀엣(Duet)기존 영상 옆에 나란히 자신의 영상을 나란히 배치해 반응/합주하는 TikTok 고유 리믹스 기능. URL/API 상 원본 videoId를 참조하는 별도 파생 콘텐츠 개념으로, X의 quote와 유사하나 영상 나열 방식이 다름. 웹리서치/실측
플랫폼 특유 개념 - 스티치(Stitch)타 영상의 클립(최대 5초)을 자신의 영상 도입부에 잘라 붙이는 기능. 원본 크리에이터 크레딧이 자동 표기됨. 웹리서치/실측
플랫폼 특유 개념 - 사운드/오디오 페이지(Sound page)특정 배경음악·오디오 클립을 사용한 모든 영상을 모아 보여주는 tiktok.com/music/{soundName}-{soundId} 형태 URL. 밈 확산의 핵심 축(동일 사운드로 영상이 파생)이나 이번 표본에는 미등장. 웹리서치/실측
플랫폼 특유 개념 - 해시태그 챌린지 페이지tiktok.com/tag/{hashtag} 형태로 특정 해시태그를 사용한 영상을 모은 페이지. 브랜드/밈 캠페인 단위 집계에 쓰이나 표본에는 없음. 웹리서치/실측
플랫폼 특유 개념 - 슬라이드쇼/포토모드영상 대신 여러 장의 사진 카루셀+배경음악을 올리는 게시 형식(/photo/ 경로). 표본에서 관측된 11건 전량 동일 URL(같은 photoId)이 다수 토큰에 재사용됨(콘텐츠 재사용/카피캣 정황). 관측
소스 필드(sourceField) 관측치동일 TikTok URL이 토큰의 website, discovered, telegram 등 서로 다른 sourceField에서 발견됨 — 즉 TikTok 링크가 웹사이트뿐 아니라 텔레그램 메시지 등 타 채널 본문에서도 발견/수집될 수 있음. 관측
fetch 상태 - skipped_paid프로필/비디오/포토 URL은 TikTok의 유료(비공식) 스크래핑 API 게이트에 걸려 실제 fetch가 스킵됨(status: skipped_paid, data: null) — 무료 채널로는 팔로워/조회수 등 실측 지표를 얻지 못함. 관측
fetch 상태 - unsupported검색(search) URL 타입은 애초에 fetch 파이프라인에서 미지원 처리됨(status: unsupported) — 특정 게시물/계정을 가리키지 않는 쿼리형 URL이라 구조적으로 수집 대상에서 제외. 관측
URL 정규화 특이점 - www 유무동일 search/video 템플릿 내에서도 https://www.tiktok.com/...과 https://tiktok.com/...(www 없음)이 혼재 관측됨(예: search/video 표본 3건 중 1건은 www 없음) — URL 매칭 시 www 유무를 정규화해야 함. 관측

1b. URL 패턴

tiktok.com/@{handle}
tiktok_profile — 프로필 URL (관측 273건, 100%)
tiktok.com/@{handle}/video|photo/{postId}
tiktok_video — 게시물(video/photo 공용) URL
tiktok.com/search/video?q={query}
tiktok_search — 검색결과(비URL 파라미터형, fetcher 미구현으로 unsupported)
URL 패턴의미ID 후보관측출처/비고
tiktok.com/@{handle}프로필 페이지. classifyTiktok: segments[0]이 '@'로 시작하면 tiktok_profile, sourceKey=handle(소문자 정규화). fetcher는 apify clockworks~tiktok-scraper actor에 profiles:[handle]로 호출, status는 유료게이트로 skipped_paid(관측 3건 전부)이며 실제 도달 시 text/createTimeISO/hashtags/isAd=fixed, playCount/diggCount/commentCount/shareCount/authorFans=mutable로 반환.handle (프로필 소문자 사용자명)관측 273건samples/tiktok_profile.json 100% 단일 템플릿
tiktok.com/@{handle}/video/{postId}일반 동영상 게시물. classifyTiktok: segments에 'video' 포함+다음 세그먼트 존재 시 tiktok_video, sourceKey=postId(숫자 원형). fetcher는 postURLs:[전체URL]로 apify 호출, 동일 valueNature 스키마.postId (숫자 게시물 ID, 대소문자/원형 유지)관측 104건samples/tiktok_video.json 구조1
tiktok.com/@{handle}/photo/{postId}포토(슬라이드) 게시물. classifyTiktok에서 'video'와 동일 루프(['video','photo'])로 처리되어 sourceType은 tiktok_video로 통합, sourceKey=postId. router 주석: photo도 post라 video와 동일 취급(fetcher가 postURLs로 직접 조회).postId (숫자 게시물 ID)관측 11건samples/tiktok_video.json 구조2
tiktok.com/search?q={query}검색 결과 페이지(홈 탭). classifyTiktok: segments[0]==='search' → tiktok_search, sourceKey=q 파라미터(소문자화). tiktok_search는 TiktokFetcher.handles에 없어 라우팅은 되지만 실제 fetcher 미구현 → orchestrator가 status='unsupported' 반환(관측 전부).query string (검색어, URL-decode 후 소문자)관측 49건samples/tiktok_search.json 구조1
tiktok.com/search/video?q={query}검색 결과의 video 서브탭. classifyTiktok에서 segments[0]==='search'만 체크하므로 segments[1]='video'는 무시되고 동일하게 tiktok_search로 분류, sourceKey=q. fetcher 없음 → unsupported.query string (검색어)관측 8건samples/tiktok_search.json 구조2
vm.tiktok.com/{shortCode} (및 tiktok.com/t/{shortCode})TikTok 단축링크(리다이렉트 전용, 실제 프로필/영상으로 302 이동). host는 route()에서 vm.tiktok.com까지 tiktok 분기로 들어가지만 classifyTiktok 내부는 '@'/'search'/'video'/'photo' 셋 중 아무것도 매치 안 해 sourceType='unknown', sourceKey=null. 라우터가 리다이렉트를 따라가지 않아 미해결.shortCode (리다이렉트 해석 전엔 식별자 아님 — 해석 후 진짜 handle/postId로 대체 필요)관측 7건unknown.json: vm.tiktok.com/ZNRTk8BQc(3) + ZNRTtUjSM/ZGd9UfYoM/ZNRTxEvBu/ZNR3pHNHo(각1) + tiktok.com/{value}/{value}(t/ZTSJ7TCua, 54건 그룹 중 일부)
tiktok.com/tag/{hashtag}해시태그 페이지. classifyTiktok 미대응 경로 → unknown. 웹 지식상 정식 해시태그 채널 URL.hashtag (문자열, 대소문자 유지)웹리서치/실측unknown.json 'tiktok.com/{value}/{value}' 그룹 샘플2(count=54 그룹 내 일부, 정확 서브카운트 미분리)
tiktok.com/music/{musicTitle}-{musicId}음원(사운드) 페이지. classifyTiktok 미대응 → unknown. 관측: /music/TAKA-LA-DENTRO-7605338273287834376.musicId (URL 끝 숫자, title은 slug라 비고유)웹리서치/실측unknown.json 'tiktok.com/{value}/{value}' 그룹 샘플3
tiktok.com/oembed?url=oEmbed API 엔드포인트가 url 파라미터 없이(빈 값) 잘못 저장된 케이스. 실제 콘텐츠 URL이 아니라 임베드 메타데이터 조회용 API 호출 흔적으로 보임 — discovered 필드에서 유입, 정상 소셜 URL 아님.없음 (url= 파라미터가 비어 식별 불가, 데이터 결손)관측 40건unknown.json 'tiktok.com/{value}' 그룹(count=40) 전부 동일 URL
tiktok.com/embed/v2/{postId}게시물 임베드(iframe) URL. postId는 video/photo와 동일한 식별자이나 classifyTiktok이 'embed' 경로를 처리하지 않아 unknown으로 누락.postId (숫자, video/photo와 동일 네임스페이스로 추정)관측 3건unknown.json 'tiktok.com/{value}/{value}/6646191924438895877' 템플릿
tiktok.com/{lang}/trending/detail/{slug}언어별 트렌딩 상세 페이지(예 /de/trending/detail/...). classifyTiktok 미대응 → unknown.slug (트렌드 항목명, 게시물 고유 ID 아님 — 약한 식별자)관측 1건unknown.json 'tiktok.com/{value}/{value}/detail/...' 템플릿
tiktok.com// (빈 path, 중복 슬래시)잘못 구성된/빈 URL. 파싱 시 segments가 빈 배열이라 classifyTiktok의 모든 분기 미스 → unknown, sourceKey=null. 데이터 결손 케이스(실제 프로필/링크 아님).없음 (식별자 없음)관측 3건unknown.json 'tiktok.com/' 템플릿
tiktok.com/@{handle}/live웹 지식상 존재하는 정식 라이브 방송 URL(미관측). classifyTiktok은 '@'로 시작하면 무조건 tiktok_profile로 분류하므로 /live 서브패스가 있어도 handle만 추출되어 프로필로 오분류될 가능성.handle (라이브는 세션형이라 handle 외 별도 콘텐츠ID 없음)웹리서치/실측미관측, 일반 지식 보완
vt.tiktok.com/{shortCode}vm.tiktok.com과 동일 성격의 또 다른 TikTok 단축도메인(신규 배포 지역에서 사용). router의 host 화이트리스트(tiktok.com/vm.tiktok.com/m.tiktok.com)에 없어 host 자체가 매칭 안 되면 apex-domain website로 폴백될 수 있음.shortCode (리다이렉트 해석 필요)웹리서치/실측미관측, 일반 지식 보완 — router.ts 60-63행 host whitelist 확인

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

2.1 sourceType 적합도 👤 판단 반영

패턴현재 type판정/제안sourceKeyconf근거사용자 결정
tiktok.com/@{handle}
tiktok_profile, key=handle(소문자)변경 없음handlehighsegments[0].startsWith('@') 분기가 정확히 매치. fetcher가 profiles:[handle]로 clockworks 액터 호출하며 valueNature 스키마도 명확(fixed: text/createTimeISO/hashtags/isAd, mutable: playCount류). 다만 observed 3건 전부 skipped_paid — 라우팅은 옳으나 유료게이트로 실사용 데이터 0건. 이건 router 문제가 아니라 orchestrator 유료게이트 정책 이슈이므로 sourceType 분류 자체는 keep.✅ tiktok_profile 유지. 단 sourceKey=handle은 alias일 뿐이고 canonical 조인키는 authorMeta.id — fetch 후 id로 resolve해 연결. handle 재할당 때문에 handle 단독 dedup 금지. 기존 제안 유지.
tiktok.com/@{handle}/video/{postId}
tiktok_video, key=postId(원형)변경 없음postIdhigh['video','photo'] 루프에서 indexOf('video')로 정확히 매치, postURLs:[전체URL]로 apify 호출. text/createTimeISO/hashtags/isAd=fixed, engagement류=mutable 스키마도 트레이더 모멘텀 신호(diggCount/shareCount 등)에 부합.✅ tiktok_video 유지(key=postId). 기존 제안 유지.
tiktok.com/@{handle}/photo/{postId}
tiktok_video로 병합, key=postId변경 없음(현행 병합 유지)postIdhighrouter 주석에 명시된 의도된 병합 — photo도 post이고 fetcher가 postURLs로 게시물을 직접 조회하므로 video와 스키마·엔드포인트가 완전히 동일. 별도 sourceType으로 split할 실익 없음(같은 postId 네임스페이스, 같은 valueNature).✅ 병합 유지 확정 — 2026-07-29 실측에서 photo 게시물의 canonical URL이 /video/{id}로 정규화돼 돌아옴을 확인(동일 네임스페이스 실증). 병합 후 형식 구분은 isSlideshow로 복원.
tiktok.com/search?q={query}
tiktok_search로 정확히 분류되나 TiktokFetcher.handles에 없어 fetcher 미구현 → orchestrator가 항상 status=unsupported(49건 전부)router 분류는 유지. fetcher 구현 여부는 별도 결정 필요 — 구현하지 않을 거라면 애초에 이 sourceType을 만들 필요가 있었는지 재검토query string(소문자)medium라우팅 정확도 자체는 문제없음(gap은 router가 아니라 fetcher 커버리지). 다만 trader-value 관점에서 검색결과는 다건 집계 페이지라 '원본 vs 재활용' 판별에 쓸 단일 콘텐츠 식별자가 없고, 항상 unsupported로 끝나 실질 신호 가치가 0 — 투자 우선순위 낮음(퀀트 signal 렌즈에서도 관측 데이터가 전무해 검증 불가).✅ router 분류 유지, fetcher 미구현(unsupported) 상태도 유지. 기존 제안 유지.
tiktok.com/search/video?q={query}
segments[0]==='search'만 체크하므로 segments[1]='video'는 무시되고 tiktok_search로 병합, key=q변경 없음(현행 병합 유지)query stringhigh검색 서브탭이라도 쿼리 의미(무엇을 찾는 검색인가)는 동일하고 결과도 다건 집계라는 본질이 같음 — 별도 타입으로 split할 근거 없음. search 자체의 fetcher 미구현 이슈는 위 항목과 동일하게 별도 사안.✅ tiktok_search 병합 유지. 기존 제안 유지.
vm.tiktok.com/{shortCode} (및 tiktok.com/t/{shortCode})
host는 vm.tiktok.com까지 tiktok 분기로 들어오지만 classifyTiktok 내부 4개 분기(search/video/photo/@) 중 아무것도 매치 안 해 unknown, key=null. tiktok.com/t/{shortCode}도 segments[0]='t'가 어떤 분기와도 안 맞아 동일하게 unknown신규 sourceType 'tiktok_shortlink' (key=shortCode) 도입 후, router는 pure/네트워크-없음 함수이므로 classify 단계에서 리다이렉트를 못 따라감 — fetcher 또는 orchestrator 앞단에 HEAD 리다이렉트 리졸버를 추가해 실제 handle/postId로 재라우팅하거나, 최소한 shortCode를 key로 저장해 추후 배치 리졸브 가능하게shortCode (해석 전엔 진짜 식별자 아님)highobserved 7건이 전부 데이터 손실(unknown, key=null) — 실제로는 프로필/영상 콘텐츠인데 완전히 유실됨. trader-value 관점에서 shortlink는 밈코인 공식 계정이 자주 쓰는 형태(bio 링크 단축)라 무시하면 신호 누락 폭이 작지 않음. 다만 router가 순수함수라 리다이렉트 해석은 아키텍처 변경(네트워크 호출)이 필요해 즉시 처리는 어려움 — 우선 sourceType만 분리해 유실을 가시화하는 것이 1차 조치.결정 변경 — 상류 수집 단계에서 canonical 정규화. 실측: vm/t 링크 6건 전부 HEAD 301로 /@handle/video/{postId} 해석 성공(무료, 즉시). 왜 fetcher가 아니라 상류인가: TikTok은 유료 게이트라 fetch가 스킵된 엔트리는 fetcher 해석 방식에선 영원히 shortlink로 남고 → 같은 영상이 canonical 관측분과 별개 엔티티로 분기(조인키에서 금지한 실패 모드). 리다이렉트 해석은 무료 HEAD 1홉이라 유료 여부와 무관하게 항상 가능. ⚠ shortlink를 tiktok_video로 고정하지 않음 — vm 링크는 프로필 공유에도 쓰여 /@handle로 떨어질 수 있음(관측 6건이 전부 video인 건 표본 편향) → 해석 결과에 따라 재분류. ⚠ Location에 ?_r=1&_t=... 추적 파라미터가 붙어오므로 제거 후 저장.
tiktok.com/tag/{hashtag}
미대응 → unknown신규 sourceType 'tiktok_hashtag' (key=hashtag, 대소문자 유지) 검토. 단 fetcher 지원 여부(clockworks 액터가 hashtag 검색을 지원하는지) 확인 전에는 우선순위 낮음hashtagmediumsearch와 동일하게 다건 집계 페이지라 단일 콘텐츠 신호는 아니지만, 해시태그 자체가 밈 확산(크로스포스트) 폭을 나타내는 모멘텀 지표가 될 수 있어 trader-value에서 완전 noise는 아님. 다만 정확 서브카운트가 unknown 그룹(54건)에 섞여 있어 실제 관측량 확인이 먼저 필요.✅ 신규 타입 검토만 유지(우선순위 낮음). 기존 제안 유지.
tiktok.com/music/{musicTitle}-{musicId}
미대응 → unknown신규 sourceType 'tiktok_music' (key=musicId, URL 끝 숫자 파싱 필요 — title은 slug라 비고유) 검토musicIdlow사운드 트렌드는 밈 확산의 간접 지표가 될 수 있으나 특정 토큰과의 연결고리가 약하고(사운드는 재사용성이 전제라 '원본 판별' 신호와 상충), 관측량도 미상(observed=null, unknown 그룹 내 일부 추정)이라 신규 sourceType 투자 우선순위는 낮음.✅ 신규 타입은 보류 유지. 다만 사운드 확산 축은 content의 musicMeta.musicId를 keep해 먼저 관측하기로 결정 — URL 타입 없이도 데이터는 쌓임.
tiktok.com/oembed?url= (빈 값)
unknown (segments=['oembed'], 어떤 분기도 매치 안 함)router 변경 불필요 — unknown 유지가 맞음. 근본 수정은 상류(discovered 필드 추출 파이프라인)에서 url= 파라미터가 빈 oEmbed API 호출 흔적을 애초에 소셜 URL로 저장하지 않도록 하는 것없음(url= 파라미터 자체가 비어 식별 불가)high이건 router의 분류 실패가 아니라 상류 데이터 결손 — url 파라미터가 비어 있어 어떤 라우팅 로직을 추가해도 식별자를 복원할 수 없음. sourceType/router 레이어에서 손댈 지점이 아님.✅ unknown 유지(상류 데이터 결손). 기존 제안 유지.
tiktok.com/embed/v2/{postId}
미대응 → unknown (loop이 'video'/'photo' 세그먼트만 찾고 'embed'/'v2'는 안 봄)tiktok_video로 병합, key=postId (마지막 숫자 세그먼트). 단 fetcher의 postURLs 입력이 raw embed URL(iframe 형식)을 그대로 받아도 apify clockworks 액터가 파싱 가능한지 미검증 — 필요시 postId만 뽑아 표준 워치 URL로 재구성해서 넘기는 보정 필요postIdmediumpostId는 video/photo와 동일 네임스페이스로 추정되므로 새 sourceType을 만들 이유가 없고 기존 tiktok_video에 합류시키는 게 맞음. 다만 라우터에서 sourceKey만 옳게 뽑고 sourceUrl(임베드 형식)을 그대로 fetcher에 넘기면 액터가 실패할 위험이 있어 confidence는 medium.✅ tiktok_video 병합 확정 + 미해결 리스크 실측으로 해소. "handle을 못 찾아 못 쓴다"는 우려는 조인키를 id로 확정하면 사라짐(creator 링크는 URL이 아니라 fetch 응답의 authorMeta.id에서 옴). 실측: embed URL 자체는 oEmbed·apify 둘 다 거부(400/FAILED)했으나, /@아무handle/video/{postId}로 감싸면 틀린 handle로도 정확한 게시물+작성자를 반환 → router는 postId만 뽑고 fetcher가 canonical 형태로 정규화하면 완전 해결.
tiktok.com/{lang}/trending/detail/{slug}
미대응 → unknown현재는 조치 보류 권장 — slug는 안정적 콘텐츠 ID가 아니라 트렌드 항목명이라 postId처럼 재현 가능한 키가 아님. 관측 1건뿐이라 신규 sourceType 투자 대비 효용 낮음slug (약한 식별자)low약한 식별자 + 극희소 관측(1건) 조합이라 signal/redundancy 렌즈에서 검증 자체가 불가능. 향후 관측량이 늘면 재검토.✅ 조치 보류 유지(약한 식별자 + 관측 1건). 기존 제안 유지.
tiktok.com// (빈 path, 중복 슬래시)
unknown, key=null (segments 빈 배열)변경 불필요없음high잘못 구성된/빈 URL로 실제 콘텐츠가 아닌 데이터 결손 케이스 — unknown 처리가 정확한 동작. router 로직 수정 대상 아님.✅ unknown 유지. 기존 제안 유지.
tiktok.com/@{handle}/live
segments[0].startsWith('@') 분기가 /live 서브패스 무시하고 handle만 추출 → tiktok_profile로 분류(우연히 정확)현행 유지. 단 라이브 세션 특유의 메트릭(동시 시청자수, 방송중 여부)이 필요해지면 그때 별도 tiktok_live sourceType 분리 검토handlemedium우려와 달리 '오분류'는 아님 — 라이브는 handle 외 별도 콘텐츠ID가 없는 세션형이라 프로필 키로 수렴하는 게 자연스럽고, 어차피 별도 라이브 전용 fetcher/액터가 없어 지금 분리해도 실질 이득이 없음. 미관측(observed=null)이라 우선순위도 낮음.✅ tiktok_profile 수렴 유지. 기존 제안 유지.
vt.tiktok.com/{shortCode}
router.classify()의 host whitelist(56-90행)가 tiktok.com/vm.tiktok.com/m.tiktok.com만 포함 — vt.tiktok.com은 어떤 if도 안 걸려 최종 폴백인 website로 떨어지고 apexDomain('vt.tiktok.com')이 'tiktok.com'을 반환해 잘못된 website 키로 오염될 위험host whitelist에 'vt.tiktok.com' 추가해 classifyTiktok으로 라우팅. 단 vm.tiktok.com과 동일하게 shortCode 리다이렉트 해석 필요(위 vm.tiktok.com 항목과 동일한 리졸버 설계에 편입)shortCode (리다이렉트 해석 필요)highhost 커버리지 갭이 명확 — 매치 실패 시 unknown이 아니라 website로 새는 게 더 나쁨(다른 정상 website 엔트리와 sourceKey='tiktok.com'으로 충돌 가능). 미관측(observed=null)이지만 신규 지역 배포 도메인이라 향후 유입 가능성 있어 whitelist 추가는 저비용·고안전 조치.✅ host whitelist에 vt.tiktok.com 추가는 그대로 유지. 상류 정규화가 기본 경로가 되더라도 만료·네트워크 실패로 raw shortlink가 남을 수 있고, 그때 whitelist가 없으면 unknown이 아니라 website로 새어 apexDomain이 'tiktok.com'을 반환해 다른 정상 website 엔트리와 키 충돌 — 실패 모드가 더 나쁨. 안전망으로 필수.

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

패턴추출 idresolve?판정conf근거사용자 결정
tiktok.com/@{handle}sourceKey = handle (소문자)okhighlive-test에서 profiles:["knowyourmeme"] 호출 결과 authorMeta.name='knowyourmeme'로 handle이 정확히 그 계정을 가리킴을 확인. 단, Apify profiles 경로는 '프로필 정보'가 아니라 해당 계정의 '최신 영상 1건'을 반환하고 authorMeta에 프로필 필드가 동봉되는 구조(fetcher는 authorMeta.fans/name만 매핑) — sourceKey 자체는 계정을 유일하게 특정하는 진짜 식별자이나, 이 식별자로 얻는 콘텐츠가 '안정적 프로필 스냅샷'이 아니라 '조회 시점 최신 영상'이라는 점은 mutable 데이터로서 look-ahead 주의 대상(별도 field-necessity 판정 사안).✅ handle 추출 자체는 정확. 단 handle은 키가 아니라 alias로 격하 — 30일마다 변경 + 옛 handle 타인 재할당(unique 위반). canonical 조인키는 authorMeta.id.
tiktok.com/@{handle}/video/{postId}sourceKey = postId (숫자 원형)okhighlive-test에서 postURLs:[...] 호출 결과 raw item.id='7652462228196560159'가 라우터 추출 sourceKey와 완전 일치 확인. postId는 게시물을 유일하게 특정하는 진짜 식별자이며 profiles 경로와 달리 '최신영상'이 아닌 해당 게시물 자체를 정확히 반환.✅ postId가 authoritative임을 실측 확인 — URL의 handle이 틀려도 정확한 게시물 반환. key=postId 유지 + content 응답의 authorMeta.id로 creator 연결.
tiktok.com/@{handle}/photo/{postId}sourceKey = postId (숫자, video와 동일 분기 처리)okmediumclassifyTiktok의 ['video','photo'] 동일 루프를 타고 fetcher도 postURLs로 동일하게 호출되므로 코드상 video 케이스와 완전히 동형. 다만 photo 게시물 자체를 대상으로 한 live-test는 수행되지 않아 postId가 photo 콘텐츠에도 동일하게 유효한 식별자로 resolve됨은 직접 관측이 아닌 코드 동형성에 근거한 추정.✅ postId가 authoritative임을 실측 확인 — URL의 handle이 틀려도 정확한 게시물 반환. key=postId 유지 + content 응답의 authorMeta.id로 creator 연결.
tiktok.com/search?q={query}sourceKey = q (URL-decode 후 소문자)wronghighlive-test(코드 확인)로 TiktokFetcher.handles에 'tiktok_search'가 없어 서비스가 디스패치 전에 unsupported로 조기 반환함을 확인 — 실제 네트워크 호출 자체가 발생하지 않아 sourceKey가 resolve되는 경로가 아예 없음. 설령 fetcher가 구현되더라도 검색어는 시간에 따라 결과가 바뀌는 쿼리이지 단일 콘텐츠를 유일하게 가리키는 식별자가 아니라 id-fit 관점에서도 근본적으로 약함(우리 목적상 '진위/원본 재활용 판별'에 쓸 고정 콘텐츠 ID가 아님).✅ 쿼리는 콘텐츠 식별자 아님(다건 집계). 기존 제안 유지.
tiktok.com/search/video?q={query}sourceKey = q (segments[1]='video' 무시)wronghighsearch 패턴과 동일하게 fetcher 미구현으로 unsupported 확정(코드 확인, 관측 status와 일치). 추가로 서브탭 정보(video 필터)가 sourceKey 추출 시 아예 버려져 원 URL이 담고 있던 필터 정보 손실도 발생 — 이중으로 부적합.✅ 쿼리는 콘텐츠 식별자 아님(다건 집계). 기존 제안 유지.
vm.tiktok.com/{shortCode} (및 tiktok.com/t/{shortCode})현재 sourceType=unknown, sourceKey=null (host는 whitelist에 있으나 path가 @/search/video/photo 중 아무것도 매치 안 함)wronghighclassifyTiktok 내부 4개 분기(search/video/photo/@) 중 어느 것도 shortCode 형태 path와 매치되지 않아 sourceKey=null로 완전 유실 확인(코드 Read). live-test에서도 표본에 vm.tiktok.com 0건이라 실제 리다이렉트 해석 시도조차 없었음. shortCode 자체는 리다이렉트를 따라가지 않는 한 진짜 콘텐츠를 가리키는 식별자가 아니므로(302 대상이 진짜 handle/postId) 현재 구현으로는 원천적으로 resolve 불가.✅ shortCode는 그 자체로 콘텐츠 식별자가 아님이 맞음. 다만 상류에서 canonical로 정규화하면 postId(또는 handle)가 확보되므로 식별 문제 해소 — 실측 6/6 해석 성공. 저장되는 최종 키는 shortCode가 아니라 해석된 postId/handle.
tiktok.com/tag/{hashtag}현재 sourceType=unknown, sourceKey=nullwrongmediumclassifyTiktok에 'tag' 분기가 없어 unknown으로 빠짐(코드 확인). live-test에서 별도 호출 시도 없이 구조적으로 fetcher 도달 불가 확정. 관측 건수 자체도 unknown.json 그룹 내 정확 서브카운트 미분리라 실사용 빈도 불확실.✅ hashtag는 집계 페이지 키 — 단일 콘텐츠 식별 불가. 기존 제안 유지.
tiktok.com/music/{musicTitle}-{musicId}현재 sourceType=unknown, sourceKey=null (musicId가 title에 붙은 slug 안에 매장)wrongmediumclassifyTiktok에 'music' 분기 없어 unknown. 설령 파싱을 추가해도 slug에서 musicId만 분리 추출하는 별도 로직이 필요(title은 비고유 slug). 현재는 어떤 형태로도 resolve되지 않음을 코드로 확인.✅ musicId 추출(끝 숫자)은 유효하나 타입 신설은 보류. musicMeta.musicId로 먼저 관측.
tiktok.com/oembed?url=현재 sourceType=unknown, sourceKey=null (url 파라미터가 비어있음)wronghighurl= 값 자체가 빈 데이터 결손 케이스(코드/샘플 모두 동일 URL 40건). 식별자 후보가 근본적으로 존재하지 않아 id-fit 판정 대상이 아니라 데이터 정제(제거) 대상.✅ url 파라미터가 비어 복원 불가 — 상류 수정 사안. 기존 제안 유지.
tiktok.com/embed/v2/{postId}현재 sourceType=unknown, sourceKey=null (postId가 문자열에 있음에도 미추출)wrongmediumclassifyTiktok의 video/photo 루프는 segments 중 'video' 또는 'photo' 토큰이 있어야 매치되는데 embed 경로는 'embed'/'v2'/postId 세그먼트라 루프에 걸리지 않아 unknown으로 누락(코드 확인). postId 자체는 video/photo와 동일 네임스페이스로 추정되어 잠재적으로 진짜 식별자이지만 현재 라우터가 뽑지 않아 resolve 불가.✅ postId를 마지막 세그먼트에서 추출하면 식별 성립. fetcher가 /@x/video/{postId}로 정규화해 호출(실측 검증됨).
tiktok.com/{lang}/trending/detail/{slug}현재 sourceType=unknown, sourceKey=nullwronglowclassifyTiktok 미대응이라 unknown(코드 확인). 관측 1건뿐이라 실사용 빈도 낮고, slug 자체도 트렌드 항목명이라 게시물 고유 ID가 아닌 약한 식별자 — 설령 파싱을 추가해도 진짜 고유 식별자로 보기 어려움.✅ slug는 재현 가능한 키가 아님 — 보류 유지. 기존 제안 유지.
tiktok.com// (빈 path)sourceType=unknown, sourceKey=nullwronghighsegments가 빈 배열이라 모든 분기 미스로 unknown 확정(코드 확인). 식별자 후보 자체가 없는 데이터 결손 케이스.✅ 기존 제안 유지(확정).
tiktok.com/@{handle}/livesourceKey = handle (segments[0].startsWith('@') 분기가 /live 서브패스 무시하고 무조건 매치)oklow코드상 '@'로 시작하면 뒤 세그먼트('live')와 무관하게 무조건 tiktok_profile+handle로 분류됨을 확인(라우터 149-163행). handle 자체는 계정을 유일하게 특정하는 진짜 식별자이므로 id-fit 관점에서는 통과하나, 실제 라이브 테스트는 미수행(관측 0건)이라 profiles:[handle] 호출 시 프로필 판정과 동일하게 최신 영상을 반환할 뿐 '라이브 세션'과는 무관한 콘텐츠가 나올 가능성 — 이는 sourceType 오분류 문제(별도 판정관 사안)이지 sourceKey 자체의 고유성 문제는 아님.✅ handle 추출 자체는 정확. 단 handle은 키가 아니라 alias로 격하 — 30일마다 변경 + 옛 handle 타인 재할당(unique 위반). canonical 조인키는 authorMeta.id.
vt.tiktok.com/{shortCode}host가 whitelist(tiktok.com/vm.tiktok.com/m.tiktok.com)에 없어 classify()의 마지막 폴백으로 떨어져 sourceType='website', sourceKey=apexDomain('vt.tiktok.com')='tiktok.com'wrongmedium라우터 classify() 62-68행 host whitelist에 vt.tiktok.com이 없음을 코드로 확인 — 89행 최종 폴백(website+apexDomain)으로 떨어져 shortCode가 완전히 유실되고, 여러 서로 다른 vt.tiktok.com 링크가 전부 동일한 sourceKey='tiktok.com'으로 뭉개져 고유 식별은커녕 다른 소스와 충돌 위험까지 있음. 미관측이라 실사용 빈도는 불확실.✅ shortCode는 그 자체로 콘텐츠 식별자가 아님이 맞음. 다만 상류에서 canonical로 정규화하면 postId(또는 handle)가 확보되므로 식별 문제 해소 — 실측 6/6 해석 성공. 저장되는 최종 키는 shortCode가 아니라 해석된 postId/handle.
커버리지 갭 — vm.tiktok.com/{shortCode} 및 tiktok.com/t/{shortCode} — 7건 전부 unknown으로 유실, 리다이렉트 해석 로직 부재(router가 순수함수라 네트워크 호출 불가)
커버리지 갭 — tiktok.com/tag/{hashtag} — router 미대응, unknown 그룹(54건)에 섞여 정확한 관측량 미분리
커버리지 갭 — tiktok.com/music/{musicTitle}-{musicId} — router 미대응, unknown 그룹에 섞임
커버리지 갭 — tiktok.com/embed/v2/{postId} — postId를 tiktok_video와 병합할 수 있는데도 unknown으로 누락
커버리지 갭 — tiktok.com/{lang}/trending/detail/{slug} — 1건 미대응, 식별자 강도도 약함
커버리지 갭 — vt.tiktok.com host 전체가 whitelist 밖 — 매치 실패 시 unknown이 아니라 website 폴백으로 새어 apexDomain 오염 위험
커버리지 갭 — tiktok_search sourceType은 router 분류는 정확하나 TiktokFetcher.handles에 없어 fetcher 자체가 미구현(49+8=57건 전부 unsupported) — router 문제는 아니지만 sourceType 존재 이유 재검토 필요

3. Fetch 테스트 + 획득 방법

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

방법비용설명트레이드오프conf
확정 ✅ (2026-07-21)사용자 선택 완료 — tiktok_profile→apify:apidojo ($0.30 / 1,000 results (profile-scraper 단독은 $2.50/1,000)) · tiktok_video→apify:apidojo ($0.30 / 1,000 results) · tiktok_search→apify:apidojo-search ($0.30 / 1,000 results (프로필/비디오와 동일 액터·가격)) adopted — ⚠️ 전환 보류(2026-07-31 실측)$0.30 / 1,000 results (profile-scraper 단독은 $2.50/1,000) / $0.30 / 1,000 results / $0.30 / 1,000 results (프로필/비디오와 동일 액터·가격)Phase A 후보 사다리에서 사람이 선택한 확정 source. §4 통합 필드 뷰는 이 source의 실제 스키마 기준.🔴 전환 시도했으나 막혔다 — apidojo 가 무료 플랜의 API 호출을 거부한다(The developer of this actor doesn't allow the use of API in the Free Plan). 4회 실행 전부 SUCCEEDED 로 기록되나 데이터 0건 + 과금만 $0.012. 그리고 README 출력 예시에 authorMeta.createTime(계정 개설일 = 러그리스크 1순위 tell) · isSlideshow(photo/video 유일 구분자) · slideshowImageLinks 등 7개 필드가 없다 — 단 API 가 막혀 확증하지 못했다. 비용 실측: clockworks $0.0047/run vs apidojo $0.0003/건 → 절감 월 약 $5로 Starter $29 를 정당화하지 못한다. 상세: [apify-cost-model.html](./apify-cost-model.html)낮음 — 실측으로 전환 조건이 미충족임이 확인됨
0 (현재)Apify actor clockworks~tiktok-scraper 실제 사용 중 (tiktok.fetcher.ts, 유일한 경로)$1.70 / 1,000 results (PPE, Apify 공식 pricing 페이지 확인)profiles:[handle] 또는 postURLs:[url]로 프로필/게시물 조회. 반환: text/createTimeISO/hashtags/isAd(fixed), playCount/diggCount/commentCount/shareCount/authorFans(mutable). follower total은 authorMeta.fans로 확보되나 생성일(계정)/인증여부는 미매핑(item에 있어도 fetcher가 파싱 안 함).검증됨(운영중)이지만 현재 사용 가능한 actor 중 가장 비쌈. 저볼륨 온디맨드엔 비용 자체는 부담 적으나 대안 대비 5배 이상 비효율.높음 (코드 Read로 직접 확인, 추정 없음)
1 (무료, 제한적)TikTok oEmbed (/oembed, 인증 불필요) 미사용, 즉시 사용 가능완전 무료, rate limit 공개 안 됨(가벼운 사용 전제)author_name, title, thumbnail_url만 반환. follower/생성일/인증여부/참여수(playCount 등) 전혀 없음.우리 필요 필드(팔로워·인증·참여수)를 거의 커버 못 해 실질적으로 게시물 존재확인/미리보기용 그 이상 아님. 백업 fallback 용도로만 가치.높음 — TikTok 공식 문서(developers.tiktok.com/doc/embed-videos) 직접 확인
2 (공식이나 구조적 불가)TikTok Display API (/v2/user/info/, /v2/video/list/) 우리 유스케이스에 사용 불가무료follower/videoList 등 필요 필드 스펙상 존재.OAuth로 대상 계정 소유자 본인이 로그인/동의해야만 접근 가능한 구조 — 우리는 토큰과 연관된 임의 제3자 계정을 관찰해야 하므로 원천적으로 부적합.높음 — TikTok 공식 문서 직접 확인
3 (공식·무료지만 자격 없음)TikTok Research API 신청 자격 자체 없음무료 (1,000 req/day, follower/following 엔드포인트는 최대 2M records/day)팔로워·생성일·인증·게시물 참여수 등 스펙상 우리 필요를 거의 다 커버.공식 FAQ에 상업적 사용자(creator/advertiser/commercial user)는 명시적으로 'No' — 학술기관·비영리 연구 전용. 우리 백엔드는 자격 미달로 원천 배제.높음 — TikTok Research API FAQ 페이지 직접 fetch, 문구 인용 확인
4 (유료, 추천 대안)Apify actor apidojo/tiktok-scraper 미사용, 교체 후보$0.30 / 1,000 results (PPE) — 현재 clockworks 대비 약 82% 저렴프로필: username/bio/verified/follower·following/video수. 게시물: views/likes/comments/shares/bookmarks + 업로드시각/해시태그. 우리 필요 필드(팔로워·인증·게시일·참여수) 구조적으로 모두 존재.현재 fetcher 인터페이스(buildResult/valueNature 매핑)만 바꾸면 되는 저비용 전환. 단, 필드명이 clockworks와 다를 가능성 높아 실제 매핑 검증(actor 출력 스키마 재확인) 필요 — 광고성 요약이라 100% 신뢰 금지.중상 — Apify 공식 상품페이지 직접 fetch, 단 필드 상세는 요약이라 실사용 전 실제 output 샘플 확인 권장
5 (유료, 비추천)TikAPI.io (비공식 관리형 API, 구독형) 미사용, 저볼륨엔 부적합월 구독 $29(300req/day)~$189(2,000req/day), pay-per-use 아님프로필/팔로워/게시물 등 REST 엔드포인트 제공.고정 월정액 + 요청수 tier 구조라 '저볼륨 온디맨드'와 과금모델 자체가 안 맞음(안 쓰는 달에도 최소 $29). PPE형 Apify 대비 비효율적.중 — 3rd party 블로그(Blotato) 요약 기반, 가격 페이지 직접 미확인이라 벤더 편향 가능성 있음. 채택 전 tikapi.io/pricing 직접 재확인 권장

3.2 공식 API 상세

확신도 높음 (공식 문서 1차 확인) — 두 공식 API 모두 우리 유스케이스(임의 제3자 토큰 관련 TikTok 계정을 저볼륨으로 온디맨드 조회)에는 사용 불가: Display API는 대상 계정 소유자 OAuth 동의가 전제라 구조적으로 불가하고, Research API는 필드는 충분하지만 상업적 사용자는 자격 자체가 없다고 FAQ에 명시됨.
단계내용conf
Display APIOAuth로 대상 계정 본인 로그인 필요 → 우리가 관찰하려는 임의 계정에는 적용 불가높음
Research API학술기관/EU 비영리 전용, 상업적 사용자 명시적 배제(FAQ: 'No')높음

3.3 라이브 테스트 실측

대상방법과금결과비고
tiktok_video oEmbed (https://www.tiktok.com/@espn/video/7652462228196560159)GET https://www.tiktok.com/oembed?url=...무료success (HTTP 200)공식 무료(인증 불필요). 반환 필드=title/author_name/author_unique_id/thumbnail_url/html 뿐. playCount/diggCount/commentCount/shareCount/follower/verified/생성일 전무 — acquisition rank1 결론과 일치, 백업 존재확인용 이상 아님.
tiktok_profile oEmbed (https://www.tiktok.com/@knowyourmeme)GET https://www.tiktok.com/oembed?url=...무료success (HTTP 200)프로필도 oEmbed로 200 응답되나 name/title뿐, follower/verified 없음.
tiktok_profile — Apify clockworks~tiktok-scraper, profiles:["knowyourmeme"], resultsPerPage:1 (fetcher가 실사용하는 정확한 엔드포인트/입력 그대로 curl 재현)POST https://api.apify.com/v2/acts/clockworks~tiktok-scraper/run-sync-get-dataset-items?token=***&timeout=90success (HTTP 201, 1 item)profiles:[handle] 는 '프로필 정보'가 아니라 해당 계정의 최신 영상 1건을 반환하며 authorMeta 안에 프로필 정보가 동봉됨 — fetcher.buildResult는 item.text/createTimeISO/playCount/diggCount/commentCount/shareCount/hashtags/authorMeta.fans/authorMeta.name/isAd만 매핑. authorMeta.verified(true 확인됨)/authorMeta.createTime(계정생성일 1556120323=2019-04-24)/collectCount/repostCount는 raw에 존재하나 fetcher가 파싱하지 않음(acquisition 요약과 일치).
tiktok_video — Apify clockworks~tiktok-scraper, postURLs:["https://www.tiktok.com/@espn/video/7652462228196560159"], resultsPerPage:1POST https://api.apify.com/v2/acts/clockworks~tiktok-scraper/run-sync-get-dataset-items?token=***&timeout=90success (HTTP 201, 1 item)postURLs 경로는 정확히 해당 게시물 자체를 반환(profiles 경로와 달리 '최신영상' 아님). isAd=true(광고성 게시물) 확인 — fetcher.isAd 매핑 정상 동작 검증됨. sourceKey(라우터 추출 '7652462228196560159')와 raw item.id 완전 일치 확인.
tiktok_search — https://www.tiktok.com/search?q=flock%20camera코드 경로 확인만 (라이브 호출 생략)무료구조적으로 fetcher에 도달 불가 → 'unsupported'로 단락 확정샘플(tiktok_search.json)의 status:'unsupported'와 코드가 일치함을 Read로 확인. 실제 네트워크 호출은 애초에 발생하지 않으므로 라이브 테스트 대상 아님(추정 아님, 코드 확인).
id-probe(shortlink/opaque URL redirect)curl -sI (계획했으나 생략)무료해당없음 — 표본에 vm.tiktok.com 등 shortlink/opaque URL 0건라우터 classifyTiktok은 host allowlist에 vm.tiktok.com/m.tiktok.com도 포함하지만 실제 표본엔 등장하지 않아 redirect 검증 스킵.
tiktok_video(photo) — /photo/ 게시물 실측 (https://www.tiktok.com/@adoptabledogsofmiami1/photo/7649280049534799137)POST clockworks~tiktok-scraper, postURLs:[photo URL], resultsPerPage:1 (fetcher 입력 그대로)success (HTTP 201, 1 item, 절단 없이 top-level 29키 전량 확인)2건 확정: ①photo/video 형식 구분자 = isSlideshow ②photo 게시물의 canonical URL이 /video/로 정규화돼 돌아옴 → 동일 네임스페이스 실증(병합 결정 정당화). 더불어 문서 objectFields에 없던 필드 다수 발견(mentions/textLanguage/isPinned/webVideoUrl/videoMeta.transcriptionLink/musicMeta.musicId 등) — IG에서 alt를 뒤늦게 발견한 것과 같은 패턴.
embed/v2 URL을 postURLs에 그대로 투입 (https://www.tiktok.com/embed/v2/6646191924438895877)POST clockworks~tiktok-scraper, postURLs:[embed URL] / 및 무료 oEmbed·HEAD 대조FAILED (apify run-failed, HTTP 400) · oEmbed 400 · 직접 HEAD 503교란요인 분리함: 이 예시 postId(6646...)는 삭제된 게시물이라 정상 형식(/@espn/video/)으로도 400. 살아있는 postId를 /embed/v2/ 형식으로 oEmbed 조회해도 400 → embed URL 형식 자체가 미지원임이 별도로 확인됨(게시물 사망과 무관).
postId authoritative 검증 — 일부러 틀린 handle + 살아있는 postId (/@zzzwronghandlezzz/video/7649280049534799137)POST clockworks~tiktok-scraper, postURLs:[틀린 handle URL] · 무료 oEmbed 대조success (HTTP 201) — apify·oEmbed 둘 다 정확한 게시물 반환URL의 handle은 완전히 무시되고 postId만으로 해석됨 → embed/v2·shortlink 등 handle 없는 URL도 postId만 확보하면 /@x/video/{postId}로 감싸 fetch 가능. "handle을 못 찾아 쓰기 어렵다"는 audit의 우려에 대한 확정 답.
shortlink 리다이렉트 해석 — 실제 관측된 vm/t 링크 6건 (예: vm.tiktok.com/ZNRTtUjSM, tiktok.com/t/ZTSJ7TCua)curl -sI (HEAD) — 무료, 네트워크 1홉무료success 6/6 (전부 HTTP 301)상류 정규화 결정의 근거. 6건 전부 즉시 해석되고 무료 → 유료 게이트와 무관하게 항상 수행 가능. ⚠ 6건이 전부 /video/ 로 떨어졌으나 프로필 공유 shortlink도 존재하므로 "shortlink=video" 고정은 금지, 해석 결과로 재분류. ⚠ ?_r=1&_t= 추적 파라미터 제거 필요.

tiktok_video oEmbed (https://www.tiktok.com/@espn/video/7652462228196560159) 실측 응답 원문:

{"version":"1.0","type":"video","title":"(via blades_collector_bd/IG) #beyblade #espnsocial ","author_name":"ESPN","author_unique_id":"espn","thumbnail_url":"https://p16-common-sign.tiktokcdn.com/...","embed_product_id":"7652462228196560159","embed_type":"video"}

tiktok_profile oEmbed (https://www.tiktok.com/@knowyourmeme) 실측 응답 원문:

{"version":"1.0","type":"rich","title":"Know Your Meme's Creator Profile","author_name":"Know Your Meme","embed_product_id":"knowyourmeme","embed_type":"profile"}

tiktok_profile — Apify clockworks~tiktok-scraper, profiles:["knowyourmeme"], resultsPerPage:1 (fetcher가 실사용하는 정확한 엔드포인트/입력 그대로 curl 재현) 실측 응답 원문:

{"id":"7658394009320099085","text":"What actually is Tung Tung Tung Sahur?...","createTimeISO":"2026-07-03T20:00:04.000Z","isAd":false,"playCount":3400000,"diggCount":340900,"commentCount":3276,"shareCount":95200,"collectCount":...,"hashtags":[{"name":"ramadan"},{"name":"indonesia"},...],"authorMeta":{"id":"6612606270220451846","name":"knowyourmeme","nickName":"Know Your Meme","verified":true,"signature":"Documenting memes...","bioLink":"https://linktr.ee/knowyourmeme","privateAccount":false,"createTime":1556120323,"following":127,"friends":47,"fans":2300000,"heart":42800000,"video":1486}}

tiktok_video — Apify clockworks~tiktok-scraper, postURLs:["https://www.tiktok.com/@espn/video/7652462228196560159"], resultsPerPage:1 실측 응답 원문:

{"id":"7652462228196560159","text":"(via blades_collector_bd/IG) #beyblade #espnsocial ","createTimeISO":"2026-06-17T20:22:04.000Z","isAd":true,"isSponsored":false,"playCount":26200000,"diggCount":1300000,"commentCount":4995,"shareCount":49900,"collectCount":54591,"hashtags":[{"id":"2110424","name":"beyblade"},{"id":"7270011888069509166","name":"espnsocial"}],"authorMeta":{"id":"6663294979903422470","name":"espn","nickName":"ESPN","verified":true,"privateAccount":false,"createTime":1551419387,"following":639,"friends":418,"fans":59900000,"heart":5700000000,"video":5632}}

tiktok_search — https://www.tiktok.com/search?q=flock%20camera 실측 응답 원문:

social-fetcher.service.ts:255-256 case 'tiktok_search': ... return buildResult(routed, 'unsupported'); (TiktokFetcher.handles=['tiktok_profile','tiktok_video']에 tiktok_search 없음, 디스패치 전에 서비스가 unsupported로 조기 반환)

id-probe(shortlink/opaque URL redirect) 실측 응답 원문:

grep 'vm.tiktok.com' tiktok_*.json → 매치 없음. 3개 샘플 파일 전부 tiktok.com/@handle[...] 형태의 canonical URL만 존재.

tiktok_video(photo) — /photo/ 게시물 실측 (https://www.tiktok.com/@adoptabledogsofmiami1/photo/7649280049534799137) 실측 응답 원문:

{"id":"7649280049534799137","isSlideshow":true,"slideshowImageLinks":[3장],"mediaUrls":[],"videoMeta":{"duration":0,"transcriptionLink":...,"subtitleLinks":...},"webVideoUrl":"https://www.tiktok.com/@adoptabledogsofmiami1/video/7649280049534799137","submittedVideoUrl":"...(동일)","textLanguage":"en","isPinned":false,"mentions":[],"detailedMentions":[],"musicMeta":{"musicId":...},"authorMeta":{"id":"7568845551296365600","name":"adoptabledogsofmiami1","createTime":1762259757,"signature":"...","bioLink":null,"profileUrl":"...","avatar":"...","commerceUserInfo":{"commerceUser":false},"ttSeller":false,"roomId":"","digg":0,...}}

embed/v2 URL을 postURLs에 그대로 투입 (https://www.tiktok.com/embed/v2/6646191924438895877) 실측 응답 원문:

{"error":{"type":"run-failed","message":"Actor run did not succeed (status: FAILED)."}}

postId authoritative 검증 — 일부러 틀린 handle + 살아있는 postId (/@zzzwronghandlezzz/video/7649280049534799137) 실측 응답 원문:

{"id":"7649280049534799137","isSlideshow":true,"authorMeta":{"id":"7568845551296365600","name":"adoptabledogsofmiami1"},"webVideoUrl":"https://www.tiktok.com/@adoptabledogsofmiami1/video/7649280049534799137"}

shortlink 리다이렉트 해석 — 실제 관측된 vm/t 링크 6건 (예: vm.tiktok.com/ZNRTtUjSM, tiktok.com/t/ZTSJ7TCua) 실측 응답 원문:

vm.tiktok.com/ZNRTtUjSM → Location: https://www.tiktok.com/@solana/video/7655366815324867854?_r=1&_t=ZN-97Vf97clmEg
vm.tiktok.com/ZGd9UfYoM → Location: https://www.tiktok.com/@thejaneguy/video/7655071949407800590?_r=1&_t=...
tiktok.com/t/ZTSJ7TCua → Location: https://www.tiktok.com/@dakota.mcmurray.w/video/7653960203552967943?_r=1&_t=...
(나머지 3건도 동일 형태로 /@handle/video/{postId} 해석)
현재 fetcher(tiktok.fetcher.ts)는 Apify clockworks~tiktok-scraper를 정확히 코드 그대로 재현한 curl로 profile/video 양쪽 모두 실제 200/201 성공 확인. sourceKey(라우터가 뽑은 '7652462228196560159')와 raw item.id가 완전 일치해 라우터↔fetcher id 정합성 문제 없음.
acquisition 요약이 지적한 '팔로워는 매핑되나 계정생성일/인증여부는 raw에 있어도 fetcher가 파싱 안 함'을 실제 raw JSON에서 authorMeta.verified=true, authorMeta.createTime=1556120323 존재로 직접 재확인(추정 아님).
profiles:[handle] 입력은 '프로필 정보' 자체가 아니라 해당 계정의 최신 영상 1건을 반환하고 그 안에 authorMeta로 프로필 정보가 동봉되는 구조 — playCount/diggCount 등 fetcher가 담는 값은 엄밀히는 '최신 영상의 참여수'이지 '계정 누적 참여수'가 아님. 코드 주석에는 명시 안 돼 있어 문서화 시 유의 필요.
tiktok_search는 라우터가 sourceType은 분류하지만 social-fetcher.service.ts에서 디스패치 전에 'unsupported'로 조기 반환되어 fetcher에 도달하지 않음(fetcher.handles에도 tiktok_search 없음) — 샘플의 status:'unsupported'와 코드가 정확히 일치, 실제 네트워크 호출 자체가 발생하지 않는 구조임을 코드 Read로 확인.
TikTok oEmbed(무료)는 video/profile 양쪽 모두 HTTP 200을 반환하나 팔로워/참여수/인증/생성일 등 acquisition이 필요하다고 정의한 필드가 전혀 없어 실질적으로 백업 fallback(존재확인/타이틀·썸네일) 이상의 가치가 없음을 실측으로 재확인.

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

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

4.1 · Creator (프로필) — tiktok (profile · authorMeta)

source: 확정 source: apify apidojo/tiktok-scraper ($0.30/1k) — fieldsDiffer=false, 현재 clockworks~tiktok-scraper 응답과 필드셋 사실상 동일. profiles:[handle] 호출은 실제로는 해당 계정의 '최신 영상 1건'을 반환하고 authorMeta 서브객체에 프로필 정보가 동봉되는 구조(계정 단위 집계 endpoint 아님) · fans/heart/video(count)/following/friends는 스크랩 시점 스냅샷 — scrapedAt과 페어링 없이 백테스트에 쓰면 look-ahead. createTime/verified는 저빈도 변경값이라 관측 윈도우 내 사실상 고정으로 취급 가능하나, age는 raw createTime만 저장하고 매 시점 as-of로 재계산해야 안전(미리 계산한 ageHours 저장은 unsafe)
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
idauthorMeta.id — 계정 고유 숫자 ID (예: '6612606270220451846')immutable⚠️ actor제공·미파싱🔑 keycontext — 조인키 용도, 그 자체로 매매결정 안 바꿈
name (handle)authorMeta.name — @핸들. 변경 가능(30일마다 1회) + 옛 핸들 타인 재할당이라 키 부적격 → 표시/해석용 속성으로만 보관(조인·dedup 금지)mutable✅ 캡처keepcontext — 사칭 판별 텍스트 비교 + URL→id 해석 경로. 조인·dedup 사용 금지(재할당 충돌)
nickNameauthorMeta.nickName — 표시이름(display name), 예: 'Know Your Meme'mutable⚠️ actor제공·미파싱skipnoise — 자유 변경 가능한 표시텍스트, 사칭 판별 보조 이상 아님
verifiedauthorMeta.verified — 인증 배지 여부(true/false)immutable⚠️ actor제공·미파싱removenoise — 검증된 원칙(verified 배지류는 구매가능이라 noise)에 따라 팀진정성 단독 결정 신호로 쓰지 않음, context 보조로만
createTimeauthorMeta.createTime — 계정 생성 unix timestamp (예: 1556120323 → 2019-04-24)immutable⚠️ actor제공·미파싱keepactionable — 러그리스크 1순위 tell(신선 계정=사칭/스캠 확률↑), raw 저장 후 as-of age 재계산 시 look-ahead safe
signature (bio)authorMeta.signature — 프로필 자기소개 텍스트mutable⚠️ actor제공·미파싱keepcontext — 팀진정성 보조 근거(공식 링크·소속 명시 여부), 단독 actionable 아님
bioLinkauthorMeta.bioLink — 프로필에 걸린 외부 링크(예: linktr.ee)mutable⚠️ actor제공·미파싱keepcontext — 크로스플랫폼 대사(website/텔레그램 링크와 일치 여부)에 보조적으로 유용
privateAccountauthorMeta.privateAccount — 비공개 계정 여부mutable⚠️ actor제공·미파싱skipnoise — 필터링용 플래그일 뿐 방향성 신호 아님
fans (followers)authorMeta.fans — 팔로워 총합mutable✅ 캡처keepcontext — viability(절대 도달) 축과 연결되나 검증된 원칙상 진입상대 수익 예측력은 약함
followingauthorMeta.following — 팔로잉 수mutable⚠️ actor제공·미파싱skipnoise — createTime+verified+fans 조합과 개념적으로 중복(redundancy), 독립신호 근거 부족
friendsauthorMeta.friends — 맞팔 수mutable⚠️ actor제공·미파싱skipnoise — following과 동일 사유로 redundant
heartauthorMeta.heart — 계정 누적 좋아요 총합(lifetime)mutable⚠️ actor제공·미파싱keepnoise — fans/video 대비 redundant, 별도 정보이득 낮음
video (count)authorMeta.video — 게시 영상 총 개수mutable⚠️ actor제공·미파싱keepnoise — 계정 성숙도 프록시로 createTime과 redundant, 약한 보조 신호
accountAgeHourscreateTime 기반 파생값(우리가 계산) — 계정 나이derivable❌ 없음skipactionable이나 unsafe — 반드시 조회 시점 as-of로 재계산, 미리 계산해 저장한 값은 look-ahead 오염
profileUrl / avatar / originalAvatarUrl / commerceUserInfo / ttSeller / roomId / digg / followDatasetUrlauthorMeta 하위 — 2026-07-29 실측에서 새로 확인된 필드들(문서 미기재였음). profileUrl=handle 파생, avatar류=이미지 URL, commerceUserInfo/ttSeller=커머스 계정 플래그, roomId=라이브 방 id(비방송 시 빈 문자열), digg=계정이 누른 좋아요 수mutable⚠️ actor제공·미파싱skipnoise — handle 파생이거나(profileUrl) 이미지/커머스 플래그라 밈신호와 무관

4.2 · Content (영상/포토) — tiktok (video/photo · postURLs)

source: 확정 source: apify apidojo/tiktok-scraper ($0.30/1k) — fieldsDiffer=false, 현재 clockworks~tiktok-scraper 응답과 필드셋 사실상 동일. postURLs:[url] 호출은 profiles 경로와 달리 해당 게시물 자체를 정확히 반환(sourceKey=postId와 raw item.id 실측 일치 확인됨) · playCount/diggCount/commentCount/shareCount/collectCount/repostCount는 스크랩 시점 스냅샷 — scrapedAt 페어링 없이 쓰면 look-ahead. text/createTimeISO/hashtags/isAd는 fixed값이라 as-of 재구성 안전. 콘텐츠 재사용(카피캣) 판정은 text+createTimeISO 조합으로 먼저 게시된 쪽만 원본으로 인정해야 look-ahead 안전(검증된 원칙: 콘텐츠 재사용=loser 신호)
필드설명값 변동성파싱 상태(현재)최종 결정트레이딩 유용성
id (postId)item.id — 게시물 고유 숫자 ID, 라우터 sourceKey(URL 마지막 세그먼트)와 실측 일치immutable✅ 캡처🔑 keycontext — 라우터가 이미 sourceKey로 보유해 payload 중복이나, dedup/조인 키로 필요
text (caption)item.text — 영상 캡션/해시태그 포함 본문immutable✅ 캡처keepactionable — 검증된 원칙(콘텐츠 재사용=loser 신호) 판정의 1차 매칭 필드, 사칭 텍스트 유사도 판별에도 사용
createTimeISOitem.createTimeISO — 게시 시각(ISO)immutable✅ 캡처keepactionable — 원본 vs 재활용(2nd+ 카피캣) 타이밍 판별 핵심 타임스탬프, look-ahead safe(fixed)
hashtags[].nameitem.hashtags — 게시물에 태깅된 해시태그 배열immutable✅ 캡처keepcontext — 동일 해시태그 co-occurrence로 조율된 프로모션(sameHandle급 캠페인) 탐지 보조
isAd / isSponsoreditem.isAd, item.isSponsored — 유료 광고/스폰서십 여부immutable✅ 캡처keepactionable — 유기적 바이럴과 구매 노출을 분리해 모멘텀 신호 오염을 방지하는 필터
playCountitem.playCount — 조회수mutable✅ 캡처keepactionable — 모멘텀(확산 속도) 핵심 지표, scrapedAt 페어링 조건부
diggCount (likes)item.diggCount — 좋아요 수mutable✅ 캡처keepcontext — 참여 깊이 보조지표, playCount와 일부 redundant
commentCountitem.commentCount — 댓글 수mutable✅ 캡처keepcontext — 참여 깊이 보조지표, 단독 actionable 아님
shareCountitem.shareCount — 공유(재공유) 수mutable✅ 캡처keepactionable — 확산/바이럴리티 직접 지표, digg/comment보다 모멘텀 신호력 높을 가능성(scrapedAt 페어링 조건부)
collectCountitem.collectCount — 북마크(저장) 수, raw엔 있으나 현재 미매핑mutable⚠️ actor제공·미파싱keepnoise — diggCount/playCount와 강한 상관 예상(redundancy), 독립신호 미검증
repostCountitem.repostCount — 재게시 수, raw엔 있으나 현재 미매핑mutable⚠️ actor제공·미파싱keepnoise — shareCount와 개념적으로 redundant
musicMetaitem.musicMeta — 사용된 배경음악/사운드 메타데이터immutable⚠️ actor제공·미파싱removecontext — 사운드 재사용은 텍스트 재사용과 다른 확산 축이나 검증된 신호 목록에 미포함, 탐색적
videoMetaitem.videoMeta — 해상도/길이 등 제작 스펙immutable⚠️ actor제공·미파싱removenoise — 토큰 성패와의 연관 근거 없음
mediaUrlsitem.mediaUrls — 실제 영상/이미지 파일 URLimmutable⚠️ actor제공·미파싱removenoise — 시각적 dedup(perceptual hash) 인프라 부재, 텍스트 기반 카피캣 탐지로 이미 커버 중
locationCreateditem.locationCreated — 게시 지역 정보immutable⚠️ actor제공·미파싱skipnoise — 러그/사칭 검증된 신호 세트(github∪브랜드사칭∪런치툴∪sameHandle)와 무관, 근거 부족
engagementRateplayCount 대비 digg/comment/share 비율 등 우리가 계산할 파생 모멘텀 지표derivable❌ 없음skipactionable이나 unsafe — raw 카운트를 scrapedAt과 함께 저장 후 as-of로만 재계산해야 look-ahead 안전
authorMeta.id (creator 링크)content 응답에 동봉된 creator 숫자 uid — 조인 성립의 필수 조건. 실측 3콜 전부 존재(예: 7568845551296365600). 추가 쿼리 불필요immutable⚠️ actor제공·미파싱🔑 keycontext — 조인키 용도, 그 자체로 매매결정 안 바꿈
authorMeta.name (관측시점 handle)게시물 조회 시점의 작성자 handle 스냅샷. content 행에 createTimeISO와 함께 남으므로 이 테이블 자체가 handle 이력 로그가 되어 as-of 해석(handle→id)을 가능케 함mutable✅ 캡처keepcontext — 사칭 텍스트 유사도 비교 + handle 이력 복원
isSlideshow2026-07-29 실측 신규 확인. photo(슬라이드쇼) 게시물이면 true. 병합된 tiktok_video 안에서 형식을 복원하는 유일한 구분자(IG의 type/productType과 동일 역할)immutable⚠️ actor제공·미파싱keepcontext — 병합 유지의 필수 조건, 형식별 참여지표 분포 차이 보정에도 필요
mentions[] / detailedMentions[]2026-07-29 실측 신규 확인. 게시물이 태그한 계정 배열(문서에 아예 없던 필드). hashtags와 대칭 구조immutable⚠️ actor제공·미파싱keepactionable — 여러 게시물이 같은 계정을 멘션 = 조율된 프로모션(sameHandle 계열)의 직접 증거
webVideoUrl / submittedVideoUrl2026-07-29 실측 신규 확인. 어떤 URL 형태로 요청해도 정규화된 canonical URL을 돌려줌 (/photo/ 요청 → /video/ 로 응답)immutable⚠️ actor제공·미파싱keepcontext — 같은 게시물이 여러 URL 형태(photo/embed/shortlink)로 관측될 때 dedup 기준
videoMeta.transcriptionLink / subtitleLinks2026-07-29 실측 신규 확인. 영상 전사·자막 링크 — videoMeta 통째 제거에서 분리해 살림. IG의 alt(AI 시각설명)에 대응하는, TikTok에서 영상 "내용"을 텍스트로 얻는 유일한 경로immutable⚠️ actor제공·미파싱keepactionable — 콘텐츠 재사용/카피캣 판별의 직접 입력(캡션만으로 못 잡는 재활용 탐지)
musicMeta.musicId사운드 고유 id — musicMeta 통째 제거에서 분리해 살림. 숫자 1개라 저장비용 무시 가능immutable⚠️ actor제공·미파싱keepcontext — 텍스트 재사용과 구분되는 별개 확산 축(같은 사운드로 퍼지는 밈)의 조인키
slideshowImageLinks[]2026-07-29 실측 신규 확인. photo 게시물의 실제 이미지 URL 배열(실측 3장)immutable⚠️ actor제공·미파싱keepcontext — 현재 perceptual-hash 인프라 부재로 즉시 활용처는 없으나 향후 비전 분석 대비
textLanguage / isPinned / effectStickers / shortDramaSeriesInfo / commentsDatasetUrl / createTime(unix)2026-07-29 실측 신규 확인(문서 미기재였음). textLanguage=캡션 언어, isPinned=프로필 고정 여부, createTime=createTimeISO와 중복(unix)immutable⚠️ actor제공·미파싱skipnoise — 약한 보조 신호이거나(textLanguage/isPinned) 기존 필드와 중복(createTime)

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

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

dataType: "tiktok_profile (Apify clockworks item, profiles:[handle] 경로 — 실제로는 최신 영상 1건+authorMeta)"

필드예시값설명
id7658394009320099085영상 id (게시물, 프로필 자체 id 아님)
textWhat actually is Tung Tung Tung Sahur?...영상 캡션 → fetcher.text (fixed)
createTimeISO2026-07-03T20:00:04.000Z영상 게시시각 → fetcher.createTimeISO (fixed)
playCount3400000조회수 → fetcher.playCount (mutable)
diggCount340900좋아요수 → fetcher.diggCount (mutable)
commentCount3276댓글수 → fetcher.commentCount (mutable)
shareCount95200공유수 → fetcher.shareCount (mutable)
collectCount미확인(이 응답엔 값 있었으나 fetcher output에 안 나감)raw에 존재하나 fetcher 미매핑
repostCountn/araw에 존재하나 fetcher 미매핑
hashtags[].name["ramadan","indonesia"]→ fetcher.hashtags (fixed, name만 추출)
isAd / isSponsoredfalse→ fetcher.isAd = isAd ?? isSponsored (fixed)
authorMeta.fans2300000팔로워 총합 → fetcher.authorFans (mutable)
authorMeta.nameknowyourmeme→ fetcher.authorName (mutable로 매핑돼있으나 사실상 불변)
authorMeta.verifiedtrueraw에 존재(true 확인)하나 fetcher 미매핑 — acquisition '인증여부 미매핑' 확인됨
authorMeta.createTime1556120323 (2019-04-24)계정 생성 unix ts, raw 존재하나 fetcher 미매핑 — acquisition '생성일 미매핑' 확인됨
authorMeta.following / friends / heart / video / diggfollowing=127raw 존재, fetcher 미매핑

dataType: "tiktok_video (Apify clockworks item, postURLs:[url] 경로 — 해당 게시물 자체)"

필드예시값설명
id7652462228196560159게시물 id, 라우터 sourceKey와 일치 확인
text / createTimeISO / playCount / diggCount / commentCount / shareCount / hashtags / isAdisAd=true(실제 광고 게시물 확인)tiktok_profile과 동일 매핑 규칙
collectCount / repostCount / locationCreated / musicMeta / videoMeta / mediaUrlscollectCount=54591raw에 존재, fetcher 완전 미사용
authorMeta.*verified=true, fans=59900000tiktok_profile과 동일 서브스키마(verified/createTime 등 미매핑 동일)
isSlideshowtrue (photo 실측)photo(슬라이드쇼) 게시물 여부 — 병합 후 형식 구분자
slideshowImageLinks[]3장photo 이미지 URL 배열
webVideoUrl / submittedVideoUrlhttps://www.tiktok.com/@adoptabledogsofmiami1/video/7649280049534799137정규화된 canonical URL
mentions[] / detailedMentions[][]게시물이 태그한 계정 배열
textLanguage / isPinneden / false캡션 언어 / 프로필 고정 여부
videoMeta.transcriptionLink / subtitleLinks(URL)영상 전사·자막 링크(영상 내용 텍스트화 경로)
musicMeta.musicId(숫자)사운드 고유 id — 사운드 재사용 조인키
authorMeta.id / .name / .signature / .bioLink / .createTimeid=7568845551296365600content 응답에 동봉된 creator 정보 — id가 조인키
authorMeta.profileUrl / .avatar / .commerceUserInfo / .ttSeller / .roomId / .diggttSeller=false2026-07-29 실측 신규 확인(미채택)
effectStickers / shortDramaSeriesInfo / commentsDatasetUrl / createTime(unix)[] / null2026-07-29 실측 신규 확인(미채택)

dataType: "TikTok oEmbed (무료 백업, /oembed)"

필드예시값설명
title(via blades_collector_bd/IG) #beyblade #espnsocial영상 캡션 또는 프로필 타이틀 문구
author_nameESPN표시 이름(nickName)
author_unique_idespn핸들(video 응답에만 존재, profile 응답엔 없음)
thumbnail_url / thumbnail_width / thumbnail_heighthttps://p16-common-sign.tiktokcdn.com/...영상 응답에만 존재
embed_typevideo'video' 또는 'profile'
html<blockquote class=...embed용 blockquote 마크업(파싱 불필요 데이터 아님)

다음 액션