metrix / docs TikTok

API · your roster · feeds

One profile per model. A feed tuned to each.

A model is a creator you manage. Register one with the hashtags that describe her lane, and every feed on this page — videos, creators, sounds, hashtags — comes back filtered to that lane. The model_id you get here is the same one the Creator Recommendations API uses.

01 What a model is

Everything on this page hangs off one id: register a model once, and her model_id is what every feed, subscription, and recommendation call below scopes to.

model — a creator you manage, identified by a stable model_id. You register her with a name and a few hashtags describing her content lane (e.g. cooking, recipes); we auto-expand those into topic subscriptions, and the video, sound, and hashtag feeds below filter to that model automatically.

Same id, two APIs

Recommendation runs on the Creator Recommendations page hang off this same model_id — register her here first, then seed a recommendation run against her. /suggested-creators below is the lighter-weight, always-on cousin of that heavier recs run.

Every request on this page needs Authorization: Bearer <API_KEY>. Examples below use <MODEL_ID> as a placeholder for the model_id a create call hands you.

02 Manage

Register a model, list your roster, look at one in detail, update her, or retire her.

POST /api/v1/models Register a model; her hashtags are auto-expanded and topic subscriptions created. Returns 201.

Only name is required. Hashtags run through the same expansion engine as topic groups — matched catalog topics become the model's subscriptions immediately. Link the model's own TikTok or Instagram identity with POST /models/{model_id}/linked-accounts after creation.

curl -s -X POST "https://api.metrix.ac/api/v1/models" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"docs-demo","hashtags":["cooking","recipes"]}'
{
  "id": "b7497949-473b-4531-b12a-b8fa9641ee06", "name": "docs-demo",
  "tiktok_user_id": null, "tiktok_username": null,
  "is_active": true, "onboarding_state": "ready", "topic_matches": []
}
FieldTypeNotes
namestringRequired, 1-200 chars.
hashtagsstring[]Optional, default [], max 50. Expanded into topic subscriptions on create.

tiktok_username and tiktok_user_id are not accepted create fields; either now returns 422. Linked accounts below are the sole supported writer for own-account identity.

Real capture from the call that created b7497949-473b-4531-b12a-b8fa9641ee06 — the persistent docs-demo model used as <MODEL_ID> throughout the rest of this page. topic_matches: [] here reflects the expansion outcome at creation time for this docs client's visible topics.

GET /api/v1/models List your active models.
curl -s "https://api.metrix.ac/api/v1/models" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "models": [
    { "id": "b7497949-473b-4531-b12a-b8fa9641ee06", "name": "docs-demo",
      "tiktok_username": null, "is_active": true, "onboarding_state": "ready" }
  ]
}
GET /api/v1/models/{model_id} One model with her topic subscriptions.
curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "id": "b7497949-473b-4531-b12a-b8fa9641ee06", "name": "docs-demo",
  "is_active": true, "onboarding_state": "ready",
  "topics": [
    { "topic_id": "food_cooking", "confidence": null, "is_primary": false,
      "subscribed_at": "2026-07-20T17:27:56.806884+00:00" }
  ]
}

Captured after subscribing this model to food_cooking below (card 7) — topics was empty right after creation until that call.

PATCH /api/v1/models/{model_id} Update fields; changed hashtags re-expand and diff topic subscriptions.

All fields optional — send only what changes. If hashtags is included, it's diffed against the model's current topic set: newly-matched topics are subscribed, dropped ones unsubscribed. Sending neither an updatable field nor hashtags returns 400.

curl -s -X PATCH "https://api.metrix.ac/api/v1/models/<MODEL_ID>" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"docs-demo"}'
{
  "id": "b7497949-473b-4531-b12a-b8fa9641ee06", "name": "docs-demo",
  "is_active": true, "onboarding_state": "ready",
  "updated_at": "2026-07-20T17:27:56.479328+00:00", // ← bumped from the create-time value
}

Real capture — an idempotent re-set of name against the live docs-demo model, safe to run against your own model the same way.

DELETE /api/v1/models/{model_id} Soft-delete (recoverable by support, gone from lists).

Nothing is destroyed at the storage layer — the model just stops appearing in GET /models and its feeds. Contact support to reactivate.

curl -s -X DELETE "https://api.metrix.ac/api/v1/models/<MODEL_ID>" \
  -H "Authorization: Bearer $TM_API_KEY"
{ "status": "deleted", "model_id": "4d7f646f-35d0-4b88-ae1c-2eb6e6744d4f" }

Captured against a disposable throwaway model (docs-throwaway-delete-demo) created solely to demonstrate this call — that's why the model_id above doesn't match b7497949-... used elsewhere on this page. Deleting your own active model works the same way.

Link the model's own account

Own-account linking is separate from inspiration subscriptions. It records the model's TikTok or Instagram identity, starts the supported crawl, and drives own-content exclusion and coverage reads. Repeating the POST is the on-demand refresh path; poll the GET for stable crawl status.

POST /api/v1/models/{model_id}/linked-accounts Link one TikTok or Instagram own-account. Returns 201.
curl -s -X POST "https://api.metrix.ac/api/v1/models/<MODEL_ID>/linked-accounts" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"platform":"tiktok","handle":"creator.handle"}'

Body fields are platform (tiktok or instagram) and handle. The response is the stored linked-account row plus nullable job_id; a same-day completed/deduplicated crawl can legitimately return job_id: null.

Contract derived from the route and tests on 2026-07-31; no production account was linked for this docs pass.

GET /api/v1/models/{model_id}/linked-accounts Stable poll target for linked-account and crawl status.
curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/linked-accounts" \
  -H "Authorization: Bearer $TM_API_KEY"
{ "linked_accounts": [], "total": 0 }

Schema-shaped example from the source contract, not a production capture.

DELETE /api/v1/models/{model_id}/linked-accounts/{platform} Soft-unlink; returns 204.
curl -s -X DELETE "https://api.metrix.ac/api/v1/models/<MODEL_ID>/linked-accounts/tiktok" \
  -H "Authorization: Bearer $TM_API_KEY"

Unlinking preserves collected data. TikTok unlink also clears the compatibility bridge key on the model.

03 Subscriptions

Two independent feeds compose into everything in section 04: topic subscriptions (auto-created from hashtags, or added by hand here) and creator subscriptions (specific handles you follow for this model).

GET /api/v1/models/{model_id}/topics Topics the model follows.
curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/topics" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "topics": [
    { "topic_id": "food_cooking", "confidence": null, "is_primary": false,
      "subscribed_at": "2026-07-20T17:27:56.806884+00:00" }
  ]
}
POST /api/v1/models/{model_id}/topics Manually add a topic subscription. Returns 201.

Use this when a model covers a niche her hashtags didn't auto-match. topic_id must be a topic your client can see — an unauthorized or unknown id returns 404.

curl -s -X POST "https://api.metrix.ac/api/v1/models/<MODEL_ID>/topics" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"topic_id":"food_cooking"}'
{ "status": "subscribed", "topic_id": "food_cooking" }

Real capture — this is the call that put food_cooking onto b7497949-..., which is why every feed card below (12-18) now has data to show.

DELETE /api/v1/models/{model_id}/topics/{topic_id} Remove one topic subscription.

Unsubscribing doesn't touch already-served history — it just stops this topic from feeding the model's /feed/* and /dashboard results going forward.

curl -s -X DELETE "https://api.metrix.ac/api/v1/models/<MODEL_ID>/topics/food_cooking" \
  -H "Authorization: Bearer $TM_API_KEY"
{ "status": "unsubscribed" }

Real capture — run against the live model right after the feed/dashboard captures above, to leave it back near its pre-demo state. A missing subscription returns 404.

POST /api/v1/models/{model_id}/subscriptions/creators Follow a specific creator's uploads for this model. Returns 201. Rate-limited to 10/min.

Accepts any well-formed handle, even one we've never crawled — a new subscription queues a crawl job so her videos populate within ~30-60s instead of waiting for the next daily scheduler tick.

curl -s -X POST "https://api.metrix.ac/api/v1/models/<MODEL_ID>/subscriptions/creators" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"creator_id":"yum.bubs"}'
{ "status": "subscribed", "creator_id": "yum.bubs", "job_id": 27332 }

Real capture. job_id is the enqueued crawl.creator job — null when the creator was already subscribed (no new crawl needed).

GET /api/v1/models/{model_id}/subscriptions/creators List followed creators.
curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/subscriptions/creators" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "subscriptions": [
    { "creator_id": "yum.bubs", "username": "yum.bubs",
      "last_crawl_status": "ok", "last_crawl_error": null,
      "video_count": 1, "verified": false }
  ],
  "total": 1
}

last_crawl_status is how the feed distinguishes "still discovering" from "crawl errored" or "crawl found zero videos" — surfaced as subscription_diagnostics on /feed/videos when non-ok.

DELETE /api/v1/models/{model_id}/subscriptions/creators/{creator_id} Unfollow. Returns 204, no body.

The handle is normalized before matching, so DELETE .../@Yum.Bubs removes the same subscription stored as yum.bubs.

curl -s -X DELETE "https://api.metrix.ac/api/v1/models/<MODEL_ID>/subscriptions/creators/yum.bubs" \
  -H "Authorization: Bearer $TM_API_KEY"
HTTP/1.1 204 No Content

Real capture — run right after the feed/dashboard captures above, to leave the live model back near its pre-demo state.

04 Feeds

Everything here is scoped by the model. Start with the topic-first dashboard for a one-call overview, then drill into the video, sound, or hashtag feed.

GET /api/v1/models/{model_id}/dashboard Everything at once — the combined trending overview; start here.

Top 10 topic videos (by views), unsubscribed creator suggestions (by affinity), sounds (by usage), and hashtags (by velocity) in one call. Suggested creators exclude the model's own handle and creators it already follows.

curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/dashboard" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "topic_ids": ["food_cooking"], "window": "24h",
  "videos": [
    { "video_id": "7581714645912718623", "creator_handle": "yum.bubs",
      "views": 269400000, "likes": 4600000 }, // ← 10 total, trimmed to 1
  ],
  "creators": [
    { "creator_id": "menwiththepot", "username": "menwiththepot",
      "follower_count": 12700000, "affinity_score": 0.9935 }
  ],
  "sounds": [
    { "sound_id": "6817665487665629186", "title": "Chopin Nocturne No. 2 Piano Mono", "video_count": 1151 }
  ],
  "hashtags": [
    { "hashtag": "recipes", "velocity": 2526176.22, "pct_change": 976.77 }
  ]
}

The dashboard's video lane is deliberately topic-only. Direct creator subscriptions remain available through /feed/videos?source=creator; they do not silently widen this overview.

GET /api/v1/models/{model_id}/feed/videos Trending videos in the model's lane.

?source= controls the mix: all (default) merges topic-niche videos with the model's directly-subscribed-creator videos, deduped by video_id; topic or creator narrows to just one. Sort with ?sort= (views, likes, velocity, relative_score) and window with ?window=.

curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/feed/videos?limit=3" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "videos": [
    { "video_id": "7581714645912718623", "source": "creator_sub",
      "creator_handle": "yum.bubs", "views": 269400000, "likes": 4600000 }, // ← from the followed creator
    { "video_id": "7230633191252462849", "source": "topic",
      "creator_handle": "bayashi.tiktok", "views": 256500000 }
  ],
  "total": 6, "topic_ids": ["food_cooking"], "creator_ids": ["yum.bubs"],
  "has_more": true, "next_cursor": "eyJvIjogM30="
}

source per video shows where it came from — creator_sub for the one video from yum.bubs, topic for the rest from the food_cooking niche.

GET /api/v1/models/{model_id}/feed/sounds Trending sounds from her topics.
curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/feed/sounds?limit=3" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "sounds": [
    { "sound_id": "6817665487665629186", "title": "Chopin Nocturne No. 2 Piano Mono",
      "video_count": 1151, "affinity_score": 0.5763 }, // ← 655 total, trimmed to 1
  ],
  "total": 655, "has_more": true, "next_cursor": "eyJvIjogM30="
}
GET /api/v1/models/{model_id}/feed/hashtags Trending hashtags from her topics.
curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/feed/hashtags?limit=3" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "hashtags": [
    { "hashtag": "recipes", "velocity": 2526176.22, "momentum": 45408476.33,
      "pct_change": 976.77, "latest_value": 64050450 }, // ← 1325 total, trimmed to 1
  ],
  "total": 1325, "has_more": true, "next_cursor": "eyJvIjogM30="
}
GET /api/v1/models/{model_id}/trending/creators Trending creators within her network specifically.

This ranks only the creators the model directly follows through subscriptions/creators, by velocity within that small set. Use suggested-creators to discover affinity-ranked creators outside the current follow-list.

curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/trending/creators?limit=3" \
  -H "Authorization: Bearer $TM_API_KEY"
{ "entity_type": "creator", "window": "24h", "computed_at": null, "total": 0, "results": [] }

Live-captured, genuinely empty — this model follows exactly one creator (yum.bubs, subscribed minutes before this capture) and no velocity row has been computed for her yet in the 24h window. Not a broken call; expected for a freshly-followed creator.

05 Inspiration

Two ways to find what to post next: a lightweight always-on suggestion feed, and an on-demand data refresh.

GET /api/v1/models/{model_id}/suggested-creators Affinity-ranked creators for content inspiration.

Lighter-weight than a full Creator Recommendations run — no seeds required, no run lifecycle to poll, just an instant affinity-ranked list from the model's own topics. Excludes her own handle and creators she already follows.

curl -s "https://api.metrix.ac/api/v1/models/<MODEL_ID>/suggested-creators?limit=3" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "suggestions": [
    { "creator_id": "ambreen_08", "username": "ambreen_08",
      "affinity_score": 0.9999, "topic_id": "food_cooking" }, // ← 3 total, trimmed to 1
  ],
  "total": 3
}
POST /api/v1/models/{model_id}/populate Queue an enrichment job from the model's seeds. Returns 202.

Refreshes the model's lane by crawling outward from seeds you supply — usernames, hashtags, keywords, and/or TikTok video URLs (must be tiktok.com). At least one seed type is required. depth (1-5, default 3) controls how many crawl waves run.

curl -s -X POST "https://api.metrix.ac/api/v1/models/<MODEL_ID>/populate" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"seeds":{"hashtags":["cooking","recipes"]},"depth":2,"priority":"velocity"}'
{ "id": 27333, "state": "queued", "estimated_waves": 2, "poll_url": "/api/v1/jobs/27333" }
FieldTypeNotes
seedsobjectRequired. At least one of usernames, hashtags, keywords, video_urls (each max 50, video_urls max 20).
depthintOptional, default 3, range 1-5.
prioritystringOptional, default velocity. Also views, recent.

Real capture — queues a job on the live model; safe to call whenever you want fresher data ahead of the normal discovery cycle. poll_url is the same jobs endpoint pattern used elsewhere.

06 Lists

A list is a simple named collection — videos, creators, sounds, or hashtags you want to keep together, independent of any model. Optionally attach one to a model_id at creation.

POST /api/v1/lists Create a named list to collect items into. Returns 201.
curl -s -X POST "https://api.metrix.ac/api/v1/lists" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"docs-demo-list"}'
{
  "id": "66f9a4b0-c551-4a22-80b8-36488e5edec4", "name": "docs-demo-list",
  "model_id": null, "description": null, "is_shared": false, "item_count": 0
}
FieldTypeNotes
namestringRequired.
descriptionstringOptional.
model_idstringOptional. Scope this list to one model.
is_sharedboolOptional, default false.

Real capture — 66f9a4b0-... is the persistent <LIST_ID> used for cards 21-27 below.

GET /api/v1/lists Your lists. Pass ?model_id= to filter to one model.
curl -s "https://api.metrix.ac/api/v1/lists" \
  -H "Authorization: Bearer $TM_API_KEY"
[
  { "id": "66f9a4b0-c551-4a22-80b8-36488e5edec4", "name": "docs-demo-list",
    "description": "Docs demo list for the client API reference.", "item_count": 3 }
]

Unlike most list-style routes on this site, this one returns a bare JSON array, not a {"lists": [...]} envelope — match your parsing accordingly. Verified against the route source (-> list[dict]), not an inconsistency in this capture.

GET /api/v1/lists/{list_id} One list.
curl -s "https://api.metrix.ac/api/v1/lists/<LIST_ID>" \
  -H "Authorization: Bearer $TM_API_KEY"
{
  "id": "66f9a4b0-c551-4a22-80b8-36488e5edec4", "name": "docs-demo-list",
  "description": "Docs demo list for the client API reference.",
  "is_shared": false, "item_count": 3
}
PATCH /api/v1/lists/{list_id} Rename / edit.
curl -s -X PATCH "https://api.metrix.ac/api/v1/lists/<LIST_ID>" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"description":"Docs demo list for the client API reference."}'
{
  "id": "66f9a4b0-c551-4a22-80b8-36488e5edec4", "name": "docs-demo-list",
  "description": "Docs demo list for the client API reference."
}
DELETE /api/v1/lists/{list_id} Delete a list. Returns 204, no body. Hard delete — not recoverable.
curl -s -X DELETE "https://api.metrix.ac/api/v1/lists/<LIST_ID>" \
  -H "Authorization: Bearer $TM_API_KEY"
HTTP/1.1 204 No Content

Captured against a disposable throwaway list (docs-throwaway-delete-list, id 17a18ae2-...) created solely to demonstrate this call — the persistent docs-demo-list used elsewhere on this page was never touched.

POST /api/v1/lists/{list_id}/items Add one item. Returns 201.

entity_type is one of video, creator, sound, hashtag; entity_id is that entity's id (a video_id, handle, sound_id, or hashtag string). Duplicate (list_id, entity_type, entity_id) returns 409.

curl -s -X POST "https://api.metrix.ac/api/v1/lists/<LIST_ID>/items" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"entity_type":"video","entity_id":"7581714645912718623","note":"docs demo"}'
{
  "id": "7216bd77-1d2f-49cb-8a13-3a24e8e5a272", "list_id": "66f9a4b0-c551-4a22-80b8-36488e5edec4",
  "entity_type": "video", "entity_id": "7581714645912718623", "note": "docs demo"
}
POST /api/v1/lists/{list_id}/items/batch Add many at once. Returns 201.

items is an array of the same shape as the single-item body above. Duplicates within the batch are silently skipped (ON CONFLICT DO NOTHING), not rejected.

curl -s -X POST "https://api.metrix.ac/api/v1/lists/<LIST_ID>/items/batch" \
  -H "Authorization: Bearer $TM_API_KEY" -H "Content-Type: application/json" \
  -d '{"items":[{"entity_type":"video","entity_id":"7230633191252462849"},{"entity_type":"video","entity_id":"7538614566691867917"}]}'
{ "added": 2, "list_id": "66f9a4b0-c551-4a22-80b8-36488e5edec4" }
GET /api/v1/lists/{list_id}/items Read the list back.
curl -s "https://api.metrix.ac/api/v1/lists/<LIST_ID>/items" \
  -H "Authorization: Bearer $TM_API_KEY"
[
  { "id": "00f18aa9-360d-4396-9ae6-bf7cb2b5d36a", "entity_type": "video",
    "entity_id": "7230633191252462849", "note": null },
  { "id": "5f035e2f-98b8-410f-8542-e695d20c216b", "entity_type": "video",
    "entity_id": "7538614566691867917", "note": null },
  { "id": "7216bd77-1d2f-49cb-8a13-3a24e8e5a272", "entity_type": "video",
    "entity_id": "7581714645912718623", "note": "docs demo" }
]

Same bare-array shape as GET /lists above — no envelope key, match your parsing accordingly. All 3 items just added (1 single-add + 2 batch-add).

DELETE /api/v1/lists/{list_id}/items/{item_id} Remove an item. Returns 204, no body.
curl -s -X DELETE "https://api.metrix.ac/api/v1/lists/<LIST_ID>/items/00f18aa9-360d-4396-9ae6-bf7cb2b5d36a" \
  -H "Authorization: Bearer $TM_API_KEY"
HTTP/1.1 204 No Content

Real capture — removed the video-7230633191252462849 item from docs-demo-list, leaving 2 items (7538614566691867917 and 7581714645912718623).