# GET /v1/projects/:projectId/content/source-recommendations (/docs/api/reference/content/source-recommendations)



<Endpoint method="GET" path="/v1/projects/:projectId/content/source-recommendations" scope="content:read" phase="1" />

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

| Mode                    | Triggered by             | Source                                                             |
| ----------------------- | ------------------------ | ------------------------------------------------------------------ |
| **Portfolio** (default) | No `keyword` query param | Pre-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 [#path-parameters]

<Parameters
  rows="[
  { name: 'projectId', type: 'string (uuid)', required: true, description: 'The project.' },
]"
/>

## Query parameters [#query-parameters]

<Parameters
  rows="[
  { name: 'keyword', type: 'string', description: 'Optional. When omitted, returns portfolio results. When supplied, switches to live search against that term. 1–200 chars.' },
  { name: 'kind', type: 'string', description: 'Filter by source kind. Default `mixed`.', enum: ['video', 'slideshow', 'mixed'] },
  { name: 'limit', type: 'integer', description: 'Page size. Default 20.', enum: ['1–50'] },
  { name: 'cursor', type: 'string', description: 'Forward-paginate. Currently always `null` in responses; reserved for future use.' },
]"
/>

## Request [#request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```sh title="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"
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts title="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();
    ```
  </Tab>

  <Tab value="Python">
    ```py title="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"]
    ```
  </Tab>
</Tabs>

## Responses [#responses]

<Response status="200" description="List of TikTok source candidates.">
  ```json
  {
    "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.
</Response>

<Callout type="info">
  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).
</Callout>

## Errors [#errors]

| Code         | When                                                                          |
| ------------ | ----------------------------------------------------------------------------- |
| `VALIDATION` | `limit` outside 1–50; `kind` not in the enum; `keyword` empty or > 200 chars. |
| `NOT_FOUND`  | Project id not in this org.                                                   |

## See also [#see-also]

* [video-remix](/docs/api/reference/content/video-remix)
* [slideshow-remix](/docs/api/reference/content/slideshow-remix)
