WordPress

The Public API You Didn’t Know You Shipped: A WordPress REST API Guide

11 min read

Open a new browser tab and type your own domain followed by /wp-json/. Unless someone has deliberately locked it down, you’re looking at a wall of JSON: a machine-readable map of your posts, pages, users, media, taxonomies, and settings. That’s the WordPress REST API, and it’s been switched on by default on virtually every WordPress site since late 2016.

Most site owners have never opened that URL and have no idea it exists. It’s the quiet engine behind the block editor, the mobile app, and a growing pile of automations and headless front-ends. It’s also, by default, a public read-only window into parts of your site you probably assumed were private.

This guide covers what the REST API actually is, how to use it to automate real work, and the two or three settings worth tightening before someone else finds it first.


What the WordPress REST API Actually Is

The REST API is a set of URLs that hand back your site’s data as JSON instead of as a rendered web page. Ask /wp-json/wp/v2/posts and you get an array of your posts as structured data — title, content, author ID, date, categories — that any program can read. Ask the same endpoint with an HTTP POST and the right credentials, and you can create a post without ever loading wp-admin.

It landed in WordPress core in stages. The underlying infrastructure arrived in WordPress 4.4, and the content endpoints — posts, comments, terms, users, meta, settings, and more — merged into core with WordPress 4.7, released December 6, 2016. Since then, every standard install ships it turned on. The block editor you use every day talks to your site almost entirely through this API; that’s how it saves a draft without a full page reload.

The base route and the shape of a request

Everything hangs off one base route: /wp-json/. Under it sits the core namespace, wp/v2, and under that the individual endpoints. A few you’ll reach for constantly:

  • /wp-json/wp/v2/posts — posts, filterable by category, tag, author, date, and search term
  • /wp-json/wp/v2/pages — pages
  • /wp-json/wp/v2/media — the media library
  • /wp-json/wp/v2/categories and /tags — your taxonomies
  • /wp-json/wp/v2/users — author profiles (the one to watch; more on that below)
  • /wp-json/wp/v2/settings — site settings, but only for authenticated administrators

If your permalinks are set to “Plain,” the pretty /wp-json/ route won’t resolve. WordPress still exposes the same data through a query-string form: ?rest_route=/wp/v2/posts. That fallback is handy when you’re debugging a site whose rewrite rules are broken.

How you’d actually call it

Reading is as simple as a GET request. From a terminal:

curl https://example.com/wp-json/wp/v2/posts?per_page=5

That returns your five most recent posts as JSON. Two response headers do the heavy lifting for pagination: X-WP-Total tells you how many items exist in total, and X-WP-TotalPages tells you how many pages of results there are at your current per_page size. The API caps per_page at 100, so pulling a large archive means walking through pages rather than asking for everything at once.

WHY IT MATTERS

The REST API is what turns WordPress from a website into a content platform. It’s the reason you can run a React or Next.js front-end on your marketing site while editors keep working in the familiar WordPress dashboard, push new posts from a script the moment a product ships, or feed a chatbot your knowledge base. But the same door that lets your tools in is open to the public by default — so it’s worth understanding both halves.

What You Can Build With It

The reason to care about any of this is that the REST API removes the human from repetitive work. Anything you’d normally click through in the dashboard, a program can do on a schedule or in response to an event.

Headless and hybrid front-ends

In a headless setup, WordPress becomes a pure content store. Your editors write posts as usual, and a separate front-end — built in Next.js, Astro, or anything that speaks HTTP — fetches that content through the REST API and renders it however you like. You keep the editing experience people already know while getting a fast, modern, fully custom presentation layer. Plenty of teams run a hybrid version instead: a mostly-normal WordPress theme that pulls one or two dynamic sections (a live inventory count, a personalized block) from the API on the client side.

Automations and integrations

This is where a small business gets the most leverage. A few patterns we see constantly: a script that publishes a blog post the instant a new item lands in a spreadsheet, a nightly job that exports every post to a backup service, a webhook that posts a summary to Slack whenever an editor hits publish, or a CRM that reads your latest case studies to keep a sales microsite current. None of that requires a plugin — just an authenticated call to the right endpoint.

The last integration we wired up for a client was a two-line change in a no-code automation tool: when a form submission came in, create a draft post pre-filled with the submitter’s details so an editor could review and publish with one click. It replaced a copy-paste ritual that had been eating twenty minutes a day.

Feeding AI tools

The API’s structured output is a natural fit for the current wave of AI work. Because every post comes back as clean JSON with the fields already separated, it’s straightforward to pull your whole content library into a script that summarizes it, generates internal-link suggestions, or builds a retrieval index for a support chatbot. You’re not scraping rendered HTML and guessing where the article ends and the footer begins — the API already drew that line for you.

Authentication: Reading Is Open, Writing Is Not

Here’s the split that trips people up. Anonymous GET requests can read anything WordPress considers public — published posts, pages, and media. The moment you want to write data, read a draft, or touch anything private, you need to authenticate, and WordPress checks that the account you’re authenticating as actually has permission for that action.

Cookie authentication, for code running inside WordPress

When the block editor talks to the API, it’s already logged in through your browser session. WordPress protects those requests with a nonce — a short-lived token that proves the request came from your own admin screen and not a malicious site in another tab. If you’re writing a plugin or theme that calls the API from the front-end for a logged-in user, this is the mechanism you’ll use. It doesn’t work for anything outside the site itself.

Application Passwords, for everything external

For a script, a mobile app, or a third-party service, WordPress ships Application Passwords, added in WordPress 5.6 in December 2020. You generate one under Users → Profile, and it’s a dedicated credential — separate from your login password, revocable on its own, and scoped so you can hand one to each integration and pull it back without changing your real password. Requests send it using HTTP Basic Authentication, so the whole thing rides on your site running over HTTPS. By default WordPress won’t even offer Application Passwords on a connection that isn’t secure.

For larger or multi-user integrations, OAuth 2.0 flows are available through dedicated plugins, but for the vast majority of “let this one script post to my site” jobs, an Application Password is the right tool.

KEY POINT

Give each integration its own Application Password, never your real login. If a script is compromised or a service you no longer use gets breached, you revoke that one credential and everything else keeps working. Treat them like the API keys they are: one per job, HTTPS only, and rotated when someone leaves the team.

The Part Nobody Warns You About: It’s Public by Default

Now for the half of this that’s a security topic rather than an automation one. Because the read side is open to anonymous visitors, a few endpoints hand out information you might have assumed was private, and one in particular deserves your attention.

The users endpoint and username enumeration

Visit /wp-json/wp/v2/users on most sites and you’ll get back a list of the accounts that have published posts, including each one’s display name and — critically — its slug, which is very often the exact login username. For an attacker, that’s half of a brute-force attempt handed over for free: they no longer have to guess your usernames, only your passwords. WordPress narrowed this endpoint years ago so it only exposes users who’ve authored public content, but on a typical blog that’s still your author accounts.

The last malware cleanup we did started exactly here. Before anything was compromised, the attacker had pulled the author list from the REST API, matched the admin username to a password from a public breach dump, and walked straight in through wp-login.php. No exploit, no clever trick — just a username the site was giving away and a password the owner had reused somewhere else years earlier.

What to actually do about it

You don’t need to disable the REST API to close the obvious gaps, and disabling it wholesale will break your block editor and any plugin that depends on it. A more surgical approach works better:

  • Restrict the users endpoint for anonymous requests so it stops broadcasting usernames. Most reputable security plugins (Wordfence, iThemes, and others) offer this as a one-click toggle.
  • Require authentication for the entire API only if you genuinely run no public integrations — the rest_authentication_errors filter lets a developer force every request to be logged in. This is a real hammer; use it knowingly, because it changes how the editor and plugins behave.
  • Enforce strong, unique passwords and rate-limit login attempts, since username enumeration only matters if the password is guessable. This is the same hardening you’d do anyway.

The goal isn’t to bolt the API shut. It’s to stop the parts that leak identity information while keeping the parts that make WordPress genuinely programmable.

Extending the API: Your Own Endpoints

The core endpoints cover the built-in content types, but the API is designed to be extended. If you’ve built a custom post type or you want a purpose-built endpoint that returns exactly the data your front-end needs, you register it yourself.

register_rest_route in practice

A developer adds a custom route by hooking register_rest_route() onto the rest_api_init action. You give it a namespace (something like myplugin/v1 so it doesn’t collide with core’s wp/v2), a route pattern, the HTTP methods it answers to, a callback that returns the data, and — this is the part people skip — a permission_callback that decides who’s allowed to call it. Since WordPress 5.5, leaving that permission check out throws a warning, because an endpoint with no gate is an endpoint anyone can hit.

Custom post types get REST support too, but not automatically. When you register the post type, you pass 'show_in_rest' => true, and optionally a custom rest_base for a cleaner URL. Miss that flag and your custom content simply won’t appear in the API — and, as a side effect, it won’t be editable in the block editor either, since the editor reaches your content through this same API.

OPEN SOURCE

Our own WordPress tools lean on the REST API for exactly this kind of programmatic work — bulk operations, background jobs, and admin interfaces that talk to your site the same way the block editor does. The full amplifi.plugins suite is MIT licensed and free, and the source is on GitHub if you want to see how custom endpoints and permission callbacks come together in real code.

View on GitHub

Practical Tips Before You Ship an Integration

A handful of things will save you a debugging session:

  • Discover, don’t hardcode. WordPress advertises its API location in an HTTP Link header and a <link> tag in the page head pointing at /wp-json/. Well-behaved clients read that rather than assuming the path, which matters on sites in a subdirectory.
  • Every response includes a _links section that points to related resources — an author, a featured image, the comments on a post. Following those links keeps your code from stitching together URLs by hand.
  • Use the _fields parameter to ask only for the data you need. ?_fields=id,title,link trims a fat response down to three fields, which adds up fast across thousands of records.
  • Test writes against a staging site first. A misfired POST loop can create a few hundred draft posts faster than you’d think, and cleaning those up is its own afternoon.

Start read-only. Get comfortable pulling data out and seeing the shape of it before you ever send a request that changes something. The API is forgiving to read from and unforgiving to write to carelessly.


Frequently Asked Questions

The REST API exposes your site’s content and data as JSON over standard HTTP, so external programs can read and write it. It powers the block editor and the WordPress mobile app, and it lets developers build headless front-ends, automations, integrations with other services, and AI tools that consume your content as structured data instead of scraped HTML.

It lives at your domain followed by /wp-json/ — for example, https://example.com/wp-json/. The core content endpoints sit under /wp-json/wp/v2/, such as /wp-json/wp/v2/posts. If your permalinks are set to Plain, use the query-string form instead: ?rest_route=/wp/v2/posts.

The API itself is safe by design — writing data always requires authentication and a permission check. The common concern is that the anonymous users endpoint can reveal author usernames, which helps attackers target brute-force attempts. The fix is to restrict that endpoint (most security plugins offer a toggle) and enforce strong, unique passwords, rather than disabling the whole API and breaking the editor.

Use Application Passwords, a feature built into WordPress since version 5.6. Generate one under Users then Profile, and send it with your requests using HTTP Basic Authentication over HTTPS. Each Application Password is a separate, revocable credential, so you can issue one per integration and revoke it without changing your real login password. For larger multi-user setups, OAuth 2.0 is available via plugins.

Yes. A developer registers a route with register_rest_route() hooked to the rest_api_init action, supplying a namespace, the route pattern, the HTTP methods, a callback that returns the data, and a permission_callback that controls access. To expose a custom post type through the API, set show_in_rest to true when you register it — that flag also makes the post type editable in the block editor.

Usually not. The block editor, the mobile app, and many plugins depend on it, so turning it off entirely tends to break things you use. A better approach is to require authentication for all requests only if you run no public integrations, or more commonly to restrict just the sensitive endpoints like users while leaving the rest available. Reach for a targeted control rather than a full shutdown.

The REST API is what makes WordPress genuinely programmable — the difference between a site you edit by hand and a platform your tools can build on. If you want a second set of eyes on an integration or a lockdown, that’s the kind of work we do.

Built by amplifi.studio — see also our WordPress security hardening checklist.