WordPress’s HTTP API wraps PHP’s curl and streams transports into a unified interface. While wp_remote_get() covers most third-party API integrations, many modern APIs — payment gateways, CRM systems, webhook deliveries, and OAuth token endpoints — require POST requests with a JSON or form-encoded body and authentication headers. wp_remote_post() handles all of this through its $args array, with support for custom headers, request body formats, timeout control, and TLS verification. Pairing it with wp_remote_retrieve_response_code() and wp_remote_retrieve_body() gives clean, testable error handling without cURL boilerplate. For authenticated API calls with OAuth bearer tokens, API keys, or Basic Auth, setting the correct headers argument is the entire solution.
Problem: A plugin needs to push order data to a CRM via its REST API. The endpoint requires a POST request with a JSON body and a Bearer token in the Authorization header. Errors from the API (4xx/5xx) must be handled distinctly from network failures.
Solution: Use wp_remote_post() with 'headers', 'body', and 'data_format' => 'body'. Check is_wp_error() for network failures, then wp_remote_retrieve_response_code() for API-level errors.
<?php
// ── POST with JSON body and Bearer token ──────────────────────────────
function push_order_to_crm( array $order_data ): array|WP_Error {
$api_key = get_option( 'my_plugin_crm_api_key', '' );
$endpoint = 'https://api.crm-example.com/v2/orders';
$response = wp_remote_post( $endpoint, [
'method' => 'POST', // default for wp_remote_post, explicit for clarity
'timeout' => 15, // seconds before giving up
'redirection' => 3,
'sslverify' => true, // never set false in production
'headers' => [
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-Plugin-Version' => '1.0.0',
],
'body' => wp_json_encode( $order_data ), // JSON-encode the body
'data_format' => 'body', // send body as raw string
] );
// Network-level failure (DNS, timeout, SSL error)
if ( is_wp_error( $response ) ) {
error_log( 'CRM push network error: ' . $response->get_error_message() );
return $response;
}
$status = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
if ( $status >= 400 ) {
$message = $data['message'] ?? $data['error'] ?? "HTTP $status";
error_log( "CRM push API error ($status): $message" );
return new WP_Error( 'crm_api_error', $message, [ 'status' => $status ] );
}
return $data ?? [];
}
// ── POST with form-encoded body (application/x-www-form-urlencoded) ───
$token_response = wp_remote_post( 'https://oauth.example.com/token', [
'body' => [
'grant_type' => 'client_credentials',
'client_id' => 'my-client-id',
'client_secret' => 'my-secret', // store in wp_options, not hardcoded
],
// WordPress automatically sets Content-Type: application/x-www-form-urlencoded
// and encodes the 'body' array when 'data_format' is NOT 'body'
'timeout' => 10,
] );
if ( ! is_wp_error( $token_response ) ) {
$token_data = json_decode( wp_remote_retrieve_body( $token_response ), true );
$access_token = $token_data['access_token'] ?? '';
// Store with an expiry:
set_transient( 'my_plugin_crm_token', $access_token, $token_data['expires_in'] ?? 3600 );
}
// ── Basic Auth ────────────────────────────────────────────────────────
wp_remote_post( $endpoint, [
'headers' => [
'Authorization' => 'Basic ' . base64_encode( 'username:password' ),
],
'body' => wp_json_encode( $payload ),
'data_format' => 'body',
] );
NOTE: Never hardcode API keys or secrets in plugin files. Store them as WordPress options (update_option( 'my_plugin_api_key', $key )) or as environment variables read via getenv(). The 'timeout' argument defaults to 5 seconds in WordPress, which is often too short for external APIs under load — set it explicitly to 15–30 seconds for payment or CRM integrations. Always use 'sslverify' => true in production; setting it to false disables certificate validation and makes the connection vulnerable to man-in-the-middle attacks. For high-volume API calls, cache tokens with set_transient() using the API's stated expiry time to avoid a new OAuth token request on every WordPress request.