Behavioral Pattern Discovery ☁️ API

Future of Intelligent Application Development


Unleash Automation

Super Fast


Quickly build a POC of any AI task in days with no strings attached enjoying our free tier plan. Once you are satisfied with the results and the code is ready, switch to production. It requires only changing a few lines of code. Pay per use model charged only on inference activity in production with detailed visibility into your bills.


Frictionless Development


Easy to use cloud API for quickly building prediction, classification, anomaly detection, and forecasting AI tasks. Programmable approach to defining and refining the pattern inside an AI task. Models are versioned for easy tracking. Plug your data sources, configure how the task will run and what will be done with the results, and you are ready to go.


Pattern Discovery


Programmable pattern mining technology capable of discovering meaningful patterns in labeled data and detecting them later on very efficiently during inference. The pattern description language describes patterns we want to find, which makes them customizable during development and production. Inference results are fully explainable.thanks to expressiveness of patterns.


Trustworthy AI


AI, as we grew to learn of, is based on black-box approaches wherein at the center of many critical decision-making processes lies a model which no one can understand taking decisions that are difficult to explain. We believe in transparency, human control, and visibility into AI models which will create a safer path to the future.

A Single API for Any Use Case



Optimize CI process, predict service degradation, detect deviations in infrastructure billing…



Smart cities, predictive maintenance, supply chain optimization, process optimization…


Marketing Automation

Segmentation, customer churn prediction, next best offer, customer journey optimization…


Customer Experience

Content and user experience personalization, smart A/B testing, real-time comm channel selection…



Employee performance optimization, intelligent debt collection, call center queue optimization…



Real-time credit scoring, robo-trading, fraud detection, loan recommendation, anti money laundering…


Pattern Detection Demo

Find the NBA Superstars

Our NBA dataset seasonal player stats has the player’s name, position, age, team, player, PER, and BPM.

The pattern description of what makes a player a superstar is:

  • Evaluate every three consecutive seasons
  • In the top five in the team and played the most minutes (MP)
  • Started at above 80% of the games he played (GS/G)
  • Wins shares (WS) is above the median of all players in the league
  • Player Efficiency Rating (PER) in all three seasons is above 18 and in the last season is above 20
  • Contribution to the team (BPM) increases over the seasons and is always one point above the league average
  • Had the highest average value over all three seasons, compared to all other players
Javascript API Source Code
const bcToken = "my_braincast_token";
const bcSecretKey = "my_secret_api_key";
const bc = BC.createClient(bcToken, bcSecretKey);
// assume the following four variables represent values specified by the user of your application:
const minYearFromUser = 1990;
const maxYearFromUser = 2019;
const positionFromUser = "center";
const teamFromUser = "Lakers";

const BasketballReferenceOpenAPITemplate = {
	openAPISpec: Examples.OpenAPI.JSON.NBA, // the Open API Specification (OAS) of a REST data source for accessing the churn user data
	path:"/query",  // the HTTP endpoint to use from the OAS spec
	operation:"get",  // the HTTP method to use
	params:{     // required values for parameters specified in the OAS
		"apikey":     "AKIAIOSFODNNSLOE:Yxg83MZaEgh3OZ3l0rLo5RTX11o="

// define and validate data sources
let stream = bc.Datastore.getStream("NBA.Example.Seasons");
if (!stream) {
	stream = bc.Datastore.defineStream(
			"source": "NBA.Example.Seasons",//source name
			"adapter": BC.Utils.SourceAdapters.createREST(BasketballReferenceOpenAPITemplate),// REST adapter to fetch data created from the data source's Open API Specification
			"record": { // provide an object to specify which attributes of the records being fetched from the source should be used (if omitted, all attributes will be used) - each property is an attribute and the value is that attribute's description:
				"Year":		     	"the season",
				"Player":	     	"player's full name",
				"Pos":		     	"player's position",
				"Tm":		     	"team's three letter name",
				"G":		     	"the number of games the player participated in",
				"GS":		     	"the number of games the player started in",
				"WS":		     	"the player's Wins Shares score",
				"MP":		     	"the amount of minutes played by the player",
				"PER":		     	"the player's Player Efficiency Rating score",
				"BPM":		     	"the player's Box +/- score",
				"rankTeamMP":    	"the player's minutes played (MP) ranking compared to other players in his team and for same year",
				"percentileYearWS": "the player's wins share (WS) percentile compared to other players in the league for the same year"
			"filter": {"where":"Year >= 1985"} // the filter for this source specifying that only the data after 1985 should be consumed
	if (!stream || !stream.validate()) {
		throw "Example stream cannot be accessed, reason: " + stream.validationErrorMsg;

// build scanner
if (!stream.getScanner("myBestNBAPlayerTheory")) { // the name of the scanner and the patterns inside the scanner are in the namespace of the stream
	let scanner = stream.createScanner(
		{// make user specified values available for the scanner as scanner variables that can be accessed via the vars keyword (don't forget to update their values every time you scan the stream, e.g. for different user queries)
			"minYearFromUser": undefined,
			"maxYearFromUser": undefined,
			"positionFromUser": undefined,
			"teamFromUser": undefined
	scanner.addConstraint("consecutiveSeasons", // constraint name
		function(curSequence) {
			for (let i = 1; i < curSequence.records.length; i++) {
				if (curSequence.records[i].Year - curSequence.records[i-1].Year !== 1) {
					return false;
			return true;
		"seasons are consecutive"// explanation
		function(curSequence) {
			return  curSequence.records.length === 3;
		"there are exactly 3 seasons"// explanation
		function(curSequence) {
			return  curSequence.records.every(record => record.Player === curSequence.records[0].Player);
		function(curSequence) {
			return  curSequence.records.every(record => record.Tm === curSequence.records[0].Tm);
		"the player played for the same team in every season"
		function(curSequence) {
			let lastSeason = curSequence.records[curSequence.records.length-1];
			return  lastSeason.rankTeamMP <= 5;
		"the player was ranked among the top 5 players in his team who played the most minutes (MP) during the last season"
		function(curSequence) {
			let lastSeason = curSequence.records[curSequence.records.length-1];
			return  lastSeason.GS / lastSeason.G > 0.8;
		"the player started in more than 80% of the games he played (GS/G) during the last season"
		function(curSequence) {
			return  curSequence.records[curSequence.records.length-1].percentileYearWS > 50;
		"the player's win shares (WS) score in the last season was above the median of all players in the league"
		function(curSequence) {
			return 	curSequence.records.every(record => record.PER > 18) ;
		"the player's efficiency rating (PER) was above 18 in every season"
		function(curSequence) {
			return 	curSequence.records[curSequence.records.length-1].PER > 20;
		"the player's efficiency rating (PER) was above 20 in the last season"
		function (curSequence) {
			return curSequence.records.every(record => record.BPM > 1);
		"the player's contribution to the team, measured by box +/- (BPM), was 1 point above the league-average (for which BPM = 0)"
		function (curSequence) {
			for (let i = 1; i < curSequence.records.length; i++) {
				if (curSequence.records[i].BPM < curSequence.records[i-1].BPM) {
					return false;
			return true;
		"the player's contribution to the team, measured by box +/- (BPM), didn't decrease from one season to the next"
	function bpmAvg(pattern) {
		return pattern.records.reduce(function (sum, record) {
			return sum + record["BPM"];
		}, 0) / (pattern.records.length || 1);
			bpmAvg, // evaluator function - the sequence for which this function's value is maximal will be selected out of all sequences satisfying all other constraints
			"having the greatest contribution to the team, measured by the average box +/- (BPM) over all seasons"
		function (curSequence) {
			let lastSeason = curSequence.records[curSequence.records.length-1];
			return 	((vars.minYearFromUser || lastSeason.Year >= vars.minYearFromUser) &&
					 (vars.maxYearFromUser || lastSeason.Year <= vars.maxYearFromUser) &&
					 (vars.positionFromUser || lastSeason.Position === vars.positionFromUser) &&
					 (vars.teamFromUser || lastSeason.Team === vars.teamFromUser));
	"satisfy the user's request parameters: "+toUserRequestString(vars.minYearFromUser,vars.maxYearFromUser,vars.positionFromUser,vars.teamFromUser)

	if (!scanner.validate()) {
		throw "Scanner not configured properly, reason: "+scanner.validationErrorMsg;
	}"myBestNBAPlayerTheory", true); // override if such exists

// Update the user specified values available to the scanner (in case the scanner was fetched from backend):
scanner.vars = {
	"minYearFromUser": minYearFromUser,
	"maxYearFromUser": maxYearFromUser,
	"positionFromUser": positionFromUser,
	"teamFromUser": teamFromUser

// run our scanner on the stream
result = stream.scan("myBestNBAPlayerTheory");
userRequirements = toUserRequestString(minYearFromUser,maxYearFromUser,positionFromUser,teamFromUser);
alert("The best NBA player matching your requirements ("+userRequirements+") is "+result+ ".");

// helper functions:
function toUserRequestString(minYearFromUser,maxYearFromUser,positionFromUser,teamFromUser) {
	return	minYearFromUser?`Year >= ${minYearFromUser} `:""+
	maxYearFromUser?`Year <= ${maxYearFromUser} `:""+
	positionFromUser?`Position = ${positionFromUser} `:""+
	teamFromUser?`Team = ${teamFromUser}`:"";