BIO.RE
Content

List Help Articles

Public paginated list of published help articles. Filters by category slug, search term (title/content/excerpt ILIKE), featured-only, locale. Each item carries embedded category. CDN 5min/10min SWR.

GET /api/v1/public/help/articles โ€” ๐ŸŒ Public ยท Rate limit: 60 req / minute

Paginated list of published help articles. Supports four filters: ?category=<slug>, ?search=<term> (case-insensitive substring match across title + content + excerpt), ?featured=true (featured articles only), ?locale=<code>. Each item is the lightweight projection (no full body) plus the embedded category ({ name, slug }).

Search is substring, not full-text. ?search=billing matches "Billing FAQ" and "How to handle billing disputes". The match runs as ILIKE over title, content, and excerpt (any of the three) with mode: 'insensitive'. No tokenization, no ranking โ€” substring contains.

?featured=true requires the literal string 'true'. Anything else (including '1', 'yes') falls back to "no featured filter". The check is featured === 'true'. Pass case-sensitive true.

Request

Query parameters

ParamTypeDefaultValidationNotes
pagenumber1ParseIntPipe, server-clamped to >= 11-based page index
limitnumber20ParseIntPipe, server-clamped to [1, 50]Items per page
categorystringโ€”โ€”Filter by HelpCategory.slug
searchstringโ€”โ€”Case-insensitive substring across title/content/excerpt
featuredstringโ€”โ€”Pass literal 'true' for featured-only; anything else = no filter
localestringโ€”โ€”Filter by locale (e.g. en, tr)

No headers required.

Response headers

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

Response

200 OK โ€” ApiResponseOf<PublicHelpArticleListDataDto>

{
  "success": true,
  "data": {
    "items": [
      {
        "slug": "how-to-withdraw",
        "title": "How to Withdraw Funds",
        "excerpt": "Step-by-step guide to withdrawing earnings.",
        "icon": "wallet",
        "readTimeMinutes": 5,
        "isFeatured": true,
        "publishedAt": "2026-04-29T20:00:00.000Z",
        "category": { "name": "Payments & Billing", "slug": "payments" }
      }
    ],
    "total": 30,
    "page": 1,
    "limit": 20,
    "totalPages": 2
  }
}

Item fields

FieldTypeNotes
slugstringPass to GET /public/help/articles/:slug for the body
titlestringDisplay title
excerptstring | nullShort summary for cards
iconstring | nullIcon identifier
readTimeMinutesnumber | nullEstimated read time
isFeaturedbooleanWhether the article is in the "featured" set
publishedAtstring (ISO 8601) | nullWhen published
categoryobject{ name, slug } of the parent HelpCategory

Top-level fields

FieldTypeNotes
itemsarrayUp to limit articles, ordered by publishedAt DESC
totalnumberTotal matching the filter
page / limit / totalPagesnumberEchoed pagination metadata

Errors

HTTPcode / i18nKeyReason
429(throttle)Rate limit exceeded (60 req/min)

Side effects

  1. Clamp page = max(page, 1), limit = min(max(limit, 1), 50).
  2. Build where:
    • status: PUBLISHED (always).
    • ?category โ†’ { category: { slug: categorySlug } }.
    • ?featured === 'true' โ†’ { isFeatured: true }.
    • ?locale โ†’ { locale }.
    • ?search โ†’ OR: [{ title: { contains: search, mode: 'insensitive' } }, { content: { contains } }, { excerpt: { contains } }].
  3. In parallel: helpArticle.findMany({ where, orderBy: publishedAt desc, include: category, skip, take }) + count({ where }).
  4. Project category to { name, slug }.
  5. Return paginated envelope. No mutations.

Code samples

# Featured articles in payments category
curl 'https://api.bio.re/api/v1/public/help/articles?category=payments&featured=true&limit=20'

# Search for "withdraw"
curl 'https://api.bio.re/api/v1/public/help/articles?search=withdraw'
type HelpCategoryEmbed = { name: string; slug: string };

type HelpArticleListItem = {
  slug: string;
  title: string;
  excerpt: string | null;
  icon: string | null;
  readTimeMinutes: number | null;
  isFeatured: boolean;
  publishedAt: string | null;
  category: HelpCategoryEmbed;
};

type HelpArticleListData = {
  items: HelpArticleListItem[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
};

async function listHelpArticles(filter: {
  page?: number;
  limit?: number;
  category?: string;
  search?: string;
  featured?: boolean;
  locale?: string;
} = {}): Promise<HelpArticleListData> {
  const url = new URL('https://api.bio.re/api/v1/public/help/articles');
  if (filter.page) url.searchParams.set('page', String(filter.page));
  if (filter.limit) url.searchParams.set('limit', String(filter.limit));
  if (filter.category) url.searchParams.set('category', filter.category);
  if (filter.search) url.searchParams.set('search', filter.search);
  if (filter.featured) url.searchParams.set('featured', 'true');
  if (filter.locale) url.searchParams.set('locale', filter.locale);
  const res = await fetch(url);
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Help articles fetch failed'), {
      code: json?.error?.code,
    });
  }
  return json.data;
}
import { useQuery, keepPreviousData } from '@tanstack/react-query';

export const contentKeys = {
  helpArticles: (filter: Record<string, unknown>) =>
    ['content', 'help', 'articles', filter] as const,
};

export function useHelpArticles(filter: {
  page?: number;
  limit?: number;
  category?: string;
  search?: string;
  featured?: boolean;
  locale?: string;
} = {}) {
  return useQuery({
    queryKey: contentKeys.helpArticles(filter),
    queryFn: async () => {
      const url = new URL('/api/v1/public/help/articles', window.location.origin);
      if (filter.page) url.searchParams.set('page', String(filter.page));
      if (filter.limit) url.searchParams.set('limit', String(filter.limit));
      if (filter.category) url.searchParams.set('category', filter.category);
      if (filter.search) url.searchParams.set('search', filter.search);
      if (filter.featured) url.searchParams.set('featured', 'true');
      if (filter.locale) url.searchParams.set('locale', filter.locale);
      const res = await fetch(url);
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Help articles fetch failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data as HelpArticleListData;
    },
    placeholderData: keepPreviousData,
    staleTime: 5 * 60_000,
  });
}

Try it

GET
/api/v1/public/help/articles

Query Parameters

page?number
limit?number
category?string

Filter by category slug

search?string

Search in title/content/excerpt

featured?string

Only featured articles

locale?string

Response Body

application/json

curl -X GET "https://loading/api/v1/public/help/articles"
{
  "success": true,
  "data": {
    "items": [
      {
        "slug": "how-to-withdraw",
        "title": "How to Withdraw Funds",
        "excerpt": "string",
        "icon": "string",
        "readTimeMinutes": 0,
        "isFeatured": false,
        "publishedAt": "2019-08-24T14:15:22Z",
        "category": {
          "slug": "payments",
          "name": "Payments & Billing"
        }
      }
    ],
    "total": 30,
    "page": 1,
    "limit": 20,
    "totalPages": 2
  }
}

Source

SourcePathLines
Controllerapps/api-core/src/modules/content/public-content.controller.ts310โ€“350 (listPublishedHelpArticles)
DTO (response)apps/api-core/src/modules/content/dto/content-public-response.dto.ts239โ€“254 (PublicHelpArticleListDataDto), 210โ€“234 (PublicHelpArticleListItemDto + PublicHelpCategoryEmbedDto)
Serviceapps/api-core/src/modules/content/content.service.ts751โ€“776 (listPublishedHelpArticles)
Prisma modelspackages/prisma/prisma/schema.prismaHelpArticle (status = PUBLISHED, optional isFeatured filter), HelpCategory (joined)

On this page