BIO.RE
Discover

Get Trending Creators

Public trending creator list. Multi-factor scoring (7-day window) cached in Redis 15min. Falls back to totalMessages sort if scoring fails. CDN-cached 5min.

GET /api/v1/discover/trending โ€” ๐ŸŒ Public ยท Rate limit: 60 req / minute ยท Kill-switched

Returns the platform's trending creators. Cached in Redis (15min TTL) so reads are constant-time after the first warm. Underlying scoring is a multi-factor signal computed over a 7-day window; on scoring-engine failure, the endpoint falls back to a simple totalMessages DESC sort.

Cached ranking is preserved on read. When the cache hits, the server fetches the listed creatorProfile rows by id and re-sorts in-memory to match the cached ranking. This means even if a creator's stats changed since the cache was warmed, their position in trending reflects the snapshot at warm-time โ€” not the live state.

limit clamps to admin-managed default. When ?limit is absent, the server reads discover.trending_count (default 10). When supplied, the value still goes through Min(1) / Max(50) validation.

Request

Query parameters

ParamTypeDefaultValidationNotes
limitnumberdiscover.trending_count (admin, default 10)Min(1), Max(50), @Type(Number)Number of trending creators to return

No headers required.

Response headers

HeaderValue
Cache-Controlpublic, s-maxage=300, stale-while-revalidate=600

Response

200 OK โ€” ApiResponseOf<CreatorListDto>

{
  "success": true,
  "data": {
    "creators": [
      {
        "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
        "userId": "u1a2b3c4-d5e6-7890-abcd-ef1234567890",
        "username": "topcreator",
        "displayName": "Top Creator",
        "avatarUrl": "https://cdn.bio.re/avatars/abc.webp",
        "level": "GOLD",
        "dmActive": true,
        "totalFollowers": 50000
      }
    ]
  }
}
FieldTypeNotes
creatorsCreatorCardDto[]Up to limit cards, ordered by trending rank (cached snapshot or live scoring)

See Search for the full CreatorCardDto field reference.

Errors

HTTPcode / i18nKeyReason
400(DTO validation)limit outside [1, 50]
429(throttle)Rate limit exceeded (60 req/min)
503features.discover_disabledAdmin kill switch DISCOVER is active

Side effects

  1. Resolve count = limit ?? config.discover.trending_count ?? 10.
  2. Cache lookup โ€” redis.cacheGet('discover:trending:<count>'). Hit:
    • Fetch creatorProfile.findMany({ where: id IN cached.ids AND active filter }).
    • In-memory re-sort to match cached id order.
    • Return.
  3. Cache miss: try multi-factor scoring (computeTrending). On failure: log warn + fallback to totalMessages DESC.
  4. Map rows โ†’ toCard() and return.

Code samples

curl https://api.bio.re/api/v1/discover/trending
curl 'https://api.bio.re/api/v1/discover/trending?limit=20'
async function getTrendingCreators(limit?: number): Promise<CreatorCard[]> {
  const url = new URL('https://api.bio.re/api/v1/discover/trending');
  if (limit) url.searchParams.set('limit', String(limit));
  const res = await fetch(url);
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Trending fetch failed'), {
      code: json?.error?.code,
    });
  }
  return json.data.creators;
}
import { useQuery } from '@tanstack/react-query';

export const discoverKeys = {
  trending: (limit?: number) => ['discover', 'trending', limit ?? 'default'] as const,
};

export function useTrending(limit?: number) {
  return useQuery({
    queryKey: discoverKeys.trending(limit),
    queryFn: async () => {
      const url = new URL('/api/v1/discover/trending', window.location.origin);
      if (limit) url.searchParams.set('limit', String(limit));
      const res = await fetch(url);
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Trending fetch failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data.creators as CreatorCard[];
    },
    staleTime: 5 * 60_000, // match the CDN s-maxage
  });
}

Try it

GET
/api/v1/discover/trending

Query Parameters

limit?number

Number of trending creators to return

Range1 <= value <= 50

Response Body

application/json

curl -X GET "https://loading/api/v1/discover/trending"
{
  "success": true,
  "data": {
    "creators": [
      {
        "id": "cuid_creator_123",
        "userId": "2c4a230c-5085-4924-a3e1-25fb4fc5965b",
        "username": "johndoe",
        "displayName": "John Doe",
        "avatarUrl": "https://cdn.bio.re/avatars/abc.jpg",
        "level": "BRONZE",
        "dmActive": false,
        "totalFollowers": 0
      }
    ]
  }
}

Source

SourcePathLines
Controllerapps/api-core/src/modules/discover/discover.controller.ts43โ€“49 (trending)
DTO (request)apps/api-core/src/modules/discover/dto/index.ts41โ€“44 (TrendingQueryDto)
DTO (response)apps/api-core/src/modules/discover/dto/discover-response.dto.ts52โ€“55 (CreatorListDto)
Serviceapps/api-core/src/modules/discover/discover.service.ts98โ€“124 (getTrending), computeTrending (scoring), getTrendingFallback (totalMessages sort)
Configapps/api-core/src/modules/config/config.service.tsdiscover.trending_count (admin-managed default 10)
CacheRedisdiscover:trending:<count> key, 15min TTL
Prisma modelpackages/prisma/prisma/schema.prismaCreatorProfile (active filter)

On this page