Every WordPress site has a built-in REST API at /wp-json/. It lets apps, scripts, headless front ends and integrations read and write content using JSON over HTTP. The block editor itself uses it. Here’s how to use it, how to authenticate safely, and how to lock down what shouldn’t be public.
Explore the API
curl -s https://example.com/wp-json/ | jq '.namespaces'
curl -s "https://example.com/wp-json/wp/v2/posts?per_page=5&_fields=id,title,link,date" | jq
Core endpoints (/wp-json/wp/v2/…):
| Endpoint | Content |
|---|---|
posts, pages |
Posts and pages |
categories, tags |
Taxonomies |
media |
Uploads |
users |
Users (public: authors with published posts) |
comments |
Comments |
search |
Site search |
types, taxonomies, settings |
Site structure (settings need auth) |
Useful query parameters: per_page (max 100), page, search, categories, orderby, _fields (return only these fields, which is faster), and _embed (include author, featured media and terms).
If /wp-json/ returns 404, enable pretty permalinks, or use /?rest_route=/. See WordPress permalinks 404.
Authentication: application passwords
Reading public content needs no authentication. Creating, editing or reading private data does. Use application passwords (built in since WordPress 5.6):
- Users → Profile → Application Passwords: name it (for example “deploy script”), and click Add.
- Copy the generated password (shown once).
- Use HTTP Basic auth over HTTPS:
curl -s -u 'editor-user:abcd efgh ijkl mnop qrst uvwx' \
-X POST https://example.com/wp-json/wp/v2/posts \
-H 'Content-Type: application/json' \
-d '{"title":"Hello from the API","content":"Posted via REST","status":"draft"}'
Benefits: per-application passwords that you can revoke individually, without changing the user’s real password. Create a dedicated user with the least privileged role that works (for example Author or Editor, not Administrator). See WordPress user roles.
Behind some proxies, the Authorization header gets stripped. On Apache with PHP-FPM, make sure it’s passed through. Nginx with PHP-FPM passes it by default.
Custom endpoints
Register your own routes in a plugin:
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/stock/(?P<id>\d+)', [
'methods' => 'GET',
'callback' => function ( WP_REST_Request $req ) {
return [ 'id' => (int) $req['id'], 'stock' => (int) get_post_meta( $req['id'], '_stock', true ) ];
},
'permission_callback' => '__return_true', // public; use a capability check for private data
] );
} );
Always set a real permission_callback (for example fn() => current_user_can( 'edit_posts' )) for anything that isn’t public.
Security hardening
User enumeration: /wp-json/wp/v2/users lists authors’ usernames (slugs), which helps brute-force attacks. Restrict it to logged-in users:
add_filter( 'rest_endpoints', function ( $endpoints ) {
if ( ! is_user_logged_in() ) {
unset( $endpoints['/wp/v2/users'], $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
}
return $endpoints;
} );
Don’t disable the whole API. The block editor, many plugins and WooCommerce rely on it. Restrict specific endpoints instead.
Rate-limit expensive endpoints (search, custom endpoints) in Nginx:
location ^~ /wp-json/ {
limit_req zone=api burst=20 nodelay;
try_files $uri $uri/ /index.php?$args;
}
See Nginx rate limiting and WordPress limit login attempts.
Performance
- Use
_fieldsto reduce response size and processing. - An object cache (Redis) speeds up repeated API requests. See Redis object cache.
- For public, read-heavy endpoints, cache responses at the edge or with Nginx. Remember to purge on content changes.
- Watch for plugins that add heavy fields to every response.
Common uses
- Headless front ends: Next.js, Astro or Nuxt fetching content. See headless WordPress.
- Automation: publishing from n8n, Zapier-style tools, or scripts. See n8n.
- Mobile apps and integrations with CRMs and shops.
- WP-CLI alternative for remote tasks when you don’t have SSH access.
Quick reference
| Task | How |
|---|---|
| API index | GET /wp-json/ |
| List posts | GET /wp-json/wp/v2/posts?_fields=id,title |
| Authenticate | Application password + HTTPS Basic auth |
| Create post | POST /wp-json/wp/v2/posts |
| Custom route | register_rest_route() with permission_callback |
| Hide users | unset /wp/v2/users for guests |
Related: API security best practices and WP-CLI guide.