> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streamforge.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Content Analysis

> Analyzing streams, videos, and content metrics across platforms

Learn how to fetch, filter, and analyze content (streams, videos, and posts) from creators and platforms.

## Content Endpoints

The API provides several ways to access content:

* `GET /platforms/{platform}/content/{content_id}` - Get single content item
* `GET /platforms/{platform}/content` - List all content for a platform
* `GET /platforms/{platform}/profiles/{profile_id}/content` - List content by creator
* `GET /platforms/{platform}/games/{game_id}/content` - List content for a game
* `POST /platforms/{platform}/content` - Get multiple content items

## Content Types

Content types vary by platform:

| Platform  | Content Types            |
| --------- | ------------------------ |
| Twitch    | `stream`, `video`        |
| YouTube   | `video`, `short`         |
| TikTok    | `video`, `short`         |
| Instagram | `post`, `video`, `story` |

<Warning>
  Instagram stories are only available via direct ID lookup and are excluded from list operations. Story availability is limited and depends on internal data collection processes - do not expect comprehensive story coverage for any given creator.
</Warning>

## Getting Content by Creator

Fetch all content from a specific creator:

```javascript theme={null}
async function getCreatorContent(platform, profileId) {
  let cursor = null;
  const allContent = [];

  do {
    const response = await fetch(
      `/platforms/${platform}/profiles/${profileId}/content?limit=50${cursor ? `&cursor=${cursor}` : ''}`,
      {
        headers: { 'x-api-key': 'YOUR_API_KEY' }
      }
    );

    const data = await response.json();
    allContent.push(...data.payload);
    cursor = data.meta.paging.next_cursor;
  } while (cursor !== null);

  return allContent;
}
```

## Content statistics

Content items include detailed statistics:

```json theme={null}
{
  "id": "987654321",
  "title": "Awesome Stream",
  "type": "stream",
  "created_at": "2024-07-04T10:00:00.000Z",
  "ended_at": "2024-07-04T11:30:00.000Z",
  "duration": 5400000,
  "statistics": {
    "viewers": {
      "avg": 2345.6,
      "max": 6789
    },
    "watched_minutes": {
      "sum": 123456
    },
    "views": {
      "sum": 45678
    },
    "comments": {
      "sum": 900
    }
  }
}
```

### Understanding Statistics

* **`statistics.views.sum`**: Total view count
* **`statistics.viewers.avg`**: Average concurrent viewers (for streams)
* **`statistics.viewers.max`**: Peak concurrent viewers
* **`statistics.watched_minutes.sum`**: Total watch time in minutes
* **`duration`**: Content duration in milliseconds

## Analyzing Content Performance

### Calculate Average Views

```javascript theme={null}
function calculateAverageViews(content) {
  const views = content
    .map(item => item.statistics?.views?.sum || 0)
    .filter(views => views > 0);

  if (views.length === 0) return 0;

  const sum = views.reduce((a, b) => a + b, 0);
  return sum / views.length;
}
```

### Find Top Performing Content

```javascript theme={null}
function getTopContent(content, metric = 'views', limit = 10) {
  return content
    .sort((a, b) => {
      // Handle nested statistics like views.sum
      const aValue = metric === 'views'
        ? (a.statistics?.views?.sum || 0)
        : (a.statistics?.[metric]?.sum || a.statistics?.[metric] || 0);
      const bValue = metric === 'views'
        ? (b.statistics?.views?.sum || 0)
        : (b.statistics?.[metric]?.sum || b.statistics?.[metric] || 0);
      return bValue - aValue;
    })
    .slice(0, limit);
}

// Usage
const topVideos = getTopContent(content, 'views', 5);
```

### Calculate Engagement Rate

```javascript theme={null}
function calculateEngagementRate(content) {
  return content.map(item => {
    const views = item.statistics?.views?.sum || 0;
    const comments = item.statistics?.comments?.sum || 0;
    const engagementRate = views > 0 ? (comments / views) * 100 : 0;

    return {
      ...item,
      engagementRate: engagementRate.toFixed(2)
    };
  });
}
```

## Time-Based Analysis

### Filter by Date Range

```javascript theme={null}
function filterByDateRange(content, startDate, endDate) {
  const start = new Date(startDate);
  const end = new Date(endDate);

  return content.filter(item => {
    const createdAt = new Date(item.created_at);
    return createdAt >= start && createdAt <= end;
  });
}

// Usage
const recentContent = filterByDateRange(
  content,
  '2024-01-01',
  '2024-12-31'
);
```

### Analyze Trends Over Time

```javascript theme={null}
function analyzeTrends(content) {
  // Group by month
  const byMonth = content.reduce((acc, item) => {
    const date = new Date(item.created_at);
    const month = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;

    if (!acc[month]) {
      acc[month] = { count: 0, totalViews: 0 };
    }

    acc[month].count++;
    acc[month].totalViews += item.statistics?.views?.sum || 0;

    return acc;
  }, {});

  return Object.entries(byMonth).map(([month, stats]) => ({
    month,
    contentCount: stats.count,
    totalViews: stats.totalViews,
    averageViews: stats.totalViews / stats.count
  }));
}
```

## Platform-Specific Features

### Twitch Chapters

Twitch streams include aggregated chapter data:

```json theme={null}
{
  "chapters": [
    {
      "id": "chapter-1",
      "title": "Opening",
      "created_at": "2024-07-04T10:00:00.000Z",
      "ended_at": "2024-07-04T10:30:00.000Z",
      "duration": 1800000,
      "statistics": {
        "viewers": {
          "avg": 2000,
          "max": 4000
        }
      }
    }
  ]
}
```

Analyze chapter performance:

```javascript theme={null}
function analyzeChapters(stream) {
  if (!stream.chapters || stream.chapters.length === 0) {
    return null;
  }

  return stream.chapters.map(chapter => ({
    title: chapter.title,
    duration: chapter.duration / 1000 / 60, // minutes
    avgViewers: chapter.statistics?.viewers?.avg || 0,
    peakViewers: chapter.statistics?.viewers?.max || 0
  }));
}
```

### Categories and Tags

Content includes categories (games) and tags:

```json theme={null}
{
  "categories": [
    {
      "id": "1234",
      "name": "Some Game",
      "type": "twitch_game",
      "igdb_id": 5678,
      "igdb_name": "Some Game",
      "igdb_slug": "some-game"
    }
  ],
  "tags": ["gaming", "live"]
}
```

Find most common games:

```javascript theme={null}
function findMostCommonGames(content) {
  const gameCounts = {};

  content.forEach(item => {
    item.categories?.forEach(category => {
      if (category.type.includes('game')) {
        const gameName = category.igdb_name || category.name;
        gameCounts[gameName] = (gameCounts[gameName] || 0) + 1;
      }
    });
  });

  return Object.entries(gameCounts)
    .sort(([, a], [, b]) => b - a)
    .map(([game, count]) => ({ game, count }));
}
```

## Content Comparison

### Compare Creators

```javascript theme={null}
async function compareCreators(platform, profileIds) {
  const results = await Promise.all(
    profileIds.map(id =>
      fetch(`/platforms/${platform}/profiles/${id}/content?limit=10`)
        .then(r => r.json())
    )
  );

  return results.map((data, index) => ({
    profileId: profileIds[index],
    contentCount: data.payload.length,
    totalViews: data.payload.reduce(
      (sum, item) => sum + (item.statistics?.views?.sum || 0),
      0
    ),
    averageViews: calculateAverageViews(data.payload)
  }));
}
```

### Compare Time Periods

```javascript theme={null}
function comparePeriods(content, period1, period2) {
  const p1 = filterByDateRange(content, period1.start, period1.end);
  const p2 = filterByDateRange(content, period2.start, period2.end);

  return {
    period1: {
      count: p1.length,
      totalViews: p1.reduce((sum, item) => sum + (item.statistics?.views?.sum || 0), 0),
      avgViews: calculateAverageViews(p1)
    },
    period2: {
      count: p2.length,
      totalViews: p2.reduce((sum, item) => sum + (item.statistics?.views?.sum || 0), 0),
      avgViews: calculateAverageViews(p2)
    },
    change: {
      count: ((p2.length - p1.length) / p1.length * 100).toFixed(1),
      views: ((p2.reduce((sum, item) => sum + (item.statistics?.views?.sum || 0), 0) -
               p1.reduce((sum, item) => sum + (item.statistics?.views?.sum || 0), 0)) /
              p1.reduce((sum, item) => sum + (item.statistics?.views?.sum || 0), 0) * 100).toFixed(1)
    }
  };
}
```

## Best Practices

<Tip>
  Use bulk endpoints when fetching multiple content items. It's more efficient than individual requests.
</Tip>

* **Paginate efficiently**: Use appropriate `limit` values (10-50) based on your needs
* **Cache content data**: Content metrics don't change frequently; cache for performance
* **Filter client-side**: Use date ranges and filters to reduce unnecessary requests
* **Handle missing metrics**: Some content may not have all metrics; handle nulls gracefully
* **Monitor quota**: List endpoints consume quota per item returned

## Use Cases

### Content Performance Dashboard

```javascript theme={null}
async function buildContentDashboard(platform, profileId) {
  const content = await getCreatorContent(platform, profileId);

  return {
    totalContent: content.length,
    totalViews: content.reduce((sum, item) => sum + (item.statistics?.views?.sum || 0), 0),
    averageViews: calculateAverageViews(content),
    topContent: getTopContent(content, 'views', 10),
    trends: analyzeTrends(content),
    commonGames: findMostCommonGames(content)
  };
}
```

### Content Discovery

```javascript theme={null}
async function discoverTrendingContent(platform, limit = 20) {
  const response = await fetch(
    `/platforms/${platform}/content?limit=${limit}&sort=views&order=desc`,
    {
      headers: { 'x-api-key': 'YOUR_API_KEY' }
    }
  );

  return response.json();
}
```

<Warning>
  Content list endpoints consume quota equal to the number of items returned. Use appropriate `limit` values to manage quota usage.
</Warning>
