A client came to me with a tricky requirement: sync users between two separate WordPress sites. One site was the source of truth, and the other had to mirror its user base in close to real time. That is a common problem, and the WordPress REST API fits it well. My goal was to build a clean, reusable API client to handle the traffic between them.
My first thought was to grab one of the existing PHP clients for the WordPress API. Why reinvent the wheel? But I hit a snag fast. Most of them are built for the wider PHP world, so they pull in external dependencies like Guzzle to handle HTTP requests. On a project already running on WordPress, that felt wrong. It is like hauling in a whole toolbox when the one screwdriver you need is already in the kitchen drawer.
WordPress already ships with a solid HTTP API built on the WP_Http class. It is stable, and it is already loaded on every request. Pulling in another library to do the same job just invites version conflicts and extra bloat. I have watched that go badly on a high-traffic site.
Building a lean WordPress API client
So I built my own lightweight client. The rule was simple: use WordPress internals and stay completely self-contained, with no outside libraries. I first saw this approach laid out by Carl Alexander on his blog, which is worth reading if you want more detail. The core of the class is its constructor, where we inject the dependencies it needs.
public function __construct( WP_Http $http, $base_url, $username, $password ) {
$this->http = $http;
$this->base_url = $base_url;
$this->token = base64_encode( $username . ':' . $password );
}
Passing in the WP_Http object is dependency injection. It keeps the class testable and decoupled from WordPress’s global state. The class does not care how the WP_Http object was built; it just needs something that can make GET requests. The last two parameters are simple: the URL of the site you are connecting to and the credentials for basic authentication.
The request method itself is short. It builds the URL, adds the authorization header, and hands the whole thing to WP_Http. When something fails, it returns a WP_Error object, the way any well-behaved WordPress code should.
private function get( $endpoint, array $query = [] ) {
$url = $this->base_url . $endpoint;
if ( ! empty( $query ) ) {
$url .= '?' . http_build_query( $query );
}
$args = [
'headers' => [
'Authorization' => 'Basic ' . $this->token,
],
];
$response = $this->http->get( $url, $args );
if ( is_wp_error( $response ) ) {
return $response;
}
$body = wp_remote_retrieve_body( $response );
$decoded = json_decode( $body, true );
if ( json_last_error() !== JSON_ERROR_NONE ) {
return new WP_Error( 'invalid_json', 'The JSON response could not be decoded.' );
}
return $decoded;
}
So what’s the point?
You could just call wp_remote_get() with a pile of arguments every time you hit an API endpoint, but that gets messy fast. Wrapping the logic in a class instead gives you a few things:
- Reusable: you can drop this client in anywhere in the project to talk to the remote site.
- Clean: the authentication and URL-building details stay hidden behind the class.
- Robust: it returns a
WP_Errorobject on failure, the standard way WordPress handles errors.
That is the difference between a quick hack and something you can maintain. A bit of thought now saves your future self, or the next developer, a real headache later.
This stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, get in touch with my team. We have probably seen your problem before.