WrytzeWrytze Docs
Guides

Blog Listing Page

Build a blog listing page with pagination, filtering, and search.

This guide covers the API patterns for building a blog listing page, regardless of your framework. You will learn how to fetch blogs, paginate results, filter by category or tag, search by title, and combine multiple filters.

If you are using a specific framework, check the dedicated guides for complete implementations:

Fetching blogs

The blog listing endpoint returns a paginated array of published blog posts. By default, results are sorted by publishedAt in descending order (newest first).

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://app.wrytze.com/api/v1/blogs?website_id=ws_your_website_id"

The response contains two top-level fields:

FieldTypeDescription
dataBlog[]Array of blog objects for the current page
paginationPaginationPagination metadata

Each blog object in the data array includes the fields listed in the Quickstart response reference.

Pagination

Control pagination with the page and limit query parameters.

ParameterTypeDefaultDescription
pagenumber1Page number (1-indexed)
limitnumber20Number of results per page (max 100)
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://app.wrytze.com/api/v1/blogs?website_id=ws_abc&page=2&limit=12"

The pagination object in the response provides everything you need to build navigation controls:

{
  "pagination": {
    "page": 2,
    "limit": 12,
    "total": 42,
    "pages": 4
  }
}
FieldTypeDescription
pagenumberCurrent page number
limitnumberResults per page
totalnumberTotal number of matching blogs across all pages
pagesnumberTotal number of pages at the current limit

Use the pagination metadata to determine when to show previous and next links:

// Previous page exists when page > 1
const hasPreviousPage = pagination.page > 1
const previousPage = pagination.page - 1

// Next page exists when current page < total pages
const hasNextPage = pagination.page < pagination.pages
const nextPage = pagination.page + 1

When building pagination URLs, preserve any existing query parameters (category, tag, search) alongside the page number so filters persist across page navigation.

Category filtering

Filter blogs by category using the category query parameter with the category slug.

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://app.wrytze.com/api/v1/blogs?website_id=ws_abc&category=technology"

The pagination.total in the response reflects the filtered count, so your pagination links automatically adjust.

Tag filtering

Filter blogs by tag using the tag query parameter with the tag slug.

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://app.wrytze.com/api/v1/blogs?website_id=ws_abc&tag=react"

Search for blogs by title using the search query parameter. The search is case-insensitive and matches partial titles.

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://app.wrytze.com/api/v1/blogs?website_id=ws_abc&search=getting%20started"

URL-encode the search parameter value. Spaces should be encoded as %20 or +.

Combining filters

Multiple filters can be used together in a single request. All filters are applied with AND logic, meaning results must match every specified filter.

curl -H "X-API-Key: YOUR_API_KEY" \
  "https://app.wrytze.com/api/v1/blogs?website_id=ws_abc&category=technology&tag=react&search=hooks&page=1&limit=10"

Query parameter reference

Here is a summary of all query parameters accepted by the blog listing endpoint:

ParameterTypeDefaultDescription
website_idstringrequiredYour website ID
pagenumber1Page number (1-indexed)
limitnumber20Results per page (max 100)
categorystringFilter by category slug
tagstringFilter by tag slug
searchstringSearch blogs by title (case-insensitive, partial match)

Building a complete listing UI

Here is a framework-agnostic example of how to construct a listing page URL from user selections:

function buildBlogListingUrl(filters: {
  page?: number
  category?: string
  tag?: string
  search?: string
}): string {
  const params = new URLSearchParams()

  if (filters.page && filters.page > 1) {
    params.set('page', String(filters.page))
  }
  if (filters.category) {
    params.set('category', filters.category)
  }
  if (filters.tag) {
    params.set('tag', filters.tag)
  }
  if (filters.search) {
    params.set('search', filters.search)
  }

  const queryString = params.toString()
  return `/blog${queryString ? `?${queryString}` : ''}`
}

// Examples:
buildBlogListingUrl({ category: 'technology' })
// → "/blog?category=technology"

buildBlogListingUrl({ category: 'technology', page: 3 })
// → "/blog?category=technology&page=3"

buildBlogListingUrl({ search: 'react hooks', tag: 'react' })
// → "/blog?search=react+hooks&tag=react"

Sorting

Results are sorted by publishedAt in descending order by default. The most recently published blogs appear first. This sort order is not currently configurable through query parameters.

Error handling

When the API returns an error, the response includes a JSON body with an error field:

{
  "error": {
    "message": "Invalid or missing API key. Provide a valid key in the X-API-Key header.",
    "status": 401
  }
}

Common errors for the listing endpoint:

HTTP StatusDescription
400A query parameter has an invalid value (e.g., limit=0)
401The API key is missing, invalid, or revoked
429You have exceeded the rate limit (100 req/min)
500An unexpected server error occurred

Always handle error responses in your code. Displaying a user-friendly empty state or error message is better than a broken page.

Next steps

On this page