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:
| Field | Type | Description |
|---|---|---|
data | Blog[] | Array of blog objects for the current page |
pagination | Pagination | Pagination 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.
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number (1-indexed) |
limit | number | 20 | Number 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
}
}| Field | Type | Description |
|---|---|---|
page | number | Current page number |
limit | number | Results per page |
total | number | Total number of matching blogs across all pages |
pages | number | Total number of pages at the current limit |
Building pagination links
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 + 1When 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
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:
| Parameter | Type | Default | Description |
|---|---|---|---|
website_id | string | required | Your website ID |
page | number | 1 | Page number (1-indexed) |
limit | number | 20 | Results per page (max 100) |
category | string | — | Filter by category slug |
tag | string | — | Filter by tag slug |
search | string | — | Search 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 Status | Description |
|---|---|
400 | A query parameter has an invalid value (e.g., limit=0) |
401 | The API key is missing, invalid, or revoked |
429 | You have exceeded the rate limit (100 req/min) |
500 | An 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.