Getting post meta via WordPress REST API


If you find this free website useful – why don’t you support this with a donation? It is easy…. read more ….

This the second post post in my series about getting and updating WordPress data via a spreadsheet.  The first post focused on the basics of Google Apps Scripting and the API call to the WP REST API – if you haven’t seen it you can read it here.

This post is about meta data. In case you are not really clear, meta data in WordPress post terms are any extra data that isn’t part of the WordPress post schema.  More often or not this data is created and maintained by plugins.

To give you a clear example, if say you use Yoast SEO plugin, the Yoast SEO version of the post title is stored in meta data item called _yoast_wpseo_title  these are also known as custom fields, and WordPress treats custom fields starting with an _ as hidden.

I read many posts on howto access meta and most of them were wrong and out dated.  The WP  Rest documentation tells you about problems with meta  and also warns you they are changing their thoughts on this – so beware – this may even be out of date soon.

If you look at the ‘posts schema’ you will see an entry for meta, but more often than not, you will see an entry “meta”:[]  i.e. empty, even if it is not e.g. curl http://demo.wp-api.org/wp-json/wp/v2/posts/1 

For unsetialized ( i.e. text string meta) the solution is much simpler that many blog posts I read make out,  you just need to tell WordPress that the meta can be exposed in the API.

To do this, you are going to need to write ( or copy at least ) some PHP code.   Some tutorials, will tell you that you add extra code to your theme’s functions.php, whilst this will work, this is not the right way to do things, themes are for layouts, plugins are for functions. If you add it to your (child) theme and one day you change your theme then the extra features will break.  So write a plugin, its not hard.

Before you write the plugin, you will have been testing on a website on the internet ( you have to as Google can’t see your local host ), so here is a quick and easy way to get editing on your remote site without faffing around with FTP etc.   Install ICEcoder with this plugin to be able to follow along.

Once you are in the editor navigate to the wp-content/plugins folder, right click on plugins and select ‘New File’ and give your plugin a name like myfunctions.php .

The copy the code below and paste it into the code area and ctrl-s  to save it.

<?php
/**
 * Plugin Name:       My Custom Code
 * Plugin URI:        http://fullworks.net/wordpress-plugins
 * Description:       Custom code
 * Version:           1.0
 * Author:            Fullworks
 * Author URI:        http://fullworks.net/
 *
 */
if ( ! defined( 'WPINC' ) ) {
	die;
}
add_action( 'rest_api_init', 'register_posts_meta_field' );

function register_posts_meta_field() {
    $object_type = 'post';
    $args1 = array( // Validate and sanitize the meta value.
        // Note: currently (4.7) one of 'string', 'boolean', 'integer',
        // 'number' must be used as 'type'. The default is 'string'.
        'type'         => 'string',
        // Shown in the schema for the meta key.
        'description'  => 'A meta key associated with a string meta value.',
        // Return a single value of the type.
        'single'       => true,
        // Show in the WP REST API response. Default: false.
        'show_in_rest' => true,
    );
    register_meta( $object_type, '_yoast_wpseo_title', $args1 );
}

Lets just take a second to explain what we are doing.

when the Rest API is initiated ‘rest_api_init’  we are calling our function which is basically registering _yoast_wpseo_title   but this time it is setting it to show in the Rest API ‘show_in_rest’ => true,

OK, so when you have saved your plugin, go to your admin > plugins and find it, and activate it, hopefully it activates without errors.

So you now are ready to go, let just extend the script we wrote in the first article to also read the meta items.

If you are following this along, you will obviously need to install the Yoast SEO plugin into your remote test WordPress, and also set a few Yoast titles up in posts.

The script code would now look like this

function onOpen() {
  // setup a custom menu
  SpreadsheetApp.getUi() 
      .createMenu('WordPress')
      .addItem('Get Posts', 'getPosts')
      .addToUi();
  setupSheet();
}

function setupSheet() {
  // just clear sheet and set & style headings
 var ss = SpreadsheetApp.getActiveSpreadsheet();
  // set the tab to post - just for neatness
 var sheetname = ss.getSheets()[0].setName('Posts');
  // clear the sheet
 var range = ss.getDataRange();
 range.clear();
 //set headings
  ss.getRange('A1').setValue('Post ID');
  ss.getRange('B1').setValue('Title');
  ss.getRange('C1').setValue('Yoast Title');
  ss.getRange('D1').setValue('Updated');
  
  ss.getRange('A1:C1').setFontWeight('bold');
  ss.getRange('A1:C1').setFontStyle('italic');

}


function getPosts(){
  setupSheet();
  // loop here as we need get back posts in chunks, Rest API max is 100 post
  offset = 0;
  per_page = 10;
  while ( (got=getPage(offset,per_page)) > 0 ) {
    offset+=got;
    Logger.log('offset %s', offset);
  }
}


function getPage(offset,per_page) {
  // gets posts in chunks of per_page
  var ui = SpreadsheetApp.getUi();  // used for error messages
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  
  var options = {
      'method': 'get',
      "contentType" : "application/json",
      'muteHttpExceptions': true
    }
  var apiHost = 'http://mytestdomain/wp-json';   // set to your own domain
  url = apiHost + '/wp/v2/posts?per_page='+per_page+'&offset='+offset;
  try {
    var response = UrlFetchApp.fetch(url, options);
    var data = JSON.parse(response)
    // loop through the map and output to sheet
    for (i = 0; i < data.length; i++) {
     row=offset+i+2;  // set the row to make sure it is below header and takes into account the paging
     ss.getRange('A'+row).setValue(data[i].id);
     ss.getRange('B'+row).setValue(data[i].title.rendered);
     ss.getRange('C'+row).setValue(data[i].meta._yoast_wpseo_title);
     ss.getRange('D'+row).setValue(data[i].modified);
  }
    return data.length;
  } catch(error) {
    var result = ui.alert( error.toString());
  }
  return 0;
}

See what I did there, just inserted column C as a title and put .meta._yoast_wpseo_title   into column C

Change the domain to your actual internet available test domain. Save and run from the spreadsheet. Refer back to the first article if you need to see how.

If you have any questions or comments, please leave them.

In the next post I will work through a method to get authentication so you can later update.


11 responses to “Getting post meta via WordPress REST API”

    • Hi Alex,

      Absolutely this code works ( WP 4.9.4 ) the key element is the hook
      add_action( 'rest_api_init', 'register_posts_meta_field' );

      Alan

      • Thank you for your reply! i tried it again as you suggested and disabled any other parts of code which were hooked into init or rest_api_init hooks to avoid possible side effects. The meta field is still empty.

        Probably I should’ve mentioned, that I’m using a custom post type. As stated in the codex, when registering it I set ‘supports’ => [‘custom-fields’]. May be there is something else what I’m missing in the register_post_type() and that is crucial for the meta to show up? I update the StackOverflow with details to that part of code.

  1. Hey, does this code just shows the meta title and description or we can update it as well?

    • Thank you for your interest, indeed the first line says ‘This the second post post in my series about getting and updating WordPress data via a spreadsheet’

      You have to work through all 4 post til you get to updating solution.

      It has been several years since I visited the code so there may be tweaks that can be improved however the principals remain solid.

Leave a Reply to Alan Cancel reply

Your email address will not be published.