diff --git src/wp-includes/rest-api.php src/wp-includes/rest-api.php
index 82d856a063..ac04a17e0d 100644
--- src/wp-includes/rest-api.php
+++ src/wp-includes/rest-api.php
@@ -233,6 +233,10 @@ function create_initial_rest_routes() {
 	// Settings.
 	$controller = new WP_REST_Settings_Controller;
 	$controller->register_routes();
+
+	// Menu location.
+	$controller = new WP_REST_Menu_Locations_Controller;
+	$controller->register_routes();
 }
 
 /**
diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-menu-locations-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-menu-locations-controller.php
new file mode 100644
index 0000000000..871fde0782
--- /dev/null
+++ src/wp-includes/rest-api/endpoints/class-wp-rest-menu-locations-controller.php
@@ -0,0 +1,269 @@
+<?php
+/**
+ * REST API: WP_REST_Menu_Locations_Controller class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 4.x.x
+ */
+
+/**
+ * Core class used to access menu locations via the REST API.
+ *
+ * @since 4.x.x
+ *
+ * @see WP_REST_Controller
+ */
+class WP_REST_Menu_Locations_Controller extends WP_REST_Controller {
+
+	/**
+	 * Constructor.
+	 *
+	 * @since 4.x.x
+	 */
+	public function __construct() {
+		$this->namespace = 'wp/v2';
+		$this->rest_base = 'menu-locations';
+	}
+
+	/**
+	 * Registers the routes for the objects of the controller.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @see register_rest_route()
+	 */
+	public function register_routes() {
+
+		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
+			array(
+				'methods'             => WP_REST_Server::READABLE,
+				'callback'            => array( $this, 'get_items' ),
+				'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				'args'                => $this->get_collection_params(),
+			),
+			'schema' => array( $this, 'get_public_item_schema' ),
+		) );
+
+		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<location>[\w-]+)', array(
+			'args' => array(
+				'location' => array(
+					'description' => __( 'An alphanumeric identifier for the menu location.' ),
+					'type'        => 'string',
+				),
+			),
+			array(
+				'methods'             => WP_REST_Server::READABLE,
+				'callback'            => array( $this, 'get_item' ),
+				'permission_callback' => array( $this, 'get_item_permissions_check' ),
+				'args'                => array(
+					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
+				),
+			),
+			'schema' => array( $this, 'get_public_item_schema' ),
+		) );
+	}
+
+	/**
+	 * Checks whether a given request has permission to read menu locations.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
+	 */
+	public function get_items_permissions_check( $request ) {
+		if ( 'edit' === $request['context'] ) {
+			$types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
+
+			foreach ( $types as $type ) {
+				if ( current_user_can( $type->cap->edit_posts ) ) {
+					return true;
+				}
+			}
+			return new WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to manage menu locations.' ), array( 'status' => rest_authorization_required_code() ) );
+		}
+
+		return true;
+	}
+
+	/**
+	 * Retrieves all menu locations, depending on user context.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
+	 */
+	public function get_items( $request ) {
+		$data = array();
+
+		foreach ( get_registered_nav_menus() as $name => $description ) {
+			$location = new stdClass();
+			$location->name = $name;
+			$location->description = $description;
+
+			$location = $this->prepare_item_for_response( $location, $request );
+			$data[ $name ] = $this->prepare_response_for_collection( $location );
+		}
+
+		return rest_ensure_response( $data );
+	}
+
+	/**
+	 * Checks if a given request has access to read a menu location.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
+	 */
+	public function get_item_permissions_check( $request ) {
+		if ( ! array_key_exists( $request['location'], get_registered_nav_menus() ) ) {
+			return new WP_Error( 'rest_menu_location_invalid', __( 'Invalid menu location.' ), array( 'status' => 404 ) );
+		}
+
+		return true;
+	}
+
+	/**
+	 * Retrieves a specific menu location.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
+	 */
+	public function get_item( $request ) {
+		$registered_menus = get_registered_nav_menus();
+		if ( ! array_key_exists( $request['location'], $registered_menus ) ) {
+			return new WP_Error( 'rest_menu_location_invalid', __( 'Invalid menu location.' ), array( 'status' => 404 ) );
+		}
+
+		$location = new stdClass();
+		$location->name = $request['location'];
+		$location->description = $registered_menus[ $location->name ];
+
+		$data = $this->prepare_item_for_response( $location, $request );
+
+		return rest_ensure_response( $data );
+	}
+
+	/**
+	 * Prepares a menu location object for serialization.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @param stdClass        $location  Post status data.
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response Post status data.
+	 */
+	public function prepare_item_for_response( $location, $request ) {
+
+		$data = array(
+			'name'         => $location->name,
+			'description'  => $location->description,
+		);
+
+		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
+		$data = $this->add_additional_fields_to_object( $data, $request );
+		$data = $this->filter_response_by_context( $data, $context );
+
+		$response = rest_ensure_response( $data );
+
+		$response->add_links( $this->prepare_links( $location ) );
+
+		/**
+		 * Filters a menu location returned from the REST API.
+		 *
+		 * Allows modification of the menu location data right before it is
+		 * returned.
+		 *
+		 * @since 4.x.x
+		 *
+		 * @param WP_REST_Response $response The response object.
+		 * @param object           $location The original status object.
+		 * @param WP_REST_Request  $request  Request used to generate the response.
+		 */
+		return apply_filters( 'rest_prepare_menu_location', $response, $location, $request );
+	}
+
+	/**
+	 * Retrieves the menu location's schema, conforming to JSON Schema.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @return array Item schema data.
+	 */
+	public function get_item_schema() {
+		$schema = array(
+			'$schema'              => 'http://json-schema.org/draft-04/schema#',
+			'title'                => 'menu-location',
+			'type'                 => 'object',
+			'properties'           => array(
+				'name'             => array(
+					'description'  => __( 'The name of the menu location.' ),
+					'type'         => 'string',
+					'context'      => array( 'embed', 'view', 'edit' ),
+					'readonly'     => true,
+				),
+				'description'      => array(
+					'description'  => __( 'The description of the menu location.' ),
+					'type'         => 'string',
+					'context'      => array( 'embed', 'view', 'edit' ),
+					'readonly'     => true,
+				),
+			),
+		);
+
+		return $this->add_additional_fields_schema( $schema );
+	}
+
+	/**
+	 * Retrieves the query params for collections.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @return array Collection parameters.
+	 */
+	public function get_collection_params() {
+		return array(
+			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
+		);
+	}
+
+	/**
+	 * Prepares links for the request.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @param stdClass $location Menu location.
+	 * @return array Links for the given menu location.
+	 */
+	protected function prepare_links( $location ) {
+		$base = sprintf( '%s/%s', $this->namespace, $this->rest_base );
+
+		// Entity meta.
+		$links = array(
+			'self' => array(
+				'href'   => rest_url( trailingslashit( $base ) . $location->name ),
+			),
+			'collection' => array(
+				'href'   => rest_url( $base ),
+			),
+		);
+
+		$menus = get_nav_menu_locations();
+		if ( array_key_exists( $location->name, $menus ) ) {
+			$id = $menus[ $location->name ];
+			$items_url = rest_url( "wp/v2/menus/${id}" );
+
+			$links['menus'] = array(
+				'href'       => $items_url,
+				'embeddable' => true,
+			);
+		}
+
+		return $links;
+	}
+}
diff --git src/wp-includes/rest-api/endpoints/class-wp-rest-menus-controller.php src/wp-includes/rest-api/endpoints/class-wp-rest-menus-controller.php
new file mode 100644
index 0000000000..076fa97a4c
--- /dev/null
+++ src/wp-includes/rest-api/endpoints/class-wp-rest-menus-controller.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * REST API: WP_REST_Menus_Controller class
+ *
+ * @package WordPress
+ * @subpackage REST_API
+ * @since 4.x.x
+ */
+
+/**
+ * Core class used to access menus via the REST API.
+ *
+ * @since 4.x.x
+ *
+ * @see WP_REST_Controller
+ */
+class WP_REST_Menus_Controller extends WP_REST_Terms_Controller {
+
+	/**
+	 * Prepares links for the request.
+	 *
+	 * @since 4.x.x
+	 *
+	 * @param object $term Term object.
+	 * @return array Links for the given term.
+	 */
+	protected function prepare_links( $term ) {
+		$links = parent::prepare_links( $term );
+
+		// Let's make sure that menu items are embeddable for a menu collection.
+		if ( array_key_exists( 'https://api.w.org/post_type', $links ) ) {
+			$post_type_links = $links['https://api.w.org/post_type'];
+
+			foreach ( $post_type_links as $index => $post_type_link ) {
+				if ( ! array_key_exists( 'href', $post_type_link ) || strpos( $post_type_link['href'], '/menu-items?' ) === false ) {
+					continue;
+				}
+
+				$post_type_links[ $index ]['embeddable'] = true;
+			}
+
+			$links['https://api.w.org/post_type'] = $post_type_links;
+		}
+
+		return $links;
+	}
+}
diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
index 92a4ee73d2..660795dda7 100644
--- src/wp-includes/taxonomy.php
+++ src/wp-includes/taxonomy.php
@@ -103,6 +103,9 @@ function create_initial_taxonomies() {
 		'show_ui' => false,
 		'_builtin' => true,
 		'show_in_nav_menus' => false,
+		'show_in_rest' => true,
+		'rest_base' => 'menus',
+		'rest_controller_class' => 'WP_REST_Menus_Controller',
 	) );
 
 	register_taxonomy( 'link_category', 'link', array(
diff --git src/wp-settings.php src/wp-settings.php
index bacf4cfddd..9557cb4041 100644
--- src/wp-settings.php
+++ src/wp-settings.php
@@ -234,6 +234,8 @@ require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-terms-controller.p
 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-users-controller.php' );
 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-comments-controller.php' );
 require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-settings-controller.php' );
+require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-menus-controller.php' );
+require( ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-menu-locations-controller.php' );
 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php' );
 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php' );
 require( ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php' );
