REST API

Silos Endpoint

REST API documentation for our Silos system.

Personalized and Customizable Carousels of Data.

Copy
http
https://api.mediahound.com/1.3/graph/silo?params=PARAMS_JSON

Overview

Imagine you want to present your Users with carousels of content, each highlighting different facets of personalized and recommended content.

Object Types

As illustrated, the User is shown 3 rows of suggestions. Each row is one Silo:

  • The first displays Movie and ShowSeries recommendations based on the User's Likes.
  • The second makes personalized Movie suggestions that are currently available to watch on a few Sources.
  • The third displays Movies with a featured Graph Genre derived from their Likes.

Silos

Silos empower you to make an endless stream of carousels that engage your Users with highly specific and targeted content. A Silos Response looks like a two dimensional array of objects, where each of the inner arrays represents one cohesive set of Content (i.e., Horror Comedies on Netflix Instant).

Driving them, however, is a bit more complicated. In effect, Silos allow you to combine multiple requests into a single large request, but have an extra feature that also allows the shared requests to work in coordination, otherwise known as a Shared Silo Pattern.

Silos have a learning curve. Before you dive into how to create Silos, you must be comfortable working with Factors, Filters, and Components from our Hound Query Language. Silos build heavily on HQL and internally utilize the underlying services of Relate, Explore and Lookup.

The Silos Language

Silos are constructed as JSON and passed into the silos endpoint as a URL-encoded query parameter called params. The Silos Language inherits some things from Factors, Filters, and Components, so go read-up if you're unfamiliar.

Here is an example of the JSON that generated the Silos in the image at the top of this page:

Copy
JSON
{
  "patterns": [{
      "parentGenerator": "UsersLikes",
      "generators": [{
        "factors": [
          "${0}"
        ],
        "filters": {
          "returnType": {
            "$in": [
              "Movie",
              "ShowSeries"
            ]
          }
        },
        "text": {
          "formattedName": "Because you watched ${0.name}",
          "hintText": "More like ${0.name}"
        }
      }]
    },
    {
      "generators": [{
        "text": {
          "formattedName": "Movie Suggestions For You On Netflix Instant, Hulu...",
          "hintText": "We thought you would like these"
        },
        "factors": [
          "mhals-current-user"
        ],
        "filters": {
          "sources": {
            "$in": [
              "mhsrcwRw33Yek3hvzTpbqzT7hL9EOAeyec7Y61npmukn",
              "mhsrczhUrCLrA4exlm5zdaH9mwEcHbLypC4489PjuylG",
              "mhsrcRcTZuZn9N3OXfGnEDR8TkfzXG9BT6OIUAqQAXqg"
            ]
          },
          "returnType": {
            "$in": [
              "Movie"
            ]
          }
        }
      }]
    },
    {
      "parentGenerator": "UsersLikes",
      "generators": [{
          "filters": {
            "traitsOf": {
              "$eq": "${0}"
            },
            "returnType": {
              "$eq": "GraphGenreTrait"
            }
          },
          "sort": [{
            "type": "random"
          }],
          "iterations": 1
        },
        {
          "factors": [
            "${1}"
          ],
          "filters": {
            "traits": {
              "$eq": "${1}"
            },
            "returnType": {
              "$in": [
                "Movie"
              ]
            }
          },
          "text": {
            "formattedName": "More Movies with ${1.name}",
            "hintText": "Movies because you watched ${0.name}"
          }
        }
      ]
    }
  ],
  "sharedPatterns": [{
    "ref": "UsersLikes",
    "generators": [{
      "filters": {
        "likers": {
          "$eq": "mhals-current-user"
        }
      }
    }]
  }],
  "global": {
    "ensureDiverseResults": false,
    "components": [
      "metadata",
      "primaryImage",
      "secondaryImage"
    ],
    "filters": {}
  },
  "silosPerPage": 5,
  "itemsPerSilo": 10
}

In brief, this request has three Silo Patterns.

  • The first takes the User's most recent Like and gets Movie and ShowSeries recommendations based on it.
  • The second gets personalized Movie recommendations for the User and filters them to results available on any of three different sources (Netflix, Hulu, and iTunes).
  • The third takes the User's next most recent Like, and gets Graph Genres connected to that Like, and then gets Movie recommendations based on that Graph Genre.

Silo Repetition: In the example, we only define 3 Silo patterns. However, this Silos request can continue to generate potentially hundreds of unique Silos because the patterns support iteration! In brief, consider the first Silo in the example. As we encounter that Silo a second time, the User's next most recent Like will be used to generate a new set of Movie and ShowSeries recommendations. That process can continue until we run out of data to feed into the Patterns (In this case, when there are no more User Likes to work with).

As you can see, there is a lot going on here, but we'll break it down for you in the next sections.

Silo Rules Query

The Silo Rules query is the outermost object in the constructed JSON. Below is an abbreviated example of the layout:

Copy
JSON
{
  "global": GLOBAL_RULES_OBJECT,
  "patterns": SILO_PATTERNS_ARRAY,
  "sharedPatterns": SHARED_SILO_PATTERNS_ARRAY,
  "silosPerPage": Integer,
  "itemsPerSilo": Integer
}

Silo Rules Fields

Field NameDescription
globalRequired Object. Please see Global Rules.
patternsRequired Array. Please see Silo Patterns.
sharedPatternsOptional Array. Please see Shared Silo Patterns.
silosPerPageOptional Integer representing how many Silos should be returned per page. Default is 6.
itemsPerSiloOptional Integer representing how many elements should be returned in each Silo. Default is 10.

Global Rules

The Global Rules object contains rules which will be applied to the outputs of each Silo Pattern. Imagine you are making a request with 10 silos but you only want to show ShowSeries to the User. You could construct each Silo individually to filter to only ShowSeries, but that's unwieldy and error-prone. Instead, you can use a single global filter to restrict all results to ShowSeries.

Copy
JSON
{
  "filters": FILTERS_OBJECT,
  "components": COMPONENTS_ARRAY,
  "ensureDiverseResults": Boolean
}

Global Rules Fields

Field NameDescription
filtersApplies Filters to the output of each Silo. Please see.
componentsSelects what metadata is desired from the output of each Silo (primaryImage, etc.). Please see Components.
ensureDiverseResultsIf true, a piece of content will only appear at most once in all silos, even as you request subsequent Pages.

Silo Patterns

Silo patterns govern how each Silo is constructed. This means that for each unique Silo you intend to display, you must specify one Silo Pattern:

Copy
JSON
{
  "generators": SILO_GENERATOR_ARRAY,
  "removeIfEmpty": Boolean,
  "ref": String,
  "parentGenerator": String
}

Silo Pattern Fields

Field NameDescription
generatorsRequired Array of Silo Generators. Please see Silo Generator.
removeIfEmptyOptional Boolean. If true, a Silo that generates zero results will be completely removed from the response. This is generally the desired behavior. However, it may desirable to show a User an empty Silo in order to encourage further interaction. Consider an app that displays a Silo called "Your Watchlist". With removeIfEmpty set to false, this Silo will appear even if the User has nothing in their Watchlist, thus providing an opportunity to prompt the User to add things to their Watchlist.
refOptional String. If this Pattern is to be used as a Shared Silo Pattern then it must have a ref name to be referenced by the child Patterns.
parentGeneratorOptional String. If this Pattern needs to follow a parent Shared Silo Pattern, then it should refer to the parent's ref in this field.

Silo Generator

The Silo Generator is the real heart of the Silo Request. Multiple generators can be chained together to form one data generator, the output of which will be fed through any filters and components you specified in the Global Rules query:

Copy
JSON
{
  "factors": FACTORS_ARRAY,
  "filters": FILTERS_OBJECT,
  "sort": SORT_ARRAY,
  "text": SILO_TEXT_OBJECT,
  "iterations": Integer
}

Silo Generator Fields

Field NameDescription
factorsOptional Array of Factors, to generate content.
filtersOptional Filters object, to generate or filter content.
sortOptional Sort array, to alter the order of the returned content.
textOptional Silo Text Object describing what dynamic text to display for this Silo.
iterationsOptional Integer allowing for artificial restriction of an intermediate Generator's output (the generator will be restricted to this number of outputs per iteration). Default is 20.

Each Generator relies on Factors and/or Filters to generate some Content, either as input to the next Generator or to be run through any Global Rules and then returned as a Silo.

Chaining Silos

Inside of Silo Patterns, Generators are specified as an array of objects. This is because when multiple Generators are listed in a Pattern, they are considered to be chained together and they form a data pipeline. The output of each Generator is fed as input to the next using the Dynamic Reference syntax. For instance, you could begin with a User's Likes, which would feed into a factors request where we'd get recommendations based on one User Like. As you iterate through your Silos via paging, the next time you reencounter the same Silo Pattern, the next User Like will be fed into the same factors request, and a new set of recommendations will be generated. This means that one Silo pattern can generate a very large number of carousels! Here's an example:

Copy
JSON
{
  "patterns": [{
    "generators": [{
      "filters": {
        "likers": {
          "$eq": "mhals-current-user"
        }
      }
    }, {
      "generators": [{
        "factors": [
          "${0}"
        ],
        "filters": {
          "returnType": {
            "$in": [
              "Movie",
              "ShowSeries"
            ]
          }
        },
        "text": {
          "formattedName": "Because you watched ${0.name}",
          "hintText": "More like ${0.name}"
        }
      }]
    }]
  }],
  "global": {
    "ensureDiverseResults": false,
    "components": [
      "metadata",
      "primaryImage",
      "secondaryImage"
    ],
    "filters": {}
  },
  "silosPerPage": 5,
  "itemsPerSilo": 10
}

Generator Limit: You are able to chain together at most 3 generators. If you use more than 3, your request will be rejected.

Dynamic References

In the above generator example, we used ${0} and ${0.name} to reference values from previous Generators. The number is an index position into the array of Generators that make up your pattern. In this case, ${0} and ${0.name} are referring to the object and object name of the output of the zeroth Generator, which was the User Likes filter.

Shared Silo Patterns

Shared Silo Patterns allow for multiple Silo Patterns to share a common parent Pattern. This is useful in cases when you want to drive multiple Silos from a User's Likes, but you don't want each Silo to see the same Liked Content (because that tends to make all your Silos look the same). Using a Shared Silo Pattern that draws from the User's Likes allows multiple Patterns to draw one Like at a time - in order - thereby preventing repeat Liked Content from being used. Below is a snippet extracted from our original User example, where we take the User's Likes and share them between two different Silo Patterns:

Copy
JSON
{
  "patterns": [{
      "parentGenerator": "UsersLikes",
      "generators": [{
        "factors": [
          "${0}"
        ],
        "filters": {
          "returnType": {
            "$in": [
              "Movie",
              "ShowSeries"
            ]
          }
        },
        "text": {
          "formattedName": "Because you watched ${0.name}",
          "hintText": "More like ${0.name}"
        }
      }]
    },
    {
      "parentGenerator": "UsersLikes",
      "generators": [{
          "filters": {
            "traitsOf": {
              "$eq": "${0}"
            },
            "returnType": {
              "$eq": "GraphGenreTrait"
            }
          },
          "sort": [{
            "type": "random"
          }],
          "iterations": 1
        },
        {
          "factors": [
            "${1}"
          ],
          "filters": {
            "traits": {
              "$eq": "${1}"
            },
            "returnType": {
              "$in": [
                "Movie"
              ]
            }
          },
          "text": {
            "formattedName": "More Movies with ${1.name}",
            "hintText": "Movies because you watched ${0.name}"
          }
        }
      ]
    }
  ],
  "sharedPatterns": [{
    "ref": "UsersLikes",
    "generators": [{
      "filters": {
        "likers": {
          "$eq": "mhals-current-user"
        }
      }
    }]
  }],
  "global": {
    "ensureDiverseResults": false,
    "components": [
      "metadata",
      "primaryImage",
      "secondaryImage"
    ],
    "filters": {}
  },
  "silosPerPage": 5,
  "itemsPerSilo": 10
}

As you can see, the first and second Silo Patterns both have a parentGenerator of UserLikes and the sharedPatterns array has one Pattern with a ref called UserLikes. As these Patterns are used to generate data, the UserLikes Pattern generates data which will be shared in order. The first Silo Pattern will consume one Like, and then the second Pattern will consume the next Like, and so on and so forth, ensuring that no Like is consumed/used twice.

Silo Text Object

Copy
JSON
{
  "formattedName": String,
  "hintText": String
}

Silo Text Fields

Field NameDescription
formattedNameOptional String. The displayable name of this silo. You can use the ${} Dynamic References to customize the name based on the generator data.
hintTextOptional String. Additional text about the silo. This can be used as secondary informational text about the silo. You can use the ${} Dynamic References to customize the text based on the generator data.

Silos Response

Silos responses tend to be large due to the fact that you're asking several questions at once, so in the example below we've abbreviated some objects, and shortened some arrays to make the response a little more readable. Below is the truncated response from our original User example, which is the data behind the image at the top of the page:

Copy
JSON
{
  "content": [{
    "content": [{
      "object": {
        "mhid": "mhmovohczuhpd5l6nXASeG7MzD8Pl6GUMYgZP1TplHTz",
        "name": "GoodFellas",
        "altId": "mhmov-goodfellas",
        "releaseDate": 631152000,
        "description": "Based on the real-life accounts of mob associate Henry Hill, the story narrates Henry’s lifelong ambition to become part of the mafia since he was a child. He grows up to be part of the gangster scene and his crime-filled career spans multiple decades, until he encounters conflicts with rival associates that lead him to turn into a state witness against the mafia.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }, {
      "object": {
        "mhid": "mhmovOvglXW5IpStu6tRVp0Peg3z0kDTmU4lG1B9Tzws",
        "name": "Raging Bull",
        "altId": "mhmov-raging-bull",
        "releaseDate": 315532800,
        "description": "Robert De Niro plays an Italian-American boxer name Jake LaMotta, a man whose violent temperament helps him reach the top of the boxing world while, at the same time, destroys his personal life outside the ring.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }, {
      "object": {
        "mhid": "mhmovE3H9DwYZNQwec0cK42zjVkLn8Xmsuc1AVnFkYM1",
        "name": "American Gangster",
        "altId": "mhmov-american-gangster",
        "releaseDate": 1167609600,
        "description": "Denzel Washington stars as a small time dealer that rises to power to become a heroin kingpin in 1970’s New York. Russell Crowe plays the gritty detective who will stop at nothing to put him behind bars.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }],
    "pagingInfo": {
      "next": null,
      "previous": null
    },
    "formattedName": "Because you watched The Godfather: Part II",
    "hintText": "More like The Godfather: Part II"
  }, {
    "content": [{
      "object": {
        "mhid": "mhmovZJGhA3L4G8gKcnr3olKhziDPaMvOuCy3qI0pz8c",
        "name": "The Fugitive",
        "altId": "mhmov-the-fugitive",
        "releaseDate": 725846400,
        "description": "A man wrongfully accused of murdering his wife is goes on the run and tries to clear his name.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }, {
      "object": {
        "mhid": "mhmovOjxSFsYgjcvugVU3tBzSolEbnrij5dkqyc1xh6S",
        "name": "The Dark Knight",
        "altId": "mhmov-the-dark-knight",
        "releaseDate": 1199145600,
        "description": "In the second installment of Christopher Nolan’s Batman trilogy, a brutal and intelligent villain appears in Gotham, leaving chaos and violence in his wake. Batman is forced to toil with his own ideals as he battles his most iconic rival.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }, {
      "object": {
        "mhid": "mhmovRAPQCwbm0BUajSPhzWlij1u4QyjF3ZeXqJ943M1",
        "name": "The Untouchables",
        "altId": "mhmov-the-untouchables",
        "releaseDate": 536457600,
        "description": "Real life lawman Eliot Ness is looking to put crime boss Al Capone behind bars. In order to do so, he assembles an all star crew of veteran cops, smart bookies, and sharpshooting rookies who share his untainted view on justice.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }],
    "pagingInfo": {
      "next": null,
      "previous": null
    },
    "formattedName": "Movie Suggestions For You On Netflix Instant, Hulu...",
    "hintText": "We thought you would like these"
  }, {
    "content": [{
      "object": {
        "mhid": "mhmovMbgw1yq4OMyk7ybJQHpLCkq0hCwfBwyoI3S7vxm",
        "name": "Fight Club",
        "altId": "mhmov-fight-club",
        "releaseDate": 915148800,
        "description": "A man and his newfound friend start a club with a group of men dedicated to mayhem and violence.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }, {
      "object": {
        "mhid": "mhmov6aoqHPGTDrT0UO2cJWPtYLdPj5JtpDVFECekINL",
        "name": "The Matrix",
        "altId": "mhmov-the-matrix",
        "releaseDate": 915148800,
        "description": "This trailblazing Science Fiction film stars Keanu Reeves as Neo, a seemingly ordinary computer programmer sought out by a group of rebels who believe the human race has been enslaved by machines.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }, {
      "object": {
        "mhid": "mhmov696bL3TZaOmDXVXoH5np0cNBRUfW3ABKooMJGno",
        "name": "Blade Runner",
        "altId": "mhmov-blade-runner",
        "releaseDate": 378691200,
        "description": "In a dark, futuristic Los Angeles, an ex-cop must track down four replicants, androids that appear human, and destroy them. Along the way he begins to reexamine his own ideas of what is human.",
        "primaryImage": PRIMARY_IMAGE_OBJECT
          },
          "context": {}
        },
        "secondaryImage": SECONDARY_IMAGE_OBJECT
          },
          "context": {}
        }
      },
      "context": {}
    }],
    "pagingInfo": {
      "next": null,
      "previous": null
    },
    "formattedName": "More Movies with Thought Provoking + Thriller",
    "hintText": "Movies because you watched Fight Club"
  }],
  "pagingInfo": {
    "next": "https://api.mediahound.com/1.3/graph/silo?useHimitsu=true&startId=aa2355c4-90fb-40f9-9910-b010ace70b6d",
    "previous": null
  }
}

As you can see, the outermost structure of the response is a content array, which is paired with a Paging Info object that allows you to page forward in your results. Within the outermost content array are three more content arrays, one per Silo generated. These are your carousels! In reality, we would receive more than three Silos in the response, but the results have been truncated for brevity.