Skip to main content
WordPress DevelopmentJune 5, 2026

WordPress REST API: Practical Guide for Developers

Learn the WordPress REST API with practical developer examples for routes, authentication, custom endpoints, permissions, security, and performance.

Category: WordPress Development

The WordPress REST API is one of the most useful tools for developers who want to connect WordPress with JavaScript apps, mobile apps, dashboards, automation tools, external services, headless frontends, and custom plugin workflows.

Instead of treating WordPress only as a PHP-rendered CMS, the REST API lets you work with WordPress data through structured JSON endpoints. You can read posts, create pages, update users, upload media, manage custom data, and expose your own plugin features through custom routes.

This guide explains the WordPress REST API from a practical developer angle: how routes work, how authentication works, how to fetch and update data, how to register custom endpoints, how to secure those endpoints, and what mistakes to avoid in production.

TL;DR: What Developers Should Know

The WordPress REST API lets developers interact with WordPress using JSON over HTTP. Use it when you need a headless frontend, custom admin interface, mobile app, external integration, plugin endpoint, or JavaScript-driven feature. Public content can usually be read without authentication, but creating, editing, deleting, or accessing private data requires authentication and permissions. Use Application Passwords for server-to-server scripts, cookie/nonces for logged-in browser requests, and permission_callback for every custom endpoint. Always validate inputs, sanitize outputs, check capabilities, paginate large responses, and avoid exposing sensitive data.

What Is the WordPress REST API?

The WordPress REST API is a set of URLs that allow applications to send and receive WordPress data as JSON. A developer can use these endpoints to retrieve posts, create content, upload media, update settings, manage users, or build custom workflows.

The default API root usually looks like this:

https://example.com/wp-json/

A posts endpoint usually looks like this:

https://example.com/wp-json/wp/v2/posts

In plain English, the REST API turns WordPress data into structured resources that another app can request, filter, create, update, or delete depending on authentication and permissions.

Common developer use cases:

  • Building a React, Vue, Next.js, or Nuxt frontend powered by WordPress content.
  • Creating a custom dashboard for editors, clients, or internal teams.
  • Connecting WordPress to a CRM, ERP, app, or automation workflow.
  • Publishing posts from an external tool.
  • Uploading media programmatically.
  • Creating custom plugin endpoints.
  • Replacing slow or messy admin-ajax.php workflows with cleaner API routes.
  • Building mobile apps that read or write WordPress content.

Official reference: WordPress REST API Handbook.

REST API Basics: Routes, Endpoints, and Methods

In WordPress REST API terminology, a route is the URL path and an endpoint is the callback behavior attached to that route. A route can support one or more HTTP methods.

Common HTTP methods:

Method Purpose WordPress Example
GET Read data List posts or fetch one page
POST Create data Create a new post
PUT / PATCH Update data Update an existing post
DELETE Delete data Delete a post, user, or custom item

Default examples:

# List posts
GET /wp-json/wp/v2/posts

# Get one post
GET /wp-json/wp/v2/posts/123

# List pages
GET /wp-json/wp/v2/pages

# List categories
GET /wp-json/wp/v2/categories

# List media
GET /wp-json/wp/v2/media

Official reference: REST API Posts Reference.

How to Fetch Posts With JavaScript

A simple frontend request can fetch public posts from WordPress without authentication.

fetch('https://example.com/wp-json/wp/v2/posts')
  .then((response) => response.json())
  .then((posts) => {
    console.log(posts);
  })
  .catch((error) => {
    console.error('REST API error:', error);
  });

You can limit fields to reduce response size:

fetch('https://example.com/wp-json/wp/v2/posts?_fields=id,date,slug,title,excerpt,link')
  .then((response) => response.json())
  .then((posts) => console.log(posts));

For production, avoid pulling unnecessary fields. Large REST responses can slow dashboards, headless frontends, and mobile apps.

Pagination: Do Not Pull Everything at Once

WordPress REST API collection endpoints are paginated. This matters when a site has hundreds or thousands of posts, products, users, or custom records.

Pagination examples:

# First 10 posts
GET /wp-json/wp/v2/posts

# Page 2
GET /wp-json/wp/v2/posts?page=2

# 50 posts per request
GET /wp-json/wp/v2/posts?per_page=50

# Maximum common per_page value
GET /wp-json/wp/v2/posts?per_page=100

A good app should loop through pages instead of requesting everything in one huge call.

JavaScript pagination example:

async function getAllPosts() {
  let page = 1;
  let allPosts = [];
  let totalPages = 1;

  do {
    const response = await fetch(
      `https://example.com/wp-json/wp/v2/posts?per_page=100&page=${page}`
    );

    if (!response.ok) {
      throw new Error(`Request failed: ${response.status}`);
    }

    totalPages = Number(response.headers.get('X-WP-TotalPages')) || 1;

    const posts = await response.json();
    allPosts = allPosts.concat(posts);
    page++;
  } while (page <= totalPages);

  return allPosts;
}

Official reference: REST API Pagination.

Use _fields to Make Responses Smaller

The _fields parameter helps reduce response size by returning only the fields your app actually needs.

GET /wp-json/wp/v2/posts?_fields=id,slug,title,link

This is useful for:

  • Mobile apps.
  • Headless frontend lists.
  • Custom dashboards.
  • Admin tools that only need IDs and titles.
  • Performance-sensitive API calls.

You can also use _embed when you need related resources, such as featured media, but do not use it blindly because embedded responses can become heavier.

GET /wp-json/wp/v2/posts?_embed

Official reference: REST API Global Parameters.

Authentication: Public vs Protected Requests

Many public REST API requests do not require authentication. For example, public posts and pages can usually be read through the API.

But authenticated requests are required when you want to:

  • Create posts.
  • Update posts.
  • Delete content.
  • Upload media.
  • Access private content.
  • Read protected metadata.
  • Manage users.
  • Run custom endpoints that require permissions.

Common authentication methods:

Method Best For Notes
Cookie + nonce Logged-in browser/admin requests Good for plugin/admin JavaScript inside WordPress
Application Passwords Server-to-server scripts and integrations Built into WordPress and revocable per integration
OAuth/JWT/plugin auth Specific apps or external auth systems Usually plugin or custom implementation

Official reference: REST API Authentication.

Application Passwords: Practical Authentication for Scripts

Application Passwords are useful when an external script, app, or integration needs authenticated access to WordPress without using the user’s main login password.

Create an Application Password:

  1. Log in to WordPress.
  2. Go to Users → Profile.
  3. Find the Application Passwords section.
  4. Add a descriptive name, such as Deployment Script.
  5. Generate the password and copy it immediately.
  6. Store it securely because WordPress shows it only once.

cURL example:

curl --user "username:application-password" \
  https://example.com/wp-json/wp/v2/users/me

Create a draft post with cURL:

curl --user "username:application-password" \
  -X POST https://example.com/wp-json/wp/v2/posts \
  -H "Content-Type: application/json" \
  -d '{
    "title": "API Draft Post",
    "content": "Created through the WordPress REST API.",
    "status": "draft"
  }'

Security rules:

  • Use HTTPS only.
  • Give the API user the minimum role required.
  • Use a unique Application Password per integration.
  • Revoke unused credentials.
  • Never expose Application Passwords in frontend JavaScript.
  • Never commit credentials into Git.

Official reference: Application Passwords.

If you are building a plugin or admin screen that runs JavaScript inside logged-in WordPress, cookie authentication with REST nonces is usually the right approach.

PHP: Pass REST data to JavaScript

wp_enqueue_script(
    'my-plugin-admin',
    plugin_dir_url( __FILE__ ) . 'admin.js',
    array( 'wp-api-fetch' ),
    '1.0.0',
    true
);

wp_localize_script(
    'my-plugin-admin',
    'MyPluginSettings',
    array(
        'root'  => esc_url_raw( rest_url() ),
        'nonce' => wp_create_nonce( 'wp_rest' ),
    )
);

JavaScript: Send nonce with request

fetch(`${MyPluginSettings.root}my-plugin/v1/report`, {
  method: 'GET',
  headers: {
    'X-WP-Nonce': MyPluginSettings.nonce
  }
})
  .then((response) => response.json())
  .then((data) => console.log(data));

This is suitable for admin tools, custom dashboards, block editor extensions, and logged-in plugin screens. It is not the right method for external server-to-server integrations.

How to Register a Custom REST API Endpoint

Use register_rest_route() inside the rest_api_init hook. Every custom route should have a namespace, route path, method, callback, and permission callback.

Basic public endpoint:

add_action( 'rest_api_init', function () {
    register_rest_route( 'fyrepress/v1', '/status', array(
        'methods'             => 'GET',
        'callback'            => 'fyrepress_get_status',
        'permission_callback' => '__return_true',
    ) );
} );

function fyrepress_get_status() {
    return rest_ensure_response( array(
        'status'  => 'ok',
        'version' => '1.0.0',
    ) );
}

Public endpoints should only expose data that is safe for visitors, bots, and external tools to read.

Official references: Adding Custom Endpoints and register_rest_route().

Secure Custom Endpoint With Permissions

A protected endpoint should check the current user’s capability before returning sensitive data or making changes.

Admin-only endpoint:

add_action( 'rest_api_init', function () {
    register_rest_route( 'fyrepress/v1', '/settings', array(
        'methods'             => 'GET',
        'callback'            => 'fyrepress_get_settings',
        'permission_callback' => function () {
            return current_user_can( 'manage_options' );
        },
    ) );
} );

function fyrepress_get_settings() {
    return rest_ensure_response( array(
        'cache_enabled' => true,
        'debug_mode'    => false,
    ) );
}

Good permission checks:

  • current_user_can( 'manage_options' ) for admin settings.
  • current_user_can( 'edit_posts' ) for editor-level content tools.
  • current_user_can( 'edit_post', $post_id ) for post-specific editing.
  • current_user_can( 'read_private_posts' ) for private content access.

Do not use __return_true for endpoints that expose private data, user data, configuration values, logs, tokens, customer data, order data, form entries, or admin-only actions.

Validate and Sanitize Request Arguments

A REST API endpoint should not trust input just because it comes from your own app. Validate arguments, sanitize values, and return useful errors.

Endpoint with arguments:

add_action( 'rest_api_init', function () {
    register_rest_route( 'fyrepress/v1', '/post-summary/(?P<id>\d+)', array(
        'methods'             => 'GET',
        'callback'            => 'fyrepress_get_post_summary',
        'permission_callback' => '__return_true',
        'args'                => array(
            'id' => array(
                'required'          => true,
                'validate_callback' => function ( $param ) {
                    return is_numeric( $param ) && (int) $param > 0;
                },
                'sanitize_callback' => 'absint',
            ),
        ),
    ) );
} );

function fyrepress_get_post_summary( WP_REST_Request $request ) {
    $post_id = absint( $request['id'] );
    $post    = get_post( $post_id );

    if ( ! $post || 'publish' !== $post->post_status ) {
        return new WP_Error(
            'fyrepress_not_found',
            'Post not found.',
            array( 'status' => 404 )
        );
    }

    return rest_ensure_response( array(
        'id'      => $post_id,
        'title'   => get_the_title( $post_id ),
        'excerpt' => wp_strip_all_tags( get_the_excerpt( $post_id ) ),
        'link'    => get_permalink( $post_id ),
    ) );
}

Validation checklist:

  • Use absint() for IDs.
  • Use sanitize_text_field() for plain text.
  • Use sanitize_email() for emails.
  • Use esc_url_raw() for stored URLs.
  • Use capability checks before returning private data.
  • Return WP_Error with useful HTTP status codes.

Return Proper REST Responses and Errors

A good API should be predictable. Do not return random strings, raw PHP errors, or inconsistent response formats.

Success response:

return rest_ensure_response( array(
    'success' => true,
    'data'    => array(
        'message' => 'Settings saved.',
    ),
) );

Error response:

return new WP_Error(
    'fyrepress_invalid_request',
    'The request is missing a valid post ID.',
    array( 'status' => 400 )
);

Common HTTP status codes:

Status Meaning Use Case
200 OK Successful read or update
201 Created New resource created
400 Bad Request Invalid or missing input
401 Unauthorized Authentication required
403 Forbidden User is authenticated but lacks permission
404 Not Found Resource does not exist
500 Server Error Unexpected server-side failure

Add Custom Fields to Existing REST Responses

Sometimes you do not need a full custom endpoint. You only need to add a field to an existing REST API response.

Add reading time to posts:

add_action( 'rest_api_init', function () {
    register_rest_field(
        'post',
        'reading_time',
        array(
            'get_callback' => function ( $post_arr ) {
                $content = get_post_field( 'post_content', $post_arr['id'] );
                $words   = str_word_count( wp_strip_all_tags( $content ) );
                return max( 1, ceil( $words / 200 ) );
            },
            'schema' => array(
                'description' => 'Estimated reading time in minutes.',
                'type'        => 'integer',
                'context'     => array( 'view', 'edit' ),
            ),
        )
    );
} );

This can be useful for headless blogs, custom dashboards, editorial tools, and frontend apps that need extra metadata.

Expose Custom Post Types in the REST API

If your plugin or theme registers a custom post type, it will not always appear in the REST API unless you enable REST support.

Custom post type example:

register_post_type( 'project', array(
    'label'        => 'Projects',
    'public'       => true,
    'show_in_rest' => true,
    'supports'     => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
) );

This creates a REST route similar to:

/wp-json/wp/v2/project

For custom taxonomies, also use show_in_rest if the taxonomy should be available to the block editor or external API consumers.

register_taxonomy( 'project_type', 'project', array(
    'label'        => 'Project Types',
    'public'       => true,
    'show_in_rest' => true,
) );

REST API vs admin-ajax.php

WordPress developers often ask whether they should use the REST API or admin-ajax.php. For modern structured data workflows, the REST API is usually cleaner.

Feature REST API admin-ajax.php
Data format Structured JSON Whatever the callback returns
Routes Clean endpoint URLs Single Ajax endpoint with action names
HTTP methods GET, POST, PUT/PATCH, DELETE Usually POST or GET with action
Permissions Built around permission callbacks Manual checks in callbacks
Best for Modern apps, plugins, integrations Legacy admin Ajax actions

Use the REST API for new structured endpoints. Use admin-ajax.php only when legacy compatibility or plugin context makes it necessary.

Security Checklist for WordPress REST API

REST API security mistakes can expose private data or create dangerous write endpoints. Every custom endpoint should be designed as if someone outside your site will try to call it.

Security checklist:

  • Always add permission_callback.
  • Use __return_true only for intentionally public data.
  • Check capabilities with current_user_can().
  • Validate and sanitize every request parameter.
  • Do not expose API keys, access tokens, logs, server paths, or private options.
  • Do not return full user objects unless necessary.
  • Do not expose customer, order, form, membership, or LMS data without strict permission checks.
  • Rate-limit expensive endpoints if needed.
  • Use HTTPS for authenticated requests.
  • Revoke Application Passwords that are no longer used.
  • Log suspicious API errors carefully without storing secrets.

Performance Checklist for REST API Endpoints

REST API endpoints can become slow if they run heavy queries, return huge responses, or call external services on every request.

Performance checklist:

  • Paginate large collections.
  • Use _fields to reduce response size.
  • Cache expensive computed data.
  • Avoid unbounded WP_Query calls.
  • Do not run slow external API calls during every request.
  • Use object cache for repeated queries.
  • Return IDs and summaries when full objects are not needed.
  • Avoid meta_query abuse on large databases.
  • Benchmark custom endpoints before exposing them publicly.
  • Use server logs or monitoring to detect heavy API traffic.

FyrePress tool: If REST requests are causing slow logs or server errors, review the log excerpts with the FyrePress Server Log Analyzer.

Common REST API Problems and Fixes

Problem: 404 on /wp-json/

Check permalinks, rewrite rules, security plugins, server config, and whether REST routes are blocked.

Problem: 401 Unauthorized

Authentication is missing or invalid. Check Application Password format, HTTPS, user credentials, and authorization headers.

Problem: 403 Forbidden

The user is authenticated but does not have the required capability. Review your permission_callback.

Problem: CORS errors

The browser is blocking cross-origin requests. Review allowed origins carefully. Do not blindly allow every origin for authenticated endpoints.

Problem: Slow API responses

Check database queries, response size, external API calls, cache, and whether the endpoint is returning too much data.

Problem: Custom endpoint works for admins but not visitors

Review permissions. If the endpoint is intended to be public, use a safe public response and permission_callback => '__return_true'. If it is protected, use authentication and capabilities.

Problem: REST API blocked by security plugin

Check security plugin firewall rules. Some plugins restrict REST API access aggressively, which can break the block editor, apps, and integrations.

Debugging REST API Requests

Debug REST API issues from multiple angles: browser, server, WordPress logs, and API client.

Useful debugging tools:

  • Browser DevTools Network tab.
  • Postman or Insomnia.
  • cURL from terminal.
  • WordPress debug log.
  • Server error logs.
  • Query Monitor.
  • REST route index at /wp-json/.

Enable debug logging:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Test with cURL:

curl -i https://example.com/wp-json/wp/v2/posts?per_page=1

Look at status code, response headers, body, and error messages. A clear WP_Error response is much easier to debug than a silent failure.

Best REST API Use Cases by Developer Type

For plugin developers

Use the REST API for custom settings screens, background reports, plugin dashboards, integrations, and replacing messy Ajax handlers with structured endpoints.

For theme developers

Use the REST API for dynamic blocks, search interfaces, filters, frontend content loading, and editor-side tools.

For headless developers

Use REST endpoints to fetch posts, pages, media, menus, taxonomies, and custom post types. Be strict with caching, pagination, and response size.

For WooCommerce developers

Use WooCommerce’s own REST APIs for store data, orders, products, and customer workflows. Be extra careful with authentication, rate limits, and private order/customer data.

For agency developers

Use the REST API for maintenance scripts, site audits, dashboard reporting, content sync, and client portals. Use Application Passwords per client/integration and revoke credentials when projects end.

REST API Production Checklist

Before shipping a REST API feature, use this checklist.

  • Every custom route has a namespace.
  • Every custom route uses permission_callback.
  • Public endpoints expose only safe public data.
  • Protected endpoints check capabilities.
  • All input is validated and sanitized.
  • Responses are consistent and documented.
  • Errors return useful HTTP status codes.
  • Large responses are paginated.
  • Heavy queries are cached or optimized.
  • External API calls have timeouts.
  • Secrets are never returned to the browser.
  • Application Passwords are stored securely and revocable.
  • Endpoints are tested with authenticated and unauthenticated users.
  • REST behavior is tested with security/cache plugins enabled.
  • Logs are monitored after deployment.

Final Recommendation

The WordPress REST API is one of the best tools for modern WordPress development. Use it when you need structured data access, custom plugin endpoints, headless content delivery, JavaScript-driven interfaces, mobile apps, or external integrations.

The most important rule is security first. Public endpoints should expose only safe public data. Protected endpoints should use proper authentication, capability checks, validation, and sanitization. Application Passwords are useful for scripts and integrations, while cookie and nonce authentication is better for logged-in browser requests inside WordPress.

For performance, keep responses small, paginate collections, cache expensive data, and avoid heavy queries. A clean REST API endpoint should be predictable, permission-aware, easy to debug, and safe to use in production.

Frequently Asked Questions

What is the WordPress REST API used for?

The WordPress REST API is used to read, create, update, and delete WordPress data through JSON endpoints. Developers use it for headless sites, mobile apps, plugin dashboards, integrations, custom endpoints, and automation.

Is the WordPress REST API public?

Public WordPress content is often accessible through the REST API without authentication. Private content, protected data, user data, settings, and write actions require authentication and proper permissions.

How do I access WordPress REST API posts?

You can access public posts with a request such as https://example.com/wp-json/wp/v2/posts. Use parameters like page, per_page, search, categories, and _fields to control the response.

How do I authenticate with the WordPress REST API?

Use cookie and nonce authentication for logged-in browser requests inside WordPress. Use Application Passwords for server-to-server scripts or external tools. Other methods, such as OAuth or JWT, usually require plugins or custom implementation.

Are WordPress Application Passwords safe?

Application Passwords are useful when handled properly. Use HTTPS, create one password per integration, give the user only the permissions required, store credentials securely, and revoke unused passwords.

How do I create a custom WordPress REST API endpoint?

Register a route inside the rest_api_init hook with register_rest_route(). Add a namespace, route path, HTTP method, callback, permission callback, and argument validation.

What is permission_callback in WordPress REST API?

permission_callback controls whether the current request is allowed to access an endpoint. Public endpoints can use __return_true, but protected endpoints should check capabilities with current_user_can().

Can I disable the WordPress REST API?

You can restrict REST API access, but fully disabling it can break the block editor, plugins, themes, mobile apps, and integrations. Restrict sensitive endpoints instead of blocking the entire API blindly.

Is REST API better than admin-ajax.php?

For modern structured data workflows, the REST API is usually better because it uses clean routes, HTTP methods, JSON responses, and permission callbacks. admin-ajax.php is still used for some legacy Ajax workflows.

How do I make the WordPress REST API faster?

Use pagination, limit response fields with _fields, cache expensive results, avoid heavy meta queries, reduce external API calls, and optimize database queries inside custom endpoint callbacks.