Understanding the WordPress REST API: A Beginner’s Guide to wp-json

31 Aug

The WordPress REST API turned WordPress from a traditional content management system into a full-fledged application platform. Since its merge into core with version 4.7, developers have used it to build headless front ends, mobile apps, and complex integrations without ever touching a PHP template file. If you have ever visited a WordPress site and appended /wp-json/wp/v2/posts to the domain, you have already seen the API in action. This guide walks through the essential concepts, shows you how to make your first requests, and highlights the practical details that often trip up newcomers.

What the REST API Actually Is

At its core, the REST API is a set of endpoints that expose WordPress data as JSON. Each endpoint represents a resource—posts, pages, media, taxonomies, users, comments, and more. Because the responses are standard JSON, any client that can speak HTTP can consume them: a React single-page application, a Flutter mobile app, a serverless function, or even a simple curl command in your terminal.

The wp-json slug is the base path. A typical installation responds to https://example.com/wp-json/ with a discovery document that lists every registered route, the supported HTTP methods, and the schema for each resource. That document is machine-readable, which means tools can auto-generate type definitions or client libraries without manual effort.

Key Concepts You Will Use Daily

Routes and Endpoints

A route is the URL pattern, such as /wp/v2/posts. An endpoint is the combination of a route and an HTTP method. The same route can have multiple endpoints: GET /wp/v2/posts returns a collection, while POST /wp/v2/posts creates a new post. Understanding this distinction helps when you read the route documentation or write permission callbacks.

Request Parameters

Most collection endpoints accept a standard set of query parameters. per_page and page handle pagination. search runs a keyword search across titles and content. orderby and order control sorting. Filtering by taxonomy terms uses categories, tags, or the generic taxonomy parameter. These parameters are consistent across core resources, so once you learn them for posts, you can apply them to pages, custom post types, or products.

Response Headers

Two headers deserve attention. X-WP-Total tells you the total number of matching items, and X-WP-TotalPages tells you how many pages of results exist. Client-side pagination logic relies on these values rather than parsing the response body.

Authentication: Choosing the Right Method

Reading public data requires no authentication. Writing data, accessing private drafts, or managing users does. WordPress core ships with cookie authentication, which works automatically when you are logged into the admin area and make requests from the same domain. For external applications, you have three practical options:

  • Application Passwords (introduced in 5.6): Generate a unique password for each application under Users → Profile. Use it with Basic Auth over HTTPS. Simple, revocable, and scoped to a single user.
  • JWT Tokens: Plugins such as JWT Authentication for WP REST API issue signed tokens after a username/password exchange. The token goes in the Authorization: Bearer header. Stateless and widely supported, but you must manage token expiration and refresh logic yourself.
  • OAuth 1.0a: The most robust standard for third-party integrations. The WP OAuth Server plugin implements it. Overkill for a personal headless site, but necessary if you are building a plugin that other site owners will install.

Whichever method you choose, enforce HTTPS. Sending credentials or tokens over plain HTTP defeats the entire purpose.

Making Your First Requests

Open your browser and visit https://your-site.com/wp-json/wp/v2/posts. You will see a JSON array of post objects. Each object contains id, date, slug, title.rendered, content.rendered, excerpt.rendered, author, featured_media, and arrays of categories and tags IDs. The _links object provides HATEOAS-style navigation to the author, collection, and version history.

For a more developer-friendly experience, use a dedicated HTTP client. Postman, Insomnia, or the VS Code REST Client extension let you save requests, set environment variables for the base URL and authentication, and inspect formatted responses. A minimal GET request to fetch the latest five posts looks like this:

GET https://example.com/wp-json/wp/v2/posts?per_page=5&orderby=date&order=desc

To create a post with an application password:

POST https://example.com/wp-json/wp/v2/posts
Authorization: Basic base64(username:app_password)
Content-Type: application/json
{
"title": "Hello from the API",
"content": "This post was created programmatically.",
"status": "publish"
}

The response returns the newly created post object with its assigned ID and links.

Working With Common Resources

Posts and Pages

Posts and pages share the same controller, so their endpoints are nearly identical. The main difference is the type parameter: wp/v2/posts versus wp/v2/pages. Both support revisions at /wp/v2/posts/{id}/revisions and autosaves at /wp/v2/posts/{id}/autosaves.

Custom Post Types

Any post type registered with 'show_in_rest' => true automatically gets its own endpoint under wp/v2/{post_type}. The rest_base argument lets you customize the slug. If you register a book post type with 'rest_base' => 'books', the endpoint becomes /wp/v2/books. The same query parameters work out of the box.

Taxonomies

Categories and tags live at /wp/v2/categories and /wp/v2/tags. Custom taxonomies follow the same pattern. Each term object includes id, name, slug, description, and a count of assigned posts. Hierarchical taxonomies expose a parent field for building tree structures.

Media

The media endpoint (/wp/v2/media) returns attachment objects with source_url, mime_type, media_details (dimensions, file size, EXIF data), and alt_text. Uploading requires a POST with multipart/form-data rather than JSON. Most HTTP clients handle this automatically when you select a file for the file field.

Users

/wp/v2/users lists authors. By default, only users who have published posts are included. Add ?who=authors or ?search= to broaden the query. The response omits sensitive fields like user_pass and user_activation_key. Creating or updating users requires the create_users or edit_users capability.

Extending the API Without Hacking Core

Two hooks cover most extension needs. register_rest_field adds a custom field to an existing resource. For example, you can attach a reading-time estimate to every post response:

add_action('rest_api_init', function () {
register_rest_field('post', 'reading_time', [
'get_callback' => function ($post) {
$word_count = str_word_count(strip_tags($post['content']['rendered']));
return ceil($word_count / 200);
},
'schema' => [
'description' => 'Estimated reading time in minutes.',
'type' => 'integer',
],
]);
});

register_rest_route creates entirely new endpoints. Use it when you need aggregated data, complex queries, or actions that don’t map to a single resource. Always namespace your routes (e.g., myplugin/v1) to avoid collisions.

When you modify responses, remember that the API serves many consumers. Adding a heavy computed field to every post collection request can slow down the mobile app that only needs titles. Consider a dedicated endpoint for expensive data instead of bloating the default response.

Performance and Caching Considerations

The REST API is not magically fast. Each request boots WordPress, runs the query, serializes the response, and returns JSON. On a busy site, uncached API calls can become a bottleneck.

  • Object caching (Redis or Memcached) speeds up the database queries inside the API controllers.
  • HTTP caching via the Cache-Control header lets browsers and CDNs serve repeated GET requests without hitting PHP. The WP REST API Cache plugin adds sensible defaults.
  • Conditional requests: Core sends ETag and Last-Modified headers. Clients can send If-None-Match or If-Modified-Since to receive a 304 Not Modified response with zero body payload.

If you run a headless front end, consider a static-site generation workflow (Next.js, Astro, Gatsby) that fetches data at build time rather than on every page view.

Security Practices Worth Adopting

The API expands the attack surface. A few pragmatic steps reduce risk:

  • Disable the API entirely for unauthenticated users if you don’t need public data: add_filter('rest_authentication_errors', function () { return new WP_Error('rest_forbidden', 'Restricted', 401); });
  • Restrict write endpoints to specific roles using the permission_callback argument when registering routes.
  • Rate-limit authentication endpoints to prevent credential stuffing. Plugins like Limit Login Attempts Reloaded cover the REST API as well.
  • Validate and sanitize input in your custom endpoints exactly as you would in a standard admin handler. The sanitize_callback and validate_callback arguments in register_rest_field and register_rest_route exist for this reason.

Troubleshooting the Most Common Issues

404 on Every Endpoint

Pretty permalinks must be enabled. Go to Settings → Permalinks and choose anything other than “Plain”. Save the settings to flush rewrite rules. On nginx, ensure the location block includes try_files $uri $uri/ /index.php?$args;.

CORS Errors in the Browser Console

Browsers block cross-origin requests unless the server sends Access-Control-Allow-Origin. The WP REST API CORS plugin or a few lines in functions.php can whitelist your front-end domain. Avoid using * in production.

Authentication Works in Postman But Not in the Front End

Cookie authentication only works for same-origin requests. If your front end lives on a subdomain, set the cookie domain to the root domain and ensure withCredentials: true is set on the fetch/XHR request. For different domains, switch to token-based authentication.

Large Responses Time Out

Fetching thousands of posts in one request exceeds PHP memory or execution limits. Use pagination (per_page=100 is the maximum) and loop through pages. For exports, consider a background job that writes a static JSON file to S3.

Where to Go From Here

The official REST API Handbook remains the most authoritative reference. It documents every core endpoint, the schema system, and the global parameters. For deeper dives, the WP_REST_Controller class source code in wp-includes/rest-api/endpoints shows how core controllers handle validation, preparation, and linking.

If you are building a headless site, evaluate a framework that understands WordPress data structures out of the box. Tools like Faust.js (Next.js), Astro WordPress Integration, or Frontity handle routing, preview mode, and incremental static regeneration so you don’t reinvent the wheel.

Finally, treat the API as a contract. Version your custom namespaces, document breaking changes, and run integration tests against a staging environment before deploying. The effort pays off when a mobile app update doesn’t require a simultaneous WordPress deploy.

Frequently Asked Questions (FAQs)

What is the difference between wp-json and the REST API?

wp-json is the default base URL path where the REST API lives. The REST API is the entire system of endpoints, controllers, and infrastructure; wp-json is simply the entry point.

Can I change the wp-json slug to something else?

Yes. The rest_url_prefix filter lets you rename it. For example, add_filter('rest_url_prefix', fn () => 'api'); moves the base to /api/. Remember to flush permalinks after changing it.

Do I need a plugin to use the REST API?

No. The REST API has been part of WordPress core since version 4.7. Plugins are only required for extra authentication methods, custom endpoints, or specialized caching.

How do I fetch only specific fields to reduce payload size?

Use the _fields parameter. For example, /wp/v2/posts?_fields=id,title.rendered,link returns only those three properties for each post.

Why does my custom post type not appear in the API?

Check the registration arguments. The post type must have 'show_in_rest' => true. If you want a custom base slug, also set 'rest_base'. After changing the registration, flush permalinks.

Is the REST API slower than admin-ajax?

Not inherently. Both bootstrap WordPress. The REST API includes more validation and schema generation, which adds a small overhead. Proper caching usually makes the difference negligible.

Can I use the REST API on a multisite network?

Yes. Each site has its own API base at https://site.network.com/wp-json/. The main site’s API does not automatically expose subsite content.

How do I handle file uploads via the API?

Send a POST request to /wp/v2/media with multipart/form-data encoding. Include the file in a field named file. The response contains the attachment ID, which you can then assign as featured_media on a post.

Leave a Reply

Your email address will not be published. Required fields are marked *