Layers
Partner APIAPI referenceContent

GET /v1/projects/:projectId/content/source-recommendations

List TikTok video and slideshow ids the partner can pass to video-remix or slideshow-remix.

View as Markdown
GET/v1/projects/:projectId/content/source-recommendations
Phase 1stable
Auth
Bearer
Scope
content:read

Returns TikTok ids the partner's end-user can pick from. Two backing modes:

ModeTriggered bySource
Portfolio (default)No keyword query paramPre-discovered posts from the project's content-research pipeline.
Live search?keyword=<string>Real-time TikTok keyword search.

Both modes normalize to the same item shape — partners don't branch on source for rendering. The field is informational only ("from your library" vs "live search results").

Path parameters

  • projectId
    string (uuid)required
    The project.

Query parameters

  • keyword
    stringoptional
    Optional. When omitted, returns portfolio results. When supplied, switches to live search against that term. 1–200 chars.
  • kind
    stringoptional
    Filter by source kind. Default `mixed`.
    One of: video, slideshow, mixed
  • limit
    integeroptional
    Page size. Default 20.
    One of: 1–50
  • cursor
    stringoptional
    Forward-paginate. Currently always `null` in responses; reserved for future use.

Request

terminal
# Portfolio mode (default)
curl "https://api.layers.com/v1/projects/{projectId}/content/source-recommendations?kind=video&limit=20" \
  -H "Authorization: Bearer $LAYERS_API_KEY"

# Live search
curl "https://api.layers.com/v1/projects/{projectId}/content/source-recommendations?keyword=fitness%20app" \
  -H "Authorization: Bearer $LAYERS_API_KEY"
source-recommendations.ts
const res = await fetch(
  `https://api.layers.com/v1/projects/${projectId}/content/source-recommendations?keyword=fitness+app`,
  { headers: { 'Authorization': `Bearer ${process.env.LAYERS_API_KEY}` } },
);
const { items, source } = await res.json();
source_recommendations.py
import os, httpx

r = httpx.get(
    f"https://api.layers.com/v1/projects/{project_id}/content/source-recommendations",
    params={"keyword": "fitness app"},
    headers={"Authorization": f"Bearer {os.environ['LAYERS_API_KEY']}"},
)
items = r.json()["items"]

Responses

200List of TikTok source candidates.
{
  "items": [
    {
      "tiktokId": "7234567890123456789",
      "kind": "video",
      "thumbnailUrl": "https://...",
      "caption": "...",
      "author": { "username": "creator_handle", "displayName": "..." },
      "stats": { "views": 1240000, "likes": 89000, "comments": 4100, "shares": 12000 }
    },
    {
      "tiktokId": "7234567890987654321",
      "kind": "slideshow",
      "thumbnailUrl": "https://...",
      "caption": "...",
      "author": { "username": "creator_handle", "displayName": "..." },
      "stats": { "views": 800000, "likes": 56000, "comments": 1200, "shares": 4400 },
      "imageCount": 7
    }
  ],
  "source": "portfolio",
  "nextCursor": null
}

source is "portfolio" when the response was served from the project's pre-discovered library, "live" when served from a real-time keyword search.

Portfolio mode draws from the project's pre-discovered library (synced posts + previously-generated containers). Fresh projects typically return a small seeded set immediately after creation while the first research sweep runs in the background; the library fills out as posts are synced. Projects that genuinely have no library yet return an empty items array with source: "portfolio" — fall back to a keyword search in that case (or surface the empty state in your UI).

Errors

CodeWhen
VALIDATIONlimit outside 1–50; kind not in the enum; keyword empty or > 200 chars.
NOT_FOUNDProject id not in this org.

See also

On this page