I need to create an endpoint that when called returns an XML with the products of the store. I have the XML generated, but I don't know how to make the endpoint return it in XML format, because now it returns it in JSON format, and being an XML, it does not return it well.

This is my code:


* Plugin Name: example
* Plugin URI: https://www.example.es/
* Description: example
* Version: 1.0
* Author: example
* Author URI: https://www.example.es/

function smg_feed() {
   $args = array( 'post_status' => 'publish', numberposts => -1 );
   $products = wc_get_products($args);

   $xml_header = '<?xml version="1.0" encoding="UTF-8"?><Document></Document>';
   $xml = new SimpleXMLElement($xml_header);

   foreach($products as $product) {
       $data = $product->get_data();
       $sku = $data['sku'];
       $categoriasNombres = array();
       $subcategoriasNombres = array();
       foreach( wp_get_post_terms( $data['id'], 'product_cat' ) as $term ){
           if( $term ){
               if ($term->name == 'XXXX' || $term->name == 'YYYY') {
                   array_push($categoriasNombres, $term->name);
               } else {
                array_push($subcategoriasNombres, $term->name);
       $categoriasNombres = implode(',', $categoriasNombres);
       $subcategoriasNombres = implode(',', $subcategoriasNombres);
       $propiedadesNombres = array();
       foreach( wp_get_post_terms( $data['id'], 'product_tag' ) as $term ){
           if( $term ){
               array_push($propiedadesNombres, $term->name);
       $propiedadesNombres = implode(',', $propiedadesNombres);
       $nombre = $data['name'];
       $formatosNombres = array();
       foreach( wp_get_post_terms( $data['id'], 'pa_formato' ) as $term ){
           if( $term ){
               array_push($formatosNombres, $term->name);
       $formatosNombres = implode(',', $formatosNombres);
       $metaData = $data['meta_data'];

       foreach($metaData as $item) {
           if ($item->key == '_role_based_price') {
               $obj = $item->value;
               $precio = $obj['api1']['regular_price'];
               $precioEspecial = $obj['api2']['regular_price'];

       $row = $xml->addChild('row');
       $row->addChild('sku', $sku);
       $row->addChild('categorias', $categoriasNombres);

       $row->addChild('subcategorias', $subcategoriasNombres);
       $row->addChild('propiedades', $propiedadesNombres);
       $row->addChild('nombre', $nombre);
       // $row->addChild('imagen', );

       $row->addChild('formatos', $formatosNombres);
       $row->addChild('precio', $data['regular_price']);
       $row->addChild('precio_especial', $precioEspecial);

   $output = $xml->asXML();

   return $output;

add_action('rest_api_init', function() {

   register_rest_route('smg/v1', 'feed', [
       'methods' => 'GET',
       'callback' => 'smg_feed',


But when I make the call to the endpoint what it returns is this:

"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<subcategorias>Nutridefense Plus<\/subcategorias>
<nombre>NutriDefense plus<\/nombre>

How can I make it return it in XML format? Thank you very much.


  • How can read this xml format from front end url. What is the url made for display this xml format data. Please let me know.
    – Heena
    Feb 18, 2021 at 8:07
  • If you're using the code in the question or Sally's answer then you can see it's registered with register_rest_route('smg/v1', 'feed', ... ); so this will be /wp-json/smg/v1/feed on your site.
    – Rup
    Feb 18, 2021 at 9:49
  • ok thanks. @Rup
    – Heena
    Feb 18, 2021 at 11:00
  • This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review
    – cjbj
    Feb 18, 2021 at 11:24

1 Answer 1


By default, the output returned by your endpoint/callback will always be sent as a JSON-encoded string (and the Content-Type header would also be/contain application/json), so you can't use that callback for sending an XML feed.

However, you can use the rest_pre_serve_request hook if you want your REST API route to serve a different type of content like the XML feed in your case.

So for example, you can do:

  1. Set your callback (smg_feed()) to return the XML data which will be echoed in the maybe_smg_feed() below: (note that the permission_callback should always be set, check the REST API handbook for more details)

    function smg_feed( $request ) {
        // ... your code ...
        return 'your XML data';
    add_action( 'rest_api_init', function () {
        register_rest_route( 'smg/v1', 'feed', [
            'methods'             => 'GET',
            'callback'            => 'smg_feed', // make sure it returns an XML string
            'permission_callback' => '__return_true',
  2. Then send the XML response like so:

    function maybe_smg_feed( $served, $result, $request, $server ) {
        // Bail if the route of the current REST API request is not our custom route.
        if ( '/smg/v1/feed' !== $request->get_route() ||
            // Also check that the callback is smg_feed().
            'smg_feed' !== $request->get_attributes()['callback'] ) {
            return $served;
        // Send headers.
        $server->send_header( 'Content-Type', 'text/xml' );
        // Echo the XML that's returned by smg_feed().
        echo $result->get_data();
        // And then exit.
    add_filter( 'rest_pre_serve_request', 'maybe_smg_feed', 10, 4 );

Alternatively (as I mentioned in the original answer), without using the REST API (which does work), you can use add_feed() to deliver the XML feed.

( PS: Thanks to @TimothyJacobs for his help/comment. :) )

  • 1
    Hello Sally, thank you very much, I will investigate ^^
    – pmj
    Nov 10, 2020 at 14:06
  • 1
    Please never directly exit from a REST API controller. If you want to send XML you can use the rest_pre_serve_request filter. Dec 3, 2020 at 5:36
  • Thanks a lot, @TimothyJacobs! I already knew that filter, but heck, I guess my brain was lacking some vitamins back then...😀
    – Sally CJ
    Dec 3, 2020 at 7:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.