LESSON 2 โฑ๏ธ 12 min read

Querying Data: Filtering, Pagination & Embedding

Query Parameters

The REST API accepts URL parameters to filter and customize responses.

Pagination

Control how many results you get:

# Get 5 posts per page
/wp-json/wp/v2/posts?per_page=5

# Get page 2 of results
/wp-json/wp/v2/posts?per_page=5&page=2

# Maximum per_page is 100
/wp-json/wp/v2/posts?per_page=100

Handling Pagination in JavaScript

async function getAllPosts() {
    const allPosts = [];
    let page = 1;
    let totalPages = 1;
    
    do {
        const response = await fetch(`/wp-json/wp/v2/posts?per_page=100&page=${page}`);
        const posts = await response.json();
        
        totalPages = parseInt(response.headers.get('X-WP-TotalPages'));
        allPosts.push(...posts);
        page++;
    } while (page <= totalPages);
    
    return allPosts;
}

Filtering by Fields

By Category or Tag

# Posts in category ID 5
/wp-json/wp/v2/posts?categories=5

# Posts in multiple categories (OR)
/wp-json/wp/v2/posts?categories=5,6,7

# Posts with tag ID 10
/wp-json/wp/v2/posts?tags=10

# Combine filters (AND)
/wp-json/wp/v2/posts?categories=5&tags=10

By Author

# Posts by author ID 1
/wp-json/wp/v2/posts?author=1

# Exclude an author
/wp-json/wp/v2/posts?author_exclude=2

By Status

# Published posts (default)
/wp-json/wp/v2/posts?status=publish

# Draft posts (requires authentication)
/wp-json/wp/v2/posts?status=draft

# Multiple statuses
/wp-json/wp/v2/posts?status=publish,draft

By Date

# Posts after a date
/wp-json/wp/v2/posts?after=2026-01-01T00:00:00

# Posts before a date
/wp-json/wp/v2/posts?before=2026-12-31T23:59:59

# Posts in a date range
/wp-json/wp/v2/posts?after=2026-01-01&before=2026-06-30

Searching

# Search posts
/wp-json/wp/v2/posts?search=wordpress

# Search is performed on title, content, and excerpt
Note: Search is case-insensitive but may be slow on large sites. Consider using a dedicated search plugin with API support.

Ordering Results

# Order by date (default: descending)
/wp-json/wp/v2/posts?orderby=date&order=desc

# Order by title alphabetically
/wp-json/wp/v2/posts?orderby=title&order=asc

# Order by menu_order (for pages)
/wp-json/wp/v2/pages?orderby=menu_order&order=asc

Available orderby values

ValueDescription
datePost date
modifiedLast modified date
titleAlphabetical title
slugAlphabetical slug
idPost ID
authorAuthor ID
relevanceSearch relevance (with search param)
menu_orderPage menu order

Selecting Specific Fields

Reduce response size by requesting only needed fields:

# Only get id, title, and link
/wp-json/wp/v2/posts?_fields=id,title,link

# Useful for performance
/wp-json/wp/v2/posts?_fields=id,title,excerpt,featured_media

Response with _fields

[
    {
        "id": 123,
        "title": {"rendered": "My Post"},
        "link": "https://site.com/my-post/"
    }
]

Embedding Related Data

Instead of making multiple requests, embed related resources:

# Embed author, featured image, terms
/wp-json/wp/v2/posts?_embed

# The response now includes _embedded object

Embedded Response Structure

{
    "id": 123,
    "title": {"rendered": "My Post"},
    "featured_media": 456,
    "_embedded": {
        "author": [{
            "id": 1,
            "name": "John Doe",
            "avatar_urls": {...}
        }],
        "wp:featuredmedia": [{
            "id": 456,
            "source_url": "https://site.com/image.jpg",
            "media_details": {...}
        }],
        "wp:term": [
            [{"id": 5, "name": "Category Name"}],
            [{"id": 10, "name": "Tag Name"}]
        ]
    }
}

Practical Example: Blog Listing

async function getBlogPosts(options = {}) {
    const {
        page = 1,
        perPage = 10,
        category = null,
        search = null
    } = options;
    
    const params = new URLSearchParams({
        page,
        per_page: perPage,
        _embed: true,
        _fields: 'id,title,excerpt,link,date,featured_media,_links'
    });
    
    if (category) params.append('categories', category);
    if (search) params.append('search', search);
    
    const response = await fetch(`/wp-json/wp/v2/posts?${params}`);
    
    return {
        posts: await response.json(),
        total: parseInt(response.headers.get('X-WP-Total')),
        totalPages: parseInt(response.headers.get('X-WP-TotalPages'))
    };
}

// Usage
const { posts, total, totalPages } = await getBlogPosts({
    page: 1,
    perPage: 12,
    category: 5
});

Filtering Custom Post Types

Custom post types are exposed at their own endpoints:

# Products (WooCommerce)
/wp-json/wc/v3/products

# Custom post type "events"
/wp-json/wp/v2/events?status=publish&orderby=date
Tip: Custom post types need 'show_in_rest' => true in their registration to appear in the API.

Next Steps

In the next lesson, we'll learn how to authenticate requests and perform write operations โ€“ creating, updating, and deleting content.

๐ŸŽฏ Lesson Complete! You can now query WordPress data with advanced filtering, pagination, and embedding.