WordPress REST API Guide for 2026

TL;DR: The WordPress REST API lets you read and write WordPress data from any application using standard HTTP requests. This guide covers every core concept you need in 2026: endpoints, authentication methods, custom routes, rate limiting, and real working code examples.

Last Updated: May 2026. Tested against WordPress 6.7 running on PHP 8.3.

The WordPress REST API guide most developers need in 2026 goes well beyond a basic introduction. Whether you’re building a mobile app, a decoupled front end, or a third-party integration, the REST API is the backbone of modern WordPress development. Introduced in WordPress 4.7, the API is now stable, well-documented, and used in production by thousands of sites worldwide. This guide covers everything from core endpoints to writing custom routes, handling authentication securely, and defending against abuse through rate limiting.

WordPress REST API guide showing endpoint structure and JSON response

What Is the WordPress REST API?

Try SiteForge Free →

The WordPress REST API is a programmatic interface that exposes WordPress data as JSON objects over standard HTTP. When a client sends a request to a REST endpoint, WordPress returns structured data, no theme rendering, no HTML soup, just clean JSON that any application can parse.

The base URL for every request follows this pattern:

https://yourdomain.com/wp-json/wp/v2/

From that base, each resource type has its own namespace. The API ships with namespaces for posts, pages, media, users, categories, tags, comments, taxonomies, post types, and more. Third-party plugins and your own custom code can register additional namespaces at any time.

The WordPress REST API also powers the block editor (Gutenberg) internally. Every save action in the editor is an authenticated REST API call. That alone tells you how production-ready and battle-tested the API is in 2026.

Core REST API Endpoints You Need to Know in 2026

The following table summarizes the most commonly used built-in endpoints. All endpoints accept optional query parameters to filter, paginate, and sort results.

Endpoint Method Action Best For
/wp/v2/posts GET List published posts Headless front ends, mobile apps
/wp/v2/posts/{id} GET Fetch a single post Dynamic post pages
/wp/v2/posts POST Create a post Automation, content pipelines
/wp/v2/posts/{id} PUT / PATCH Update a post Editorial workflows
/wp/v2/posts/{id} DELETE Trash a post Content management tools
/wp/v2/pages GET List pages Static page rendering
/wp/v2/media GET / POST List or upload media Media libraries, upload tools
/wp/v2/users/me GET Fetch current user Auth checks, profile pages
/wp/v2/categories GET List categories Navigation menus, filtering
/wp/v2/search GET Site-wide search Search interfaces, autocomplete

A simple GET request to list the ten most recent posts looks like this:

GET https://yourdomain.com/wp-json/wp/v2/posts?per_page=10&_fields=id,title,slug,link

The _fields parameter limits the response to only the properties you need, which significantly reduces payload size and improves response time. This is a best practice for any production WordPress REST API implementation.

WordPress REST API Authentication: Three Methods Compared

Public GET requests to published content require no authentication. Write operations, draft access, and user data always require authentication. In 2026 there are three practical authentication methods for the WordPress REST API.

Method How It Works Best For Security Level
Application Passwords Generate a token in the user profile, pass via Basic Auth header Server-to-server integrations, plugins High (requires HTTPS)
JWT Authentication Exchange credentials for a signed token, send in Authorization header SPAs, mobile apps, headless front ends High (short-lived tokens)
Cookie + Nonce WordPress session cookie plus X-WP-Nonce header On-site JavaScript (e.g., block editor) Medium (same origin only)

Using Application Passwords (Recommended for 2026)

Application Passwords have been a core WordPress feature since version 5.6. To generate one, navigate to Users, select your account, scroll to the Application Passwords section, and give it a name. WordPress generates a 24-character token.

Pass the token using HTTP Basic Auth. In a PHP context:

$response = wp_remote_post( 'https://yourdomain.com/wp-json/wp/v2/posts', array(
    'headers' => array(
        'Authorization' => 'Basic ' . base64_encode( 'your_username:xxxx xxxx xxxx xxxx xxxx xxxx' ),
        'Content-Type'  => 'application/json',
    ),
    'body' => json_encode( array(
        'title'   => 'My New Post',
        'content' => 'Post body here.',
        'status'  => 'draft',
    ) ),
) );

Replace the token with your actual Application Password. Note the spaces in the token are significant; do not remove them before base64-encoding the string. This request must go over HTTPS. Sending Application Passwords over plain HTTP is a critical security error.

JWT Authentication for Headless Projects

For headless WordPress setups where a Next.js or Nuxt front end needs to authenticate users, JWT is the preferred approach. The JWT Authentication for WP REST API plugin (by Enrique Chavez, available on WordPress.org) adds a /wp-json/jwt-auth/v1/token endpoint. Post credentials to receive a signed token, then send that token as a Bearer header on subsequent requests:

// Get token
const res = await fetch('https://yourdomain.com/wp-json/jwt-auth/v1/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username: 'editor', password: 'secure_password' }),
});
const { token } = await res.json();

// Use token in subsequent requests
const posts = await fetch('https://yourdomain.com/wp-json/wp/v2/posts?status=draft', {
  headers: { 'Authorization': `Bearer ${token}` },
});

If you are building a full headless WordPress setup, see our guide on WordPress Headless CMS in 2026 for architecture decisions, caching strategies, and framework recommendations.

Registering Custom REST API Routes in WordPress

The WordPress REST API is designed to be extended. You register custom routes using the register_rest_route() function inside the rest_api_init action hook. A minimal example that returns site statistics:

add_action( 'rest_api_init', function () {
    register_rest_route( 'myplugin/v1', '/stats', array(
        'methods'             => WP_REST_Server::READABLE, // GET
        'callback'            => 'myplugin_get_stats',
        'permission_callback' => '__return_true', // public endpoint
    ) );
} );

function myplugin_get_stats( WP_REST_Request $request ) {
    $post_count = wp_count_posts()->publish;
    return rest_ensure_response( array(
        'published_posts' => (int) $post_count,
        'version'         => '1.0',
    ) );
}

The route above would be accessible at /wp-json/myplugin/v1/stats. Several key rules apply when registering custom routes:

  • Always use a versioned namespace (e.g., myplugin/v1) so future changes do not break existing consumers.
  • Never set 'permission_callback' => '__return_true' on endpoints that expose private data or perform write operations.
  • Use WP_REST_Server::READABLE, WP_REST_Server::EDITABLE, and WP_REST_Server::DELETABLE constants instead of raw strings.
  • Validate and sanitize all incoming parameters using the args key in your route definition.

Adding Parameter Validation to Custom Routes

Here is a more complete example with parameter validation. This endpoint accepts a category ID and returns post titles:

add_action( 'rest_api_init', function () {
    register_rest_route( 'myplugin/v1', '/posts-by-category', array(
        'methods'             => WP_REST_Server::READABLE,
        'callback'            => 'myplugin_posts_by_category',
        'permission_callback' => '__return_true',
        'args'                => array(
            'category_id' => array(
                'required'          => true,
                'validate_callback' => function( $param ) {
                    return is_numeric( $param ) && absint( $param ) > 0;
                },
                'sanitize_callback' => 'absint',
            ),
        ),
    ) );
} );

function myplugin_posts_by_category( WP_REST_Request $request ) {
    $cat_id = $request->get_param( 'category_id' );
    $posts   = get_posts( array(
        'category'    => $cat_id,
        'numberposts' => 10,
        'post_status' => 'publish',
    ) );
    $data = array_map( fn( $p ) => array( 'id' => $p->ID, 'title' => $p->post_title ), $posts );
    return rest_ensure_response( $data );
}

The validate_callback runs before the main callback and returns a WP_Error or boolean. The sanitize_callback transforms the raw value before it reaches your code. This pattern is required for any production endpoint.

WordPress REST API custom route registration code example in a PHP file

WordPress REST API Rate Limiting: What You Need to Know

WordPress core does not include built-in rate limiting for the REST API. That means a misconfigured client or a malicious actor can hammer your endpoints until the server runs out of resources. In 2026, rate limiting is a non-negotiable part of any production REST API setup.

There are three layers where you can implement rate limiting:

1. Server-Level Rate Limiting (Nginx / Apache)

The most performant approach limits requests before they reach PHP. In Nginx, add a limit zone to your server block:

http {
    limit_req_zone $binary_remote_addr zone=wp_api:10m rate=30r/m;
}

server {
    location ~ ^/wp-json/ {
        limit_req zone=wp_api burst=10 nodelay;
    }
}

This configuration allows 30 requests per minute per IP address with a burst allowance of 10. Requests that exceed the limit receive a 429 Too Many Requests response, which clients should respect and handle with exponential back-off.

2. WordPress Plugin Rate Limiting

If server configuration is not accessible (common on shared hosting), use a plugin. The WP REST API Rate Limiting plugin or a general security plugin like Wordfence can throttle REST API traffic at the application layer. Wordfence includes a brute force protection option that applies to the REST API authentication endpoints, which is particularly important for JWT and Application Password endpoints.

3. Custom Rate Limiting with Transients

For fine-grained per-user rate limiting inside a custom route, use WordPress transients to track request counts:

function myplugin_check_rate_limit( WP_REST_Request $request ) {
    $ip        = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    $key       = 'rate_limit_' . md5( $ip );
    $count     = (int) get_transient( $key );
    $limit     = 60; // requests per hour

    if ( $count >= $limit ) {
        return new WP_Error(
            'rate_limit_exceeded',
            'Too many requests. Please wait before trying again.',
            array( 'status' => 429 )
        );
    }

    if ( 0 === $count ) {
        set_transient( $key, 1, HOUR_IN_SECONDS );
    } else {
        set_transient( $key, $count + 1, HOUR_IN_SECONDS );
    }

    return true;
}

Pass this function as the permission_callback in your route definition. It allows 60 requests per IP per hour and returns a proper 429 error when the limit is exceeded. For high-traffic APIs, replace transients with Redis or Memcached to avoid database load.

Extending Existing Endpoints with Custom Fields

A frequent requirement is adding custom fields, such as ACF data or post meta, to existing REST API responses. Use register_rest_field() rather than hacking core response objects:

add_action( 'rest_api_init', function () {
    register_rest_field( 'post', 'featured_video_url', array(
        'get_callback'    => function ( $post_data ) {
            return get_post_meta( $post_data['id'], '_featured_video_url', true );
        },
        'update_callback' => function ( $value, $post ) {
            update_post_meta( $post->ID, '_featured_video_url', sanitize_url( $value ) );
        },
        'schema'          => array(
            'description' => 'URL of the featured video for this post.',
            'type'        => 'string',
            'format'      => 'uri',
        ),
    ) );
} );

The featured_video_url field now appears in every post response and is writable via PUT/PATCH requests. Always include a schema definition to make the endpoint self-documenting and compatible with tools like the Swagger / OpenAPI ecosystem.

Filtering, Pagination, and Ordering API Responses

The default REST API endpoints support a rich set of query parameters. Understanding these prevents you from over-fetching data or writing unnecessary custom routes.

Pagination: Use per_page (1–100) and page to step through large result sets. The response headers include X-WP-Total and X-WP-TotalPages for building pagination UI without an extra request.

GET /wp-json/wp/v2/posts?per_page=5&page=2&orderby=date&order=desc

Filtering by taxonomy: Pass category or tag IDs directly as query parameters.

GET /wp-json/wp/v2/posts?categories=12,34&tags=7

Searching: The search parameter queries the post title and content.

GET /wp-json/wp/v2/posts?search=headless+wordpress

Embedding related data: Add _embed to include author, featured media, and terms in a single response, eliminating multiple round trips.

GET /wp-json/wp/v2/posts?_embed&per_page=5

For more on how the REST API powers custom content structures, see our guide to WordPress Custom Post Types. Every CPT you register with 'show_in_rest' => true automatically gets full CRUD endpoints at its own namespace.

Security Best Practices for the WordPress REST API

The REST API expands your site’s attack surface. These practices are required for any site exposing write endpoints or user data.

  • Always use HTTPS. All authentication credentials and tokens must travel over encrypted connections. WordPress itself warns you if you attempt to use Application Passwords over HTTP.
  • Restrict user enumeration. By default, the /wp/v2/users endpoint is public and reveals usernames. Restrict it with a permission callback that requires authentication, or disable user enumeration via a filter.
  • Validate permission callbacks rigorously. Never leave 'permission_callback' => '__return_true' on a route that touches private data or modifies anything.
  • Log and monitor API access. Use a plugin or your hosting’s access logs to detect unusual patterns, such as a single IP requesting thousands of pages or repeatedly hitting auth endpoints.
  • Disable the REST API for non-logged-in users if you do not need public access. The rest_authentication_errors filter lets you return a WP_Error for unauthenticated requests to any endpoint.

For a full security hardening walkthrough that complements REST API lockdown, see our Wordfence Tutorial. Wordfence’s firewall rules specifically target REST API authentication abuse patterns.

The official REST API handbook at developer.wordpress.org/rest-api is the authoritative reference for every endpoint schema, argument, and response shape. The RFC 7807 Problem Details specification, which WordPress follows for error responses, is worth bookmarking for client-side error handling.

REST API and Headless WordPress in 2026

The REST API is the foundation of every decoupled WordPress architecture. In a headless setup, WordPress handles content management while a JavaScript framework like Next.js, Nuxt, or Astro handles presentation. The REST API serves content to the front end on demand.

While GraphQL (via WPGraphQL plugin) has gained traction for headless builds because of its query flexibility, the REST API remains the simpler, zero-dependency option that works without additional plugins. For most projects, the REST API’s filtering and embedding capabilities cover the majority of data-fetching requirements.

Our complete WordPress Headless CMS guide covers architecture decisions, ISR versus SSR rendering strategies, and how to structure your REST API calls for optimal build performance.

If you are hosting a REST API-heavy WordPress site and noticing slow response times, the single most impactful change is switching to quality managed WordPress hosting. GigaPress WordPress hosting plans include object caching (Redis), full-page caching, and PHP 8.3 out of the box, all of which dramatically accelerate REST API response times.

Want a Pro WordPress Site in Minutes?

SiteForge builds you a full WordPress site in about 15 minutes — AI handles layout, styling, content, and images. Free to design, only pay when you’re ready to go live. If you’re building a headless front end or REST API-powered app on top of WordPress, SiteForge gets your backend site live fast so you can focus on the front-end experience.

Frequently Asked Questions

What is the WordPress REST API base URL?

The WordPress REST API base URL is https://yourdomain.com/wp-json/. Core WordPress endpoints live under the wp/v2/ namespace, so a full endpoint looks like https://yourdomain.com/wp-json/wp/v2/posts. You can verify the API is enabled by visiting that base URL in a browser, which returns a JSON discovery document listing all registered routes.

How do I authenticate with the WordPress REST API?

The recommended authentication method in 2026 is Application Passwords, a built-in WordPress feature since version 5.6. Generate a token in your user profile and pass it via an HTTP Basic Authorization header over HTTPS. For headless applications with user login flows, JWT Authentication (via plugin) is the preferred approach. Cookie plus nonce authentication is available for JavaScript running on the same domain.

How do I create a custom REST API endpoint in WordPress?

Use the register_rest_route() function inside the rest_api_init action hook. Provide a versioned namespace (e.g., myplugin/v1), a route path, the HTTP method, a callback function, and a permission callback. The permission callback controls who can call the endpoint and must never be omitted. Your custom endpoint will be accessible at /wp-json/myplugin/v1/your-route.

Does the WordPress REST API support rate limiting?

WordPress core does not include built-in rate limiting. You should implement it at the server level using Nginx or Apache directives, at the plugin level using a security or rate-limiting plugin, or inside custom routes using WordPress transients to track per-IP request counts. All three approaches are covered in this guide with working code examples.

How do I disable the WordPress REST API for logged-out users?

Add the following code to your theme’s functions.php file or a custom plugin to block all unauthenticated REST API requests:

add_filter( 'rest_authentication_errors', function( $result ) {
    if ( ! empty( $result ) ) {
        return $result;
    }
    if ( ! is_user_logged_in() ) {
        return new WP_Error(
            'rest_not_logged_in',
            'REST API access is restricted to authenticated users.',
            array( 'status' => 401 )
        );
    }
    return $result;
} );

Note that this also blocks Gutenberg from loading for logged-out visitors, which is usually acceptable. If you use the block editor for your site, test thoroughly before deploying this to production.

Similar Posts