The WordPress REST API: What It's Good For
Every WordPress site you've touched since late 2016 has been running an API. Not an optional plugin, not a developer feature you have to switch on — a full JSON API, live at /wp-json/wp/v2/, answering questions about your posts, pages, users, and media right now. Most site owners have never looked at it. Some discover it only when a security scanner flags it, panic briefly, and move on.
That's a shame, because the REST API is one of the more useful things WordPress ever shipped — and also one of the more oversold. It powers the block editor, it makes certain integrations trivially easy, and it has been pitched (mostly by people selling JavaScript frameworks) as the gateway to a glorious headless future that most sites will never need. This post is the sorting exercise: what the API genuinely does well, what it does badly, and a few decision rules for figuring out which side of the line your project falls on.
First, what it actually is
The REST API is a set of URL routes that return JSON instead of HTML. Visit yoursite.com/wp-json/wp/v2/posts in a browser and you'll get your ten most recent published posts as structured data: title, content, date, author ID, featured media ID, and so on. The core routes cover the things WordPress itself manages:
/wp/v2/posts,/wp/v2/pages— content, with filtering by category, tag, author, search term, and date/wp/v2/media— the media library, including upload via POST/wp/v2/users— user records (publicly, only users who have published content)/wp/v2/categories,/wp/v2/tags— taxonomies/wp/v2/comments,/wp/v2/settings,/wp/v2/types— the rest of the furniture
Reads of published content are public by default. Writes — creating a post, uploading an image, editing anything — require authentication, and permission checks mirror what the logged-in user could do in wp-admin. An author-role account can't publish someone else's draft through the API any more than it could through the dashboard. The API is a different door into the same house, with the same locks.
Two details worth knowing before we get to use cases. First, WordPress itself is the API's biggest consumer: the block editor is a JavaScript application that saves your posts by POSTing to these routes. If the REST API breaks, your editor breaks — which is why 'disable the REST API' advice from older security posts is now actively harmful. Second, plugins register their own routes under their own namespaces (/wp-json/contact-form-7/v1/, /wp-json/wc/v3/ for WooCommerce), so a mature site's API surface is much larger than core's.
What it's genuinely good for
Getting content out of WordPress and into something else
This is the bread-and-butter case, and the API is excellent at it. A company mobile app that shows the latest three blog posts. A digital signage screen in a lobby pulling announcements. A sister site that syndicates your articles. A weekly newsletter tool that fetches everything published since last Monday. All of these are a single authenticated-or-not GET request with query parameters:
GET /wp-json/wp/v2/posts?categories=12&per_page=3&_fields=title,link,excerpt
That _fields parameter deserves more fame than it has. By default the API returns the full rendered content of every post, which can make responses enormous. Asking for only the fields you need cuts response size dramatically and spares the server the work of rendering content nobody will read.
Pushing content into WordPress from your other tools
The reverse direction is just as practical. If your team drafts in Google Docs, Notion, or a homegrown tool, a small script can create WordPress drafts automatically: POST to /wp/v2/posts with a title, content, and status=draft, and it appears in the dashboard ready for an editor. Publishing pipelines, migration scripts, 'post this alert to the site when the monitoring system fires' automations — all one HTTP call.
Authentication for this kind of server-to-server work got dramatically easier when application passwords landed in core (WordPress 5.6). Under Users → Profile, you generate a per-application password — a 24-character string that works with plain HTTP Basic auth over HTTPS, can be revoked individually, and never exposes the user's real password. Before that, you needed a plugin for OAuth or JWT; now the honest advice for most integrations is: create a dedicated user with the minimum role that can do the job (usually Author or Editor, almost never Administrator), generate an application password for it, and revoke it the day the integration retires.
Small interactive features on an otherwise normal site
You don't need to go headless to use the API from JavaScript. A 'load more posts' button that fetches the next page without a reload, a live search box that queries /wp/v2/search as the user types, a filterable project grid — these are a few dozen lines of fetch() against routes that already exist. This is the API's sweet spot for typical business sites: sprinkle interactivity onto server-rendered pages, rather than rebuilding the whole front end.
Exposing your own data with custom routes
If you build anything custom in WordPress — a plugin, a client feature — register_rest_route() gives you a proper endpoint with argument validation, permission callbacks, and JSON handling, for maybe fifteen lines of PHP. The older alternative, hanging handlers off admin-ajax.php, gives you none of that structure and boots more of WordPress on every call. For new code there is no contest: custom REST routes win.
What it's bad at
Performance-sensitive, high-traffic reads
Here's the uncomfortable part. A standard REST API request boots most of WordPress — core, every active plugin, the theme's functions file — before it returns a byte of JSON. On decent hosting that's often 200–600 milliseconds per uncached request, and unlike regular pages, API responses are frequently not covered by your page cache. Many cache layers pass /wp-json/ requests straight through to PHP by design, because API responses can be personalized. If you build a popular front end that hits an uncached WordPress API on every visit, you have effectively turned your page cache off for your real traffic.
The fixes exist — cache API responses at the CDN or reverse proxy, use a persistent object cache, put long Cache-Control headers on public routes — but none of them are the default, and all of them are your job. Decision rule: if a route will be hit more than a few times per second, decide where its cache lives before you ship it, not after the server falls over.
Anything resembling complex queries
The API exposes the filters WordPress core thought of: category, tag, author, search, date, a handful of orderings. It does not expose 'posts where this custom field is between two values, joined with...' — and it shouldn't, because arbitrary meta queries from unauthenticated visitors are a denial-of-service invitation. If your use case is really 'query my data flexibly,' the API will feel like a keyhole. You'll either write custom routes for each query shape (fine, that's the intended path) or you should question whether the consumer ought to talk to the database through some other layer entirely.
Being invisible
The API happily tells anyone your post authors' usernames via /wp/v2/users (for users who have published posts) and enumerates your content structure. None of this is a vulnerability — it's public information that the HTML also exposes, just more conveniently machine-readable. But 'more convenient for machines' includes scrapers and credential-stuffing bots that harvest usernames. Reasonable hygiene: don't use valuable usernames as display names, rate-limit /wp-json/ at the edge like you rate-limit wp-login.php, and use a filter on rest_authentication_errors to require authentication for the users route if it genuinely bothers you. Don't disable the whole API; your editor needs it.
The headless question, answered honestly
Headless WordPress — the API (REST or GraphQL) as pure content backend, with a React/Next.js/whatever front end — is the use case the API gets marketed on hardest, so it deserves a straight assessment.
| Factor | Traditional WordPress | Headless |
|---|---|---|
| Front-end freedom | Themes, page builders | Anything you can code |
| Preview, menus, plugins that touch the front end | Just work | Each one is a project |
| Team required | WordPress generalist | WordPress + JS framework + deployment pipeline |
| Things that can break | One application | Two applications and the contract between them |
| Cost over time | Baseline | Commonly 2–3× baseline, as a rule of thumb |
Every WordPress feature that assumes it controls the front end — content preview, menu rendering, SEO plugin output, form plugins, shortcodes, oEmbeds — becomes something you reimplement or work around. That's not a dealbreaker for organizations with real engineering teams and real reasons: publishing one content pool to web, apps, and kiosks; strict front-end performance or security requirements; an existing framework investment. It is a dealbreaker disguised as a modernization for a 30-page business site whose marketing team currently edits everything themselves. The wry version: headless converts a content-management problem you'd solved into a software-engineering problem you haven't.
Decision rules you can apply in five minutes
- Moving content between WordPress and another system, either direction? Use the REST API with an application password on a least-privilege user. This is the happy path; don't overthink it.
- Adding interactivity to an existing theme? Use the API from front-end JavaScript. Ask for
_fields. Keep the theme. - Building custom back-end functionality? Register custom REST routes with proper
permission_callbacks. Never ship'permission_callback' => '__return_true'on a route that writes anything — that line appears in an alarming number of plugin vulnerability disclosures. - Expecting sustained public traffic on API routes? Design the caching first. If you can't say where a route's responses are cached, you aren't done.
- Considering headless? Only with a standing engineering team, a multi-channel or hard-requirement justification, and a budget that survives the honest multiplier. 'Our agency suggested it' is not a requirement.
- Worried about security? Rate-limit it, audit which plugins register routes (visit
/wp-json/and look at the namespace list — it's enlightening), and keep everything updated. Don't turn it off.
The short version
The WordPress REST API is plumbing, and good plumbing at that: reliable for moving content in and out, pleasant for small interactive features, and well-structured for custom endpoints. It is not a free pass to high-performance public APIs — you bring the caching — and it is not, for most sites, a reason to go headless. Treat it as the integration layer it was designed to be. The next time a tool asks 'can it talk to your WordPress site?', the answer has been yes since 2016. Quietly, at /wp-json/, it's been waiting.
Photo: Stanislav Kondratiev on Pexels