If you worked with the first version of the WP-API, you know that getting custom post types was really easy.

To get all posts, I could send a GET request to http://scottbolinger.com/wp-json/posts. To get a specific type, I could send my request to http://scottbolinger.com/wp-json/posts?type=event.

There have been some pretty big changes in version 2, which is the version that will (hopefully) be in WordPress core sometime soon.

Custom post types are now hidden from the API by default. I can see why they did this, many themes and plugins added post types that they never intended to be public. Making those public would cause more problems than it would solve.

However, that now means we can’t just add the ‘type’ flag to our request, and expect to get post types back.

How to get custom post types in the WP-API v2

Any custom post type that you want to be available to the WP-API needs to have the show_in_rest = true argument. You can do this in your registration function, or after the post is already registered.

Find available post types

The WP-API lists all available post types at mysite.com/wp-json/wp/v2/types

The Register Function

Using the ‘book’ post type example from the codex, we just add the show_in_rest argument.

add_action( 'init', 'codex_book_init' );
 * Register a book post type.
 * @link http://codex.wordpress.org/Function_Reference/register_post_type
function codex_book_init() {
	$labels = array(
		'name'               => _x( 'Books', 'post type general name', 'your-plugin-textdomain' ),
		'singular_name'      => _x( 'Book', 'post type singular name', 'your-plugin-textdomain' ),
		'menu_name'          => _x( 'Books', 'admin menu', 'your-plugin-textdomain' ),
		'name_admin_bar'     => _x( 'Book', 'add new on admin bar', 'your-plugin-textdomain' ),
		'add_new'            => _x( 'Add New', 'book', 'your-plugin-textdomain' ),
		'add_new_item'       => __( 'Add New Book', 'your-plugin-textdomain' ),
		'new_item'           => __( 'New Book', 'your-plugin-textdomain' ),
		'edit_item'          => __( 'Edit Book', 'your-plugin-textdomain' ),
		'view_item'          => __( 'View Book', 'your-plugin-textdomain' ),
		'all_items'          => __( 'All Books', 'your-plugin-textdomain' ),
		'search_items'       => __( 'Search Books', 'your-plugin-textdomain' ),
		'parent_item_colon'  => __( 'Parent Books:', 'your-plugin-textdomain' ),
		'not_found'          => __( 'No books found.', 'your-plugin-textdomain' ),
		'not_found_in_trash' => __( 'No books found in Trash.', 'your-plugin-textdomain' )

	$args = array(
		'labels'             => $labels,
                'description'        => __( 'Description.', 'your-plugin-textdomain' ),
		'public'             => true,
		'publicly_queryable' => true,
		'show_ui'            => true,
		'show_in_menu'       => true,
		'show_in_rest'       => true,
		'query_var'          => true,
		'rewrite'            => array( 'slug' => 'book' ),
		'capability_type'    => 'post',
		'has_archive'        => true,
		'hierarchical'       => false,
		'menu_position'      => null,
		'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )

	register_post_type( 'book', $args );

Method 2: Add support after the post is already registered

If you are exposing a CPT that is not in your own plugin, or you need to do it in a different place, you can use this function courtesy of Rachel Baker.

function wpsd_add_book_args() {
    global $wp_post_types;

    $wp_post_types['book']->show_in_rest = true;
    $wp_post_types['book']->rest_base = 'book';
    $wp_post_types['book']->rest_controller_class = 'WP_REST_Posts_Controller';
add_action( 'init', 'wpsd_add_book_args', 30 );

Here’s how to do that if you have a lot of CPTs, and you only want specific ones exposed in the API.

Note that you’ll want to use register_post_type_args as soon as it’s available. Here’s what that will look like:

function sb_add_cpts_to_api( $args, $post_type ) {
	if ( 'movie' === $post_type ) {
		$args['show_in_rest'] = true;
		$args['rest_base'] = 'movie';
		// $args['rest_controller_class'] = 'WP_REST_Posts_Controller';
	return $args;
add_filter( 'register_post_type_args', 'sb_add_cpts_to_api', 10, 2 );

At the time of this writing, register_post_type_args is not available.

Getting Your Posts

If you use either of those methods, your CPT will be available at your site like this: http://scottbolinger.com/wp-json/wp/v2/book

Note that v2 of the API has new routes, you won’t be able to get your CPT using the old routes.

This information may change, please consult their documentation for the latest usage.

16 thoughts on “Working with Custom Post Types in WP-API v2

  1. Scott, have you tried adding this support and creating a new post with the custom post type? I’ve been trying this and can get it to work fine when posting to /posts. It creates the post without a problem. But then I added support for a ‘course’ custom post type but the post is added without accepting the title, excerpt, or content that I am sending. Nothing is different between the two javascript calls except the /course vs. /posts endpoint.

    Do you think that *should* be working? Or do you think I need to write my own custom route to handle that?

  2. I don’t follow why I have to explicitly allow access to post types if my client is authenticated at the same level as a WordPress administrator.

    A pain!

  3. hi, i am completely new to wordpress in the way of rummaging in the php.files. so with that said where to i find the register_post_type in which file?

  4. Something odd with mine. A bunch of my custom post types working properly, except for 1. Continues to 404 with no rest route, despite having the same code as the rest.

    Any idea what would cause that?

  5. Thank you for this. I really battled to add existing post types. Lots of hours of frustrations sorted out in two min. If only I found your post earlier! :):)

Leave a Reply

Your email address will not be published. Required fields are marked *