
The WordPress REST API lets any application read and write WordPress content over HTTP as JSON, using endpoints like /wp-json/wp/v2/posts that already exist on every WordPress 5.0+ site by default. That single fact is why it powers everything from the block editor itself to fully decoupled React and Next.js frontends: you don’t install anything to get basic read access, you just need to know which endpoint to call and how to authenticate when you want to write data back.
What the REST API Actually Is
Before WordPress 4.7 (December 2016), getting data out of WordPress programmatically meant XML-RPC, custom AJAX handlers, or querying the database directly. The REST API replaced all of that with a standard, resource-based interface: posts, pages, users, comments, media, and custom post types are each exposed as a collection of JSON resources under a predictable URL structure. Every WordPress install since 4.7 ships with it active, mounted at /wp-json/.
This matters for two very different audiences. Theme and plugin developers use it to build dynamic, JavaScript-driven interfaces inside wp-admin (the block editor is a REST API client). Headless developers use it as the entire backend for a decoupled frontend built in React, Next.js, or a mobile app — see our guide to using WordPress as a headless backend for React and Next.js for that specific setup.

Core Endpoints You’ll Use Constantly
Every default post type gets a namespaced endpoint under /wp-json/wp/v2/. These are the ones you’ll reach for on almost every project:
| Endpoint | Purpose |
|---|---|
| GET /wp-json/wp/v2/posts | List published posts, with pagination, search, and taxonomy filtering via query parameters |
| GET /wp-json/wp/v2/posts/{id} | Fetch a single post by ID, including rendered content, title, and excerpt |
| GET /wp-json/wp/v2/pages | List and fetch pages the same way as posts |
| GET /wp-json/wp/v2/media/{id} | Fetch a media item’s URL, dimensions, and available image sizes |
| GET /wp-json/wp/v2/categories & /tags | List taxonomy terms, used to build filters and navigation |
| GET /wp-json/wp/v2/users | List author information (public fields only, unless authenticated) |
Query parameters do most of the real work. ?per_page=20&page=2 handles pagination, ?search=term does a basic keyword search, ?categories=5 filters by taxonomy term ID, and ?_embed=true pulls in related data (featured image, author, terms) in the same request instead of forcing extra round trips — the single most common performance mistake developers make when they first pull data from this API.
Making Your First Request
Read access to public content needs no authentication at all. Any of these will return live JSON from a public WordPress site:
// Plain fetch
const res = await fetch('https://example.com/wp-json/wp/v2/posts?_embed=true&per_page=5');
const posts = await res.json();
// cURL, from the command line
curl "https://example.com/wp-json/wp/v2/posts?per_page=5"JavaScript developers coming from a React or Next.js background will recognize this immediately — it behaves like any other JSON API. Our Fetch vs Axios comparison covers the request-library side of this in more depth if you’re deciding how to structure the calls.
Authentication: What to Use and When
Writing data (creating a post, updating a user, uploading media) or reading private content requires authentication. WordPress supports several methods, but for the vast majority of real projects one option covers it:
- Application Passwords — built into WordPress core since version 5.6, no plugin required. Generate one under a user’s profile in wp-admin, then send it as HTTP Basic Auth. This is the standard choice for server-to-server integrations and most headless setups.
- Cookie authentication with a nonce — used when JavaScript running inside wp-admin (like a custom Gutenberg block or admin page) needs to call the REST API as the logged-in user. WordPress automatically supplies a nonce for this.
- OAuth or JWT plugins — only worth adding if you need token-based auth for a public-facing app with many external users (a mobile app, for example). For most business sites and headless frontends, this is unnecessary complexity on top of what Application Passwords already solves.
// Creating a post with an Application Password
const credentials = btoa('admin:xxxx xxxx xxxx xxxx xxxx xxxx');
await fetch('https://example.com/wp-json/wp/v2/posts', {
method: 'POST',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: 'New post from the API',
content: 'Post body here',
status: 'publish'
})
});
Building a Custom Endpoint
Beyond the built-in resources, you can register your own routes for anything the default API doesn’t cover — a custom search, a dashboard summary, or data from a custom post type with fields the default response doesn’t expose. This is done with register_rest_route() inside a plugin or theme’s functions.php, hooked to rest_api_init:
add_action('rest_api_init', function () {
register_rest_route('myplugin/v1', '/featured/', [
'methods' => 'GET',
'callback' => 'myplugin_get_featured_posts',
'permission_callback' => '__return_true',
]);
});
function myplugin_get_featured_posts($request) {
$posts = get_posts(['meta_key' => 'featured', 'meta_value' => '1']);
return rest_ensure_response($posts);
}The route becomes live at /wp-json/myplugin/v1/featured/ with no extra configuration. permission_callback is not optional in current WordPress versions — omitting it triggers a deprecation warning, and __return_true should only be used for genuinely public data. For anything sensitive, check capabilities inside the callback with current_user_can().
Common Problems and How to Fix Them
- “Could not connect to the WordPress REST API” in the block editor usually means a security plugin, firewall rule, or permalink setting is blocking /wp-json/ requests. Check that pretty permalinks are enabled (Settings, Permalinks, Save) and whitelist /wp-json/* in any security plugin’s firewall rules.
- 401 or 403 responses on write requests almost always mean the Application Password header isn’t being sent correctly, or the user doesn’t have the required capability for that action.
- CORS errors when calling the API from a separate frontend domain require adding the appropriate Access-Control-Allow-Origin headers via the rest_pre_serve_request filter, since WordPress doesn’t send permissive CORS headers by default.
- Slow responses on large collections are usually an unbounded _embed query against thousands of posts. Paginate with per_page and only request _embed when you actually need the related data.
When to Reach for It
If you’re deciding between a traditional theme, a headless build, or a full framework migration, the REST API is the piece that makes headless possible at all — see our WordPress vs Next.js comparison for how that decision plays out in practice. If you’re staying inside WordPress but building a custom theme, you’ll still use these same endpoints for anything dynamic on the frontend; our custom WordPress theme development guide covers where that work starts.
If you’d rather extend WordPress with your own functionality than lean on the REST API alone, see WordPress Plugin Development Basics for JavaScript Developers.