tutorialWordPressREST APIAPI

WordPress REST API: A Complete Guide for Developers

WordPress REST API: A Complete Guide for Developers

The WordPress REST API lets you interact with WordPress programmatically. It's the foundation for headless WordPress and enables building modern frontends while keeping WordPress as a content backend.


What Is the REST API?

The REST API exposes WordPress data via HTTP endpoints. Instead of rendering HTML, you get JSON data.

Example:

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

Returns:

[

{

"id": 1,

"title": {

"rendered": "Hello World"

},

"content": {

"rendered": "

Welcome to WordPress...

"

},

"excerpt": {

"rendered": "

A short excerpt...

"

},

"date": "2026-02-05T10:00:00",

"author": 1,

"categories": [1, 3],

"tags": [2, 5],

"_links": { ... }

}

]


Core Endpoints

Discovery

Start here to see all available routes:

GET /wp-json/

Posts

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

GET /wp-json/wp/v2/posts/{id} # Single post

POST /wp-json/wp/v2/posts # Create post

PUT /wp-json/wp/v2/posts/{id} # Update post

DELETE /wp-json/wp/v2/posts/{id} # Delete post

Pages

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

GET /wp-json/wp/v2/pages/{id} # Single page

POST /wp-json/wp/v2/pages # Create page

PUT /wp-json/wp/v2/pages/{id} # Update page

DELETE /wp-json/wp/v2/pages/{id} # Delete page

Categories & Tags

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

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

Users

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

GET /wp-json/wp/v2/users/{id} # Single user

GET /wp-json/wp/v2/users/me # Current user (auth required)

Media

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

GET /wp-json/wp/v2/media/{id} # Single media item

POST /wp-json/wp/v2/media # Upload media

Comments

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

POST /wp-json/wp/v2/comments # Create comment


Query Parameters

Filtering Posts

Get 5 posts

/wp-json/wp/v2/posts?per_page=5

Page 2 of results

/wp-json/wp/v2/posts?page=2

Posts in category 3

/wp-json/wp/v2/posts?categories=3

Posts by author 1

/wp-json/wp/v2/posts?author=1

Search posts

/wp-json/wp/v2/posts?search=keyword

Order by date descending

/wp-json/wp/v2/posts?orderby=date&order=desc

Only published posts (default)

/wp-json/wp/v2/posts?status=publish

Include specific posts

/wp-json/wp/v2/posts?include=1,2,3

Exclude specific posts

/wp-json/wp/v2/posts?exclude=4,5,6

Customizing Response

Embed related data (author, featured media, terms)

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

Only specific fields

/wp-json/wp/v2/posts?_fields=id,title,excerpt

Posts with specific slug

/wp-json/wp/v2/posts?slug=post-name


Authentication

No Auth (Public Data)

Reading public posts, pages, categories works without authentication:

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

Application Passwords (WordPress 5.6+)

1. Go to Users → Your Profile

2. Scroll to "Application Passwords"

3. Generate a password

curl -u "username:xxxx xxxx xxxx xxxx" \

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

-X POST \

-H "Content-Type: application/json" \

-d '{"title":"My New Post","content":"Content here","status":"draft"}'

JWT Authentication (Plugin Required)

Install a JWT plugin like JWT Authentication for WP REST API:

Get token

curl -X POST https://example.com/wp-json/jwt-auth/v1/token \

-H "Content-Type: application/json" \

-d '{"username":"user","password":"pass"}'

Use token

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

-H "Authorization: Bearer YOUR_TOKEN"

OAuth (Plugin Required)

For third-party applications, use OAuth 1.0a or 2.0 plugins.


Working with Data

Creating a Post

// JavaScript/Node.js

const response = await fetch('https://example.com/wp-json/wp/v2/posts', {

method: 'POST',

headers: {

'Content-Type': 'application/json',

'Authorization': 'Basic ' + btoa('username:app_password'),

},

body: JSON.stringify({

title: 'My New Post',

content: '

Post content here

',

status: 'publish',

categories: [1, 3],

tags: [5, 7],

}),

});

const post = await response.json();

console.log('Created post:', post.id);

Updating a Post

const response = await fetch('https://example.com/wp-json/wp/v2/posts/123', {

method: 'PUT',

headers: {

'Content-Type': 'application/json',

'Authorization': 'Basic ' + btoa('username:app_password'),

},

body: JSON.stringify({

title: 'Updated Title',

content: 'New content here',

}),

});

Uploading Media

const formData = new FormData();

formData.append('file', imageFile);

formData.append('title', 'My Image');

formData.append('alt_text', 'Description of image');

const response = await fetch('https://example.com/wp-json/wp/v2/media', {

method: 'POST',

headers: {

'Authorization': 'Basic ' + btoa('username:app_password'),

},

body: formData,

});


Custom Endpoints

Registering Custom Routes

// In your plugin or theme functions.php

add_action('rest_api_init', function() {

register_rest_route('myplugin/v1', '/featured-posts', [

'methods' => 'GET',

'callback' => 'get_featured_posts',

'permission_callback' => '__return_true',

]);

});

function get_featured_posts($request) {

$posts = get_posts([

'meta_key' => 'featured',

'meta_value' => '1',

'posts_per_page' => 10,

]);

$data = [];

foreach ($posts as $post) {

$data[] = [

'id' => $post->ID,

'title' => $post->post_title,

'excerpt' => get_the_excerpt($post),

'link' => get_permalink($post),

];

}

return new WP_REST_Response($data, 200);

}

Access at: /wp-json/myplugin/v1/featured-posts

With Parameters

register_rest_route('myplugin/v1', '/posts-by-author/(?P\d+)', [

'methods' => 'GET',

'callback' => 'get_posts_by_author',

'permission_callback' => '__return_true',

'args' => [

'author_id' => [

'required' => true,

'validate_callback' => function($param) {

return is_numeric($param);

},

],

],

]);

function get_posts_by_author($request) {

$author_id = $request->get_param('author_id');

// Fetch and return posts

}

Protected Routes

register_rest_route('myplugin/v1', '/private-data', [

'methods' => 'GET',

'callback' => 'get_private_data',

'permission_callback' => function() {

return current_user_can('edit_posts');

},

]);


Extending Default Endpoints

Adding Custom Fields to Response

add_action('rest_api_init', function() {

register_rest_field('post', 'reading_time', [

'get_callback' => function($post) {

$content = get_post_field('post_content', $post['id']);

$word_count = str_word_count(strip_tags($content));

return ceil($word_count / 200) . ' min read';

},

'schema' => [

'description' => 'Estimated reading time',

'type' => 'string',

],

]);

});

Now every post response includes reading_time.

Including ACF Fields

add_action('rest_api_init', function() {

register_rest_field('post', 'acf_fields', [

'get_callback' => function($post) {

return get_fields($post['id']);

},

]);

});


Building Headless WordPress

React/Next.js Example

// lib/api.js

const API_URL = process.env.WORDPRESS_API_URL;

export async function getPosts() {

const res = await fetch(${API_URL}/wp-json/wp/v2/posts?_embed);

const posts = await res.json();

return posts.map(post => ({

id: post.id,

title: post.title.rendered,

excerpt: post.excerpt.rendered,

content: post.content.rendered,

date: post.date,

slug: post.slug,

featuredImage: post._embedded?.['wp:featuredmedia']?.[0]?.source_url,

author: post._embedded?.['author']?.[0]?.name,

categories: post._embedded?.['wp:term']?.[0],

}));

}

export async function getPostBySlug(slug) {

const res = await fetch(

${API_URL}/wp-json/wp/v2/posts?slug=${slug}&_embed

);

const posts = await res.json();

return posts[0];

}

Next.js Page

// app/blog/[slug]/page.tsx

import { getPostBySlug, getPosts } from '@/lib/api';

export async function generateStaticParams() {

const posts = await getPosts();

return posts.map((post) => ({

slug: post.slug,

}));

}

export default async function Post({ params }) {

const post = await getPostBySlug(params.slug);

return (

);

}


Common Issues

CORS Errors

Add to functions.php or a plugin:

add_action('rest_api_init', function() {

remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');

add_filter('rest_pre_serve_request', function($value) {

header('Access-Control-Allow-Origin: *');

header('Access-Control-Allow-Methods: GET, POST, OPTIONS');

header('Access-Control-Allow-Headers: Content-Type, Authorization');

return $value;

});

}, 15);

Authentication Issues

  • Ensure SSL (HTTPS) for production
  • Check Application Password format (spaces matter)
  • Verify user has required capabilities

Missing Data

  • Use _embed to include related data
  • Check if custom post types expose to REST
  • Register REST fields for custom data

Performance Considerations

API Response Time

WordPress REST API can be slow:

  • Database queries per request
  • Plugin overhead
  • No built-in caching

Solutions

Server-side caching:

add_action('rest_api_init', function() {

// Cache API responses

header('Cache-Control: public, max-age=300');

});

CDN/Edge caching:

  • Cloudflare API caching
  • Fastly
  • AWS CloudFront

Static Site Generation:

Fetch at build time, not runtime:

// Next.js ISR

export async function generateStaticParams() {

const posts = await getPosts();

return posts.map((post) => ({ slug: post.slug }));

}

export const revalidate = 60; // Rebuild every 60 seconds


FAQ

Q: Is the REST API secure?

Yes, for read operations on public content. Write operations require authentication. Always use HTTPS. See our WordPress security guide →

Q: Can I disable the REST API?

Partially. You can restrict certain endpoints, but core functionality depends on it.

Q: How do I get custom post types via API?

Ensure show_in_rest is true when registering the post type:

register_post_type('book', [

'show_in_rest' => true,

'rest_base' => 'books',

// ... other args

]);

Q: Why are some fields missing?

Not all meta fields are exposed by default. Use register_rest_field() to add them.

Q: What's the best approach for performance?

Static site generation fetches data at build time. See our headless WordPress tutorial →


Conclusion

The WordPress REST API enables:

  • Headless WordPress architectures
  • Mobile app backends
  • Third-party integrations
  • Custom admin interfaces

For the best performance, consider building static sites that fetch from the API at build time rather than on each request.

Related guides:

Learn more about headless WordPress →

Share:

Related Articles

View all

Ready to Migrate Your WordPress Site?

Use our free tool to export your WordPress content in minutes.

Start Free Migration