HTTP API WordPress

In PHP, there are several ways to send an HTTP request, while in WordPress there is only one. But this one method includes all the options supported by PHP - this is the API, it is a standard and it is convenient!

Introduction to the HTTP API

For complete beginners, it is probably worth explaining what an HTTP request is - it is a request from a browser to a server, or from one server to another, where a similar dialogue takes place:

  1. Hello server, can you show me the file: file.html?
  2. Hello! I can, here it is...

The dialogue looks like this (only in this case, our server acts as a client, making a request to another server):

ch-negotiation

When creating HTTP requests in PHP, one of the following options is usually used: cURL library or built-in PHP streams. To simplify and standardize the various ways of sending requests, the class WP_Http appeared in WordPress from version 2.7, which formed the basis of the HTTP API.

Why do we need the HTTP API

The convenience and necessity of such an API lies in the fact that different hosts support different options for sending requests, and some do not support any. The task of the HTTP API is to create a unified standard for using requests in WordPress, while ensuring that requests always work. If one method of request transport is not supported, an alternative will be found.

Another task is to simplify development. Plugin authors have to write a lot of code, invent bicycles, and make mistakes, all in order for their plugin to work with any host. With the HTTP API, this task should be simplified to a few built-in WordPress functions.

Another advantage of the HTTP API is that we have a unified standard for specifying data when working with different types of request transport, i.e. we always specify the same parameters and pass them to the HTTP API function, and the class already selects the appropriate transport type, for example cURL, modifies our parameters to be understandable for the current transport type, and sends the request.

Since version 2.7, the HTTP API only worked with basic request elements: header, body, and response. Since version 2.8, the following were added:

  • compression.
  • cookies.
  • proxy support.

Some of the functions are passive, i.e. they work automatically, without setting additional request parameters.

Requests Library

Since WP 4.6, the HTTP API works based on the Requests library, which is developed and maintained by WordPress.

Thus, the core WP_Http was completely replaced by the PHP library Requests, and now all requests are made through it.

Thus, technically the HTTP API WordPress has changed drastically, but the interface remains the same: everything works as it did before.

With the introduction of the Requests library, new capabilities are expected, some of which have already appeared (case-insensitive headers, support for international domains like böcean.ch). And others (parallel requests) will appear in subsequent releases and are currently only possible using the Requests library directly.

Starting from WP 4.6, the classes WP_Http_Curl and WP_Http_Streams are no longer used.

Previously, they worked as follows: WP_Http determines the transport type and calls another class corresponding to this type. The called class makes the request. By default, WordPress has two such classes for different types: WP_Http_Curl and WP_Http_Streams.

HTTP API WordPress today

The HTTP API WordPress today is a full-fledged API, in which many details are taken into account and hundreds of errors are fixed. It is also worth noting that before version WP 4.4, the HTTP API significantly differed from what it is now, so some points from this manual may not work on versions before 4.4.

Almost all transport capabilities can be changed through options or filters. For example, through the http_api_transports filter, you can add another, your own transport class. Or by setting constants in the wp-config.php file, you can enable proxy mode:

define('WP_PROXY_HOST', '192.168.84.101');
define('WP_PROXY_PORT', '8080');
define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com, *.wordpress.org');
define('WP_PROXY_USERNAME', 'login');
define('WP_PROXY_PASSWORD', 'pass');

To understand how the proxy works, see the WP_HTTP_Proxy{} class.

The HTTP API can be extended to work with the Twitter API, Google Maps API, etc.

HTTP API Functions and Request Sending

Using the HTTP API is very simple, for this purpose there are special API functions:

Request sending functions:

All functions return: an array or WP_Error.

  • The result is returned as an array and contains all the response data: content (body) of the response
  • WP_Error is returned in case of an unsuccessful request. If the server returns a non-standard error: 500, 404, etc., then you will receive response data, not WP_Error..

Response handling functions:

Function parameters

$url(string) (required)
The URL to which the request will be sent (from which the data will be obtained).
$args(array)
Request parameters. See the description of wp_remote_request() for the list of parameters.
Default: array() (default settings)
$response(array)
The request result returned by any request function.

Basic usage example

Now that we know all the request functions, let's try to create a simple request.

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');

Here, we sent a request to the URL: http://httpbin.org/get and added two request parameters ?a=b&c=d. As a result, $response will contain the following array:

Array (
	[headers] => Array (
			[server] => nginx
			[date] => Sun, 19 Jun 2016 08:18:20 GMT
			[content-type] => application/json
			[content-length] => 316
			[connection] => close
			[access-control-allow-origin] => *
			[access-control-allow-credentials] => true
		)

	[body] => {
		"args": {
			"a": "b",
			"c": "d"
		},
		"headers": {
			"Accept": "*/*",
			"Accept-Encoding": "deflate;q=1.0, compress;q=0.5, gzip;q=0.5",
			"Host": "httpbin.org",
			"User-Agent": "WordPress/4.5.2; http://wp-kama.ru"
		},
		"origin": "5.101.156.80",
		"url": "http://httpbin.org/get?a=b&c=d"
	}

	[response] => Array (
			[code] => 200
			[message] => OK
		)

	[cookies] => Array()

	[filename] =>

	[http_response] => WP_HTTP_Requests_Response Object (
		...
	)
)

As you can see, the result contains keys, each key is a part of the server response, where:

  • headers - contains the HTTP server response headers.
  • body - the response body. If we request an HTML page, it will contain HTML code. If JSON data is returned, it will contain a JSON string. And so on.
  • response - contains the HTTP response status code. For example, 200 - OK.
  • cookies - contains the cookies that the server requests to set in response to the request.
  • filename - contains the location or path of the file that was sent to the server and placed there. You can send a file with a POST request.
  • http_response - since WP 4.6. Contains the entire object of the Requests library request result.

Each part of the response can be obtained by selecting it from the array. For example, let's get the response headers we need:

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');
$headers = $response['headers'];

echo $headers['server']; //> nginx
echo $headers['content-type']; //> application/json
echo $headers['content-length']; //> 316

However, it is not recommended to obtain response parts in this way! Because the response can be different and needs to be checked or processed additionally.

To get each part of the response, it is recommended to use the special functions mentioned above. Let's get the response headers we need correctly:

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');

echo wp_remote_retrieve_header($response, 'server'); //> nginx
echo wp_remote_retrieve_header($response, 'content-type'); //> application/json
echo wp_remote_retrieve_header($response, 'content-length'); //> 316

With this, we can conclude the theoretical part of the HTTP API and move on to the examples.

Examples of using the WP HTTP API

#1 Passing Request Parameters in GET and POST

For a GET request, the arguments are passed directly in the URL:

$url = 'http://httpbin.org/get';

// request parameters
$params = array(
	'foo' => 'value 1',
	'bar' => 10
);

// ensure proper encoding of Cyrillic characters
$params = urlencode_deep( $params );

// Add the parameter to the URL
$url = add_query_arg( $params, $url );

// resulting URL:
// http://httpbin.org/get?foo=%D0%B7%D0%B0%D0%BD%D1%87%D0%B5%D0%BD%D0%B8%D0%B5+1&bar=10

// send the request
$response = wp_remote_get( $url );

$body = wp_remote_retrieve_body( $response );

print_r( $body );

For a POST request, the request parameters are passed in the body option:

$url = 'http://httpbin.org/post';

// request parameters
$args = [
	'body' => [
		'foo' => 'value 1',
		'bar' => 10,
	],
	'timeout' => '5',
];

$response = wp_remote_post( $url, $args );

#2 Modifying Request Parameters

Before sending the request, you can modify the default request settings. For example, you can set a timeout for the connection or specify a user agent.

Refer to the full list of parameters (settings) in the wp_remote_request() documentation.

The following example demonstrates how to pass such settings:

$url = 'http://httpbin.org/get';

$args = array(
	'timeout' => 5, // Time in seconds to wait for data.
	'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36',
);

$response = wp_remote_get( $url, $args );

#3 Retrieving Response Body (Our Favorite)

This example shows how to pass an additional header in the request and retrieve the body (text) of the response.

$url = 'http://httpbin.org/get?a=b&c=d';

// Passing a header in this case is not meaningful - this is just a demonstration.
$args = array(
	'headers' => array( "Content-type" => "application/json" )
);

$response = wp_remote_get( $url, $args );

$body = wp_remote_retrieve_body( $response );

#4 Retrieving Response Headers

#1 Making a Full Request and Retrieving Only the Response Headers

This example demonstrates how to make a full request but retrieve only the response headers:

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');

// retrieve only the response headers
$response_headers = wp_remote_retrieve_headers( $response ); //> array with all headers

// retrieve a specific response header
$content_type = wp_remote_retrieve_header( $response, 'content-type' ); //> application/json
#2 Requesting Only the Headers

In this example, we will not create a full request but instead request only the headers. To achieve this, we will use the wp_remote_head() function:

$response = wp_remote_head('http://httpbin.org/get?a=b&c=d');

$response_headers = wp_remote_retrieve_headers( $response ); //> array with all headers

We will receive a response to this type of request faster than a "full request." Therefore, if you do not need the response body, use this type of request.

#3 Requesting Only the Headers (Method 2)

Using the wp_get_http_headers() function, we can also request and immediately retrieve only the headers without creating a full request:

$response_headers = wp_get_http_headers('http://wordpress.org');

print_r( $response_headers );

/*
Array
(
	[server] => nginx
	[date] => Wed, 22 Jun 2016 02:03:20 GMT
	[content-type] => application/json
	[content-length] => 316
	[connection] => close
	[access-control-allow-origin] => *
	[access-control-allow-credentials] => true
)
*/

Examples 2 and 3 will never contain the response body, and their use may be useful for highly loaded APIs that restrict the number of requests within a period of time. Therefore, before retrieving the response body, it is necessary to check whether the data has been modified, whether the server responds to our request, etc. In such cases, it is recommended to first send a HEAD request, process the response to check for new data, and only then make a full request to retrieve the response body.

#4 Debugging: Request Data

Variant 1:

To delve into the WordPress HTTP API, I recommend the following code snippet using the http_api_debug event:

add_action( 'http_api_debug', function( $response, $type, $class, $args, $url ) {

	// You can change this from error_log() to var_dump(), but it can break AJAX requests
	echo sprintf( "Request URL: %s\n", print_r( $url, 1 ) );
	echo sprintf( "Request Args: %s\n", print_r( $args, 1 ) );
	echo sprintf( "Request Response : %s\n", print_r( $response, 1 ) );

}, 10, 5 );
Variant 2:

Sometimes, you may need to obtain the request data that will be sent immediately before the request. The latest hook I managed to find, immediately before the request is made based on the determined $transport, is the hook requests-(hook) - requests-requests.before_request:

add_action( "requests-requests.before_request", function( $parameters, $request, $url ){

	/* $parameters
		https://example.com/wp-json/wp/v1/post/456?
	*/

	/* $request
	Array
	(
		[Authorization] => Basic a2FtYTp1a2RDFTHWjQgYTRVRyBYVjJzIFU5STIgeW5oiu==
		[Cookie] => my_cookie=cookie_value
	)
	*/

	/* $url
		null
	*/

}, 10, 3 );

After the transport is obtained - Requests::get_transport() and the request is made, the hook requests-requests.before_parse will be triggered, on which you can obtain the response data (although you can also obtain the response data in the standard way):

add_action( "requests-requests.before_parse", function( $parameters, $request, $url ){

	// $parameters
	/*
		HTTP/1.1 200 OK
		Date: Mon, 23 May 2022 10:02:26 GMT
		Server: Apache/2.4.41 (Ubuntu)
		Upgrade: h2,h2c
		Connection: Upgrade, close
		X-Cache: miss
		X-Robots-Tag: noindex
		Link: ; rel="https://api.w.org/"
		X-Content-Type-Options: nosniff
		Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link
		Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
		Expires: Wed, 11 Jan 1984 05:00:00 GMT
		Cache-Control: no-cache, must-revalidate, max-age=0
		Allow: GET, POST
		Content-Length: 1630
		Content-Type: application/json; charset=UTF-8

			RESPONSE Content ...
	*/

	// $request
	/*
	 https://example.com/wp-json/wp/v1/post/456?
	*/

	// $url
	/*
	Array
	(
		[Authorization] => Basic a2FtYTp1a2RDFTHWjQgYTRVRyBYVjJzIFU5STIgeW5oiu==
		[Cookie] => my_cookie=cookie_value
	)
	*/

}, 10, 3 );

#6 Retrieving Response Code

$response = wp_remote_get('http://httpbin.org/get?a=b&c=d');

echo wp_remote_retrieve_response_code( $response ); //> 200
echo wp_remote_retrieve_response_message( $response ); //> OK

#7 Custom Request Type

In addition to GET and POST request types, we can send any other type of request, such as PUT or DELETE.

For this, we use the wp_remote_request() function, which is the basis for all request functions:

$url = 'http://httpbin.org';

$args = array(
	'method' => 'PUT',
	//'method' => 'DELETE'
);

$response = wp_remote_request( $url, $args );

#8 Retrieving Movie Data from Kinopoisk

This example demonstrates how to retrieve movie data from Kinopoisk based on the specified movie ID.

/**
 * Get information about a movie from the Kinopoisk website
 * Works based on the service http://docs.kinopoiskapi.apiary.io/.
 *
 * @param int $id Movie ID on the website
 * @return string|WP_Error Returns the response body in case of success and a WP_Error object in case of failure
 */
function kinopoisk_get_movie( $id ) {

	// GET request parameters
	$params = array(
		'filmID' => absint( $id ),
	);

	// Create a URL with parameters
	$url = 'http://api.kinopoisk.cf/getFilm';
	$url = add_query_arg( $params, esc_url_raw($url) );

	// make the request
	$response = wp_remote_get( $url );

	// Check the response code
	$response_code    = wp_remote_retrieve_response_code( $response );
	$response_message = wp_remote_retrieve_response_message( $response );
	$response_body    = json_decode(wp_remote_retrieve_body( $response ));

	if ( 200 != $response_code && ! empty( $response_message ) )
		return new WP_Error( $response_code, $response_message );

	elseif ( 200 != $response_code )
		return new WP_Error( $response_code, 'Unknown error' );

	elseif( ! $response_body )
		return new WP_Error( 'nodata', 'No data about the movie or the movie does not exist in the database' );

	else
		return $response_body;
}

// Example of using the function ---

// Request
$res = kinopoisk_get_movie( 714888 );

// Display the error or information
if ( is_wp_error( $res ) )
	echo 'Error while requesting from IMDB: '. wp_strip_all_tags( $res->get_error_message() );

else
	echo 'Movie: "' . esc_html( $res->nameRU ) .'" ('. (int) $res->year .' year). Rating: '. $res->ratingData->rating;

// resulting output:
// Movie: "Star Wars: The Force Awakens" (2015 year). Rating: 7.3

#9 Cache Management

To manage caching, you need to specify the Cache-Control header:

$readme_url = 'http://example.com/foo';
$http_params = [
	'headers' => [
		'Authorization' => "token $auth_token",
		'Cache-Control' => 'no-cache',
	],
];

$res = wp_remote_get( $readme_url, $http_params );

#10 Hook: Allow Internal Requests with htpasswd

Suppose a password is set on the server using the .htpasswd file, see note. In this case, cron tasks will stop working, and possibly other internal requests. To solve this problem, you can set default authentication parameters for all WP HTTP API requests:

if( 'production' !== wp_get_environment_type() ){

	add_filter( 'http_request_args', function ( $parsed_args, $url ){

		if( str_contains( $url, parse_url( home_url(), PHP_URL_HOST ) ) ){

			$log_pass = 'login:password';

			$auth = & $parsed_args['headers']['Authorization'];

			if( ! $auth ){
				$auth = 'Basic ' . base64_encode( $log_pass );
			}

		}

		return $parsed_args;
	}, 10, 2 );

}

You can place this code, for example, in must-use plugins.

#11 Example of using a proxy

The proxy will work for https as long as the proxy itself supports this protocol.

$proxy = '180.168.232.64:8001';
$proxyauth = 'k7yQJa:R9Cdeu';
$url = 'https://example.com';

[ $host, $port ] = explode( ':', $proxy );
[ $log, $pass ] = explode( ':', $proxyauth );

define( 'WP_PROXY_HOST', $host );
define( 'WP_PROXY_PORT', $port );
define( 'WP_PROXY_USERNAME', $log );
define( 'WP_PROXY_PASSWORD', $pass );
//define( 'WP_PROXY_BYPASS_HOSTS', 'example.com, *.wordpress.org' );

$resp = wp_remote_get( $url );
$headers = wp_remote_retrieve_headers( $resp );
$code = wp_remote_retrieve_response_code( $resp );
$body = wp_remote_retrieve_body( $resp );

var_dump( $code );
print_r( $headers );
echo $body;

To work with socks5 and socks4 protocols, they must be enabled (supported) by the CURL server, and it will also be necessary to forcibly specify them. Here's an example of how to do this:

add_action( 'http_api_curl', 'wpkama_curl_before_send', 100, 3 );

function wpkama_curl_before_send( $handle, $request, $url ) {

	curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5 );
	// curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4 );
	// curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4A );
}

If you make a request using CURL directly, it will look like this:

$proxy = '180.168.232.64:8001';
$proxyauth = 'k7yQJa:R9Cdeu';
$url = 'https://example.com';

$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_PROXY, $proxy );
curl_setopt( $ch, CURLOPT_PROXYUSERPWD, $proxyauth );
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_HEADER, 1 );
$curl_scraped_page = curl_exec( $ch );
curl_close( $ch );

echo $curl_scraped_page;

You can buy inexpensive proxies here: proxy6.net. Enter the partner code RgJ8zx8ycP and get a 5% discount.

Saving the result of the request (caching)

It is always necessary to cache the result of an HTTP request, there are probably no exceptions.

Caching the result is a very important aspect - saving it somewhere. This is important because without caching, the pages of the site will be generated slowly. To understand the importance of caching, it is necessary to understand how everything happens and why HTTP requests will slow down page loading.

The point is that when using one of the HTTP API functions, our server sends a request to another server and waits for a response. During this waiting period, no PHP operations are performed on our server - it simply stops script execution and waits. This waiting can be quite long. Typically, this waiting lasts from 0.5 to 5 seconds.

The picture looks something like this: an HTTP request came to our server: http://example.com/page, and our server should display the page page. To do this, our server starts generating this page, and various modules are connected before performing any page generation operations: nginx, apache, PHP, PHP modules, etc. Then page generation begins, and the more complex the page, the longer it will take to generate. But at the same time, the server uses its data from RAM, disk, database, etc. it gets all this data in fractions of a second. And now imagine that at some point of data collection and page generation, you need to get data from another server. At this moment, a request is assembled and sent, and our server waits for a response: until the request reaches the other server, until all the modules are assembled there, until the page (response) is generated, and until it returns to us through all the nodes of the Internet connection. All this in terms of the server is very long - from 0.5 to 5 seconds. And what if there are several such requests, for example, for data in some table?

Now, answer yourself, should we save the request data to our server, where it can get it in fractions of a second?

Another problem: "bandwidth"

In theory, it is possible to do without caching if there is only one HTTP request on your page and the server processing this request allows an unlimited number of requests to be sent to it. But as a rule, if you use some API, the server processing the requests has a limit on the number of requests in a period of time, the so-called "bandwidth" of the API. And at some point, the remote server simply will not respond to your request, and you will not receive the necessary data.

Based on all of the above, we can make the only correct conclusion - HTTP requests should always be cached, and it is not difficult to do this at all.

Caching in temporary options (transients)

So we sent a request, received the result, and now we need to save it. There are many options for where to save the result. For example, we can write the result to a file and take it from there next time, or we can save the result in the database. But this will require additional code, which is usually not needed because WordPress already has suitable functions:

Where:

$transient(string) (required)
The name of the temporary option. The maximum length is 172 characters. Data with escaped slashes is expected, and the slashes are automatically cleaned before writing to the database. The function automatically protects against SQL injections.
$value(mixed) (required)
The value of the temporary option. Data with escaped slashes is expected, and the slashes are automatically cleaned before writing to the database. The function protects against SQL injections. Serialization, if an array is passed, is also done automatically.
$expiration(string/array/number/object/boolean)

The lifetime of the option in seconds, starting from the present moment. For example, to set an option for one day, you need to specify 60 * 60 * 24.

If 0 is specified, the option will never be deleted and will not have an expiration date. This will make it a regular option, only with the prefix _transient_ in its name - this will mean that it is temporary and when cleaning the wp_options table, it can be safely deleted without the risk of damaging anything.
Default: 0

An important point about temporary options (transients). They can work with object caching. That is, they are written to the wp_options database table for a while. But if the site has an object caching module (plugin) installed, they are not written to the database, but are stored in object cache files.

#1 Examples of caching an HTTP request in a temporary option

Let's say we get the exchange rate of the dollar from an external site via an HTTP request. To speed up page loading, we need to cache the received result for 12 hours.

$transient = 'one_usd_in_rub'; // the name of the temporary option

$usd_in_rub = get_transient( $transient ); // let's get the saved data

// if there is no data or it has expired, get and save it
if( ! $usd_in_rub ){
	$url = 'http://www.cbr.ru/scripts/XML_daily.asp';
	$expiration = DAY_IN_SECONDS / 2; // cache lifetime - half a day

	$resp = wp_remote_get( $url ); // get the data

	// if the response status is 200 - OK
	if( wp_remote_retrieve_response_code($resp) === 200 ){
		$body = wp_remote_retrieve_body( $resp );

		$xml = simplexml_load_string($body); // parse the XML data
		$data = json_decode(json_encode($xml)); // convert it to a regular object

		//print_r( $data );

		$USD = wp_list_filter( $data->Valute, array('CharCode'=>'USD') ); // get only USD data
		$USD = array_shift($USD);

		/*
		stdClass Object (
			[NumCode] => 840
			[CharCode] => USD
			[Nominal] => 1
			[Name] => Доллар США
			[Value] => 64,0165
		)
		*/

		// echo "$USD->Nominal $USD->Name equals $USD->Value rub."; //> 1 US Dollar equals 64,0165 rub.

		$usd_in_rub = $USD->Value;
	}
	// the response status is not 200 - OK
	else
		$usd_in_rub = 'no data';

	// save the dollar exchange rate in a temporary option
	set_transient( $transient, $usd_in_rub, $expiration );
}

echo 'The exchange rate of the dollar to the ruble today: $1 = '. $usd_in_rub .' rub.';
//> The exchange rate of the dollar to the ruble today: $1 = 64,0165 rub.

The result of this code will be the retrieval of the latest dollar exchange rate every half day.

Deleting a temporary option

Let's say you no longer use a temporary option and it is not being updated, and is no longer needed. In this case, it is better to delete it. The temporary option itself is not deleted and will remain in the wp_options table until you clear all temporary options with a special plugin or something else. Therefore, it is recommended to delete it manually if it is no longer used. You can do this by calling such a line once:

delete_transient('name_of_temporary_option');

Caching in meta fields

Temporary options are suitable for caching at all times, but it is not always reasonable. To explain, let's consider the following situation: you have records on your site, for example, movies, and for each movie, you parse data from an external source, for example, the rating on Kinopoisk. Such data also needs to be updated occasionally, for example, once a week.

In this case, it would be more logical to save the received data not in the options table, but in the meta fields of the record. Because it will be more convenient to get them, we will be able to sort the records by rating (by meta field), and it will work faster and more efficiently in the code. Because when setting the $expiration parameter for temporary options, two simple database queries are made to get them, whereas WordPress works much more efficiently with meta fields.

But when using meta fields, you need to manually check the data's lifetime and update it when necessary. The data's lifetime can be written to a similar named meta field. By the way, in temporary options in the wp_options table, the same thing is done - a temporary option is created, and next to it, another option with a similar name and the time value when the option becomes obsolete.

#1 Examples of caching HTTP request in meta-fields

For example, let's take an analogy where the records are movies and the function kinopoisk_get_movie( $id ) from the above example is used to retrieve movie data from Kinopoisk. Also, let's assume that for each record, we already have the movie ID on Kinopoisk stored in the meta-field kinopoisk_id.

Now, let's write the code to update the rating of each movie weekly. To do this, we'll create a function that displays the rating and simultaneously checks if the data needs to be updated or retrieved:

function get_film_rating( $post_id = 0 ){
	if( ! $post_id ) $post_id = $GLOBALS['post']->ID;

	$rating  = get_post_meta( $post_id, 'kinopoisk_rating', 1 );
	$up_time = get_post_meta( $post_id, 'kinopoisk_rating_uptime', 1 );

	// Check if data needs to be updated
	if( time() > $up_time + WEEK_IN_SECONDS ){
		// Time is expired or no data in meta-fields

		$film_id = get_post_meta( $post_id, 'kinopoisk_id', 1 );

		// Try to retrieve data from Kinopoisk
		$res = kinopoisk_get_movie( $film_id );

		// Data retrieved successfully!
		if( ! is_wp_error($res) && $res->ratingData->rating )
			$rating = $res->ratingData->rating;
		// Error while retrieving data. Keep the rating as it was or set it to -1 (no rating) if there was no rating before
		else
			$rating = $rating ?: '-1';

		// Update meta-fields
		update_post_meta( $post_id, 'kinopoisk_rating_uptime', time() );
		update_post_meta( $post_id, 'kinopoisk_rating', $rating );
	}

	return $rating;
}

// Function is ready, now call it on the page where the rating needs to be displayed:
echo get_film_rating();

HTTP Response Codes

Standard HTTP status code/reason phrases map:

/** @var array Map of standard HTTP status code/reason phrases */
$phrases = [
	100 => 'Continue',
	101 => 'Switching Protocols',
	102 => 'Processing',
	200 => 'OK',
	201 => 'Created',
	202 => 'Accepted',
	203 => 'Non-Authoritative Information',
	204 => 'No Content',
	205 => 'Reset Content',
	206 => 'Partial Content',
	207 => 'Multi-status',
	208 => 'Already Reported',
	300 => 'Multiple Choices',
	301 => 'Moved Permanently',
	302 => 'Found',
	303 => 'See Other',
	304 => 'Not Modified',
	305 => 'Use Proxy',
	306 => 'Switch Proxy',
	307 => 'Temporary Redirect',
	400 => 'Bad Request',
	401 => 'Unauthorized',
	402 => 'Payment Required',
	403 => 'Forbidden',
	404 => 'Not Found',
	405 => 'Method Not Allowed',
	406 => 'Not Acceptable',
	407 => 'Proxy Authentication Required',
	408 => 'Request Time-out',
	409 => 'Conflict',
	410 => 'Gone',
	411 => 'Length Required',
	412 => 'Precondition Failed',
	413 => 'Request Entity Too Large',
	414 => 'Request-URI Too Large',
	415 => 'Unsupported Media Type',
	416 => 'Requested range not satisfiable',
	417 => 'Expectation Failed',
	418 => 'I\'m a teapot',
	422 => 'Unprocessable Entity',
	423 => 'Locked',
	424 => 'Failed Dependency',
	425 => 'Unordered Collection',
	426 => 'Upgrade Required',
	428 => 'Precondition Required',
	429 => 'Too Many Requests',
	431 => 'Request Header Fields Too Large',
	451 => 'Unavailable For Legal Reasons',
	500 => 'Internal Server Error',
	501 => 'Not Implemented',
	502 => 'Bad Gateway',
	503 => 'Service Unavailable',
	504 => 'Gateway Time-out',
	505 => 'HTTP Version not supported',
	506 => 'Variant Also Negotiates',
	507 => 'Insufficient Storage',
	508 => 'Loop Detected',
	511 => 'Network Authentication Required',
];