Parts of WordPress that look like they’re reading straight from the database are often actually going through an HTTP API behind the scenes. Loading a post list dynamically with JavaScript, or having an external PHP script pull “just the titles and excerpts of the latest three posts” — both of these can be done without touching SQL directly, by calling the REST API that ships with WordPress itself. Understanding this means that whenever you need one system to peek into another WordPress site’s content, you don’t have to install a plugin or scrape the admin screen to do it.
Note: REST (Representational State Transfer) is an API design style where a resource — a post, a page, a user — is identified by a URL, and HTTP methods (GET/POST/PUT/DELETE) express what you want to do with it. WordPress’s REST API applies that model to posts, pages, and other content, exchanging them as JSON.
What /wp-json/ Actually Does
Since WordPress 4.7 (2016), every standard install ships with the REST API built in — no plugin required. Appending /wp-json/ to a site’s URL and opening it in a browser returns a JSON listing of the endpoints that site exposes.
The endpoint most people reach for first is /wp-json/wp/v2/posts, which returns published posts as a JSON array. Each entry carries the post ID, title (title.rendered), content (content.rendered), excerpt (excerpt.rendered), publish date (date), and permalink (link), among other fields.
GET https://example.com/wp-json/wp/v2/posts?per_page=3
Query parameters like per_page control how many results come back, which page, filtering by category, and so on. This particular endpoint requires no authentication because it’s serving something anyone could already see on the site: published posts. Fetching drafts, or writing — creating, updating, deleting a post — is a different story and does require authentication, covered below.
Pulling Related Data in One Round Trip With _embed
The plain response doesn’t include the featured image itself or the category name as text — only references to their IDs (featured_media as a numeric ID, categories as an array of numeric IDs). To turn those into an actual image URL or category name, you’d normally need a follow-up request to a separate endpoint (/wp-json/wp/v2/media/<ID>, /wp-json/wp/v2/categories/<ID>) for each one.
The _embed parameter collapses that into a single request.
GET /wp-json/wp/v2/posts?per_page=3&_embed
Adding _embed populates an _embedded field on each post containing the actual featured-image data (wp:featuredmedia) and taxonomy terms (wp:term) inline, so the caller gets the post and its related data in one round trip instead of firing off an extra request per item. Avoiding that per-item fan-out — sometimes called the N+1 problem — is one of the first things worth knowing when working with any REST API, WordPress or otherwise.
A Working Example: The “Latest Posts” Block on the Landing Page
wpmm_fetch_latest_posts() in server/wpmm-web/includes/blog_latest.php uses exactly this mechanism. The “latest posts” section shown on the JP and EN landing pages doesn’t query a shared database — it makes an HTTP request to the blog’s own WordPress instance at /blog/wp-json/wp/v2/posts?per_page=3&_embed&status=publish and builds the section from whatever comes back.
$api_url = ($lang === 'en')
? "https://en.wpmm.jp/blog/wp-json/wp/v2/posts?per_page={$count}&_embed&status=publish"
: "https://wpmm.jp/blog/wp-json/wp/v2/posts?per_page={$count}&_embed&status=publish";
From the returned JSON it pulls the title (title.rendered), excerpt (excerpt.rendered), permalink (link), featured-image URL (_embedded['wp:featuredmedia'][0]['source_url']), and category name (_embedded['wp:term'][0]), and renders them into HTML. The landing page code never needs to know anything about how the blog’s WordPress is structured internally — only the shape of the JSON the REST API returns. That’s the general benefit of API-based integration over direct database access: the caller depends on a stable interface, not on implementation details it has no business knowing.
Caching So Every Page Load Doesn’t Trigger a Fresh API Call
Firing an HTTP request to the REST API on every single landing-page load would add latency to the landing page and unnecessary load to the blog’s WordPress instance. So wpmm_fetch_latest_posts() caches the result as a JSON file under sys_get_temp_dir() for one hour. While the cache is fresh, it’s returned immediately with no API call at all; once it’s older than an hour, the function re-fetches and refreshes the cache.
The failure handling deserves a closer look. If the REST API request fails — the blog’s WordPress is briefly down, the request times out — the function doesn’t just return an empty result. It first checks whether a stale cache file is still sitting around and serves that instead. Only if no usable cache exists at all does it fall back to an empty array, at which point the calling landing page skips rendering the “latest posts” section entirely rather than showing something broken. That’s a graceful degradation ladder: prefer fresh data, fall back to stale data if fresh isn’t available, and fall back to showing nothing at all rather than showing something wrong.
Why Write Endpoints Need Authentication
Endpoints that change state — creating, updating, or deleting a post — don’t allow anonymous access. WordPress’s REST API verifies who’s making the request using a nonce tied to a logged-in session (for JavaScript running inside the admin screen) or Application Passwords, a standard feature since WordPress 5.6 that lets you issue a dedicated username-and-password pair for an external application to authenticate with.
The underlying distinction — a read-only GET against public information needs no authentication, while a POST/PUT/DELETE that changes state does — isn’t specific to WordPress; it’s a baseline assumption worth carrying into any REST API. Asking “should anyone be able to read this, or does this action require proving who’s asking” up front tends to make the rest of the design fall into place.
Summary
| Point | Takeaway |
|---|---|
/wp-json/ |
Built into WordPress since 4.7, no plugin needed. Returns a JSON list of available endpoints |
/wp/v2/posts |
The standard endpoint for fetching published posts; readable via GET without authentication |
_embed |
Bundles related data (featured image, taxonomy terms) into the same response, avoiding follow-up requests |
Working example (blog_latest.php) |
The landing page calls the blog WordPress’s REST API externally, with zero knowledge of its internal implementation |
| Caching design | One-hour file cache, with a staged fallback to stale cache data on API failure |
| Authentication for writes | GET (read) and POST/PUT/DELETE (write) carry fundamentally different authentication requirements |
The WordPress REST API works as a window that lets an external system peek safely into WordPress’s data without installing a plugin for it. Starting from the read-only endpoints, and pairing them with caching plus a staged fallback for failures, keeps a temporary hiccup on the WordPress side from taking down whatever system is calling into it.