The REST API

Since version 4.7, WordPress has shipped with a built-in REST API that exposes posts, pages, users, comments, and more as JSON, over plain HTTP endpoints. This turns WordPress from "a system that only renders its own PHP templates" into a data source that any application — written in any language — can read from and write to.

1. Built-In Endpoints

Every WordPress site automatically exposes endpoints under /wp-json/wp/v2/. Visiting one directly in a browser shows raw JSON — for example, requesting all published posts:

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

conceptually returns an array of post objects shaped like this:

[
  {
    "id": 42,
    "date": "2026-08-01T10:00:00",
    "title": { "rendered": "Hello World" },
    "content": { "rendered": "<p>Post body HTML</p>" },
    "link": "https://example.com/hello-world"
  }
]

Similar endpoints exist for pages (/wp-json/wp/v2/pages), users, categories, tags, and media — the entire admin's core data model is reachable as JSON with no extra setup.

2. Why This Matters: Headless WordPress

Because content is available as plain JSON, WordPress can be used purely as a content backend — editors keep using the familiar WordPress admin to write posts, while the actual public-facing site is a completely separate frontend built in React, Vue, or any other framework, fetching content over the REST API. This pattern is called headless WordPress. The REST API is also what powers the block editor (Gutenberg) itself — every block save and load goes through these same endpoints.

3. Registering a Custom REST Endpoint

Beyond the built-in endpoints, plugins can expose their own custom routes with register_rest_route(), hooked to the rest_api_init action. This is useful whenever a plugin manages data that doesn't fit the built-in post/page model.

<?php
function myplugin_register_routes() {
    register_rest_route('myplugin/v1', '/books', [
        'methods'  => 'GET',
        'callback' => 'myplugin_get_books',
        'permission_callback' => '__return_true',
    ]);
}
add_action('rest_api_init', 'myplugin_register_routes');

function myplugin_get_books($request) {
    $books = [
        ['id' => 1, 'title' => 'PHP Basics'],
        ['id' => 2, 'title' => 'WordPress Development'],
    ];
    return new WP_REST_Response($books, 200);
}

// Now reachable at: /wp-json/myplugin/v1/books

myplugin/v1 is the route's namespace — always version it (v1, v2...) so future changes don't break existing consumers. The permission_callback is required as of WordPress 5.5+; returning __return_true makes the endpoint public, while a real capability check would restrict who can call it.

Cross-reference: A custom post type registered with show_in_rest => true, as shown in Custom Post Types & Taxonomies, is automatically exposed at its own /wp-json/wp/v2/ endpoint too — no extra route registration needed.

4. Authentication Basics

Read access to public content needs no authentication at all — that's why the examples above work with a plain GET request. Writing data, or reading private content, does need authentication, and WordPress offers a few layers:

  • Cookie authentication — works automatically for logged-in users inside the WordPress admin (e.g. the block editor calling the REST API behind the scenes)
  • Application Passwords — a built-in feature (since WP 5.6) letting a user generate a dedicated password just for API access, ideal for external scripts and apps
  • Third-party auth plugins — such as JWT authentication plugins, useful for headless setups needing token-based auth
Note: This is intentionally a conceptual overview, not a deep dive — the right authentication method depends heavily on who's consuming the API and from where.

5. Calling the REST API from JavaScript

Consuming these endpoints from the frontend is just an ordinary HTTP request. Using the browser's built-in fetch():

fetch('https://example.com/wp-json/wp/v2/posts')
  .then(response => response.json())
  .then(posts => {
    posts.forEach(post => {
      console.log(post.title.rendered);
    });
  });

This single pattern — fetch JSON, render it however you like — is the entire foundation a headless frontend is built on.

Next up: Exposing data publicly raises an obvious question: how do you keep it safe? Security Best Practices covers nonces, sanitizing, escaping, and capability checks.