How to get recommended users to follow

There are several different ways you can use Pipeless to get personalized recommendations for users to follow, depending on what user engagement and content data you choose to send to Pipeless.

The four approaches below are based on what data you have in Pipeless:

  1. Users: User following user data
  2. Users with Tags: User following user data + user interests/tags
  3. Posted Content by Users: User following user data + user generated content posts + user content engagement data
  4. Posted Content with Tags: User following user data + user generated content posts + user content engagement data + content tags

More data objects and relationships and the calls spent adding more new events will increase your costs (we charge by total items stored which is unique objects + unique relationships, and number of calls per month) but more data can provide superior recommendations. It's up to you how you want to implement this, we encourage you to test out whichever approaches appeal to you to compare and then move forward from there.

Before you get started

First, you'll need to have an app in Pipeless to get an App ID and your API token, so if you have not done so already, please create an account and add an app.

If you know you need to test with a larger dataset than the free plan allows, please contact us using the Intercom chat widget in the bottom right of your browser window, or email us at [email protected] and we can provide a temporary promotion to your app to help you test.

1. Recommended Users to Follow - Users

You can have a powerful recommendation system using only users with directional follow relationships. Optionally, you can include "dismissed" relationships if you want to let users dismiss user recommendations (or for users who have been unfollowed) and keep those dismissed users from being recommended again. For this data schema, you could use either the "Content Recommendations" or "Recommended Accounts" endpoints.

Data Structure

Data needed: User following user data

The data schema will look like this when mapped to a Pipeless graph database:

1408

Data schema of user following user data with optional "dismissed" relationships

Adding event data

To achieve this data structure, you'll want to follow this data format to add your events (object-relationship-object) to Pipeless so we can map it to our graph database. You can refer to our Uploading Data doc for more details on uploading, like batch uploading and our Google Sheets integration.

A note about the examples of user names and tags - these are represented as spelled out words for demonstration purposes - ideally all the object ID's sent to Pipeless are unique user ID's or tag ID's.

📘

Need help uploading your historical data?

If you're having trouble mapping your current data to the Pipeless event format, we can help out, just message us using the Intercom chat widget in the bottom right of your browser window, or email us at [email protected].

https://api.pipeless.io/v1/apps/app_id/events/batch

const data = JSON.stringify({
  "events": [
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "followed",
        "created_on": "2020-09-28T09:31:18"
      },
      "end_object": {
        "id": "kim",
        "type": "user"
      }
    },
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "dismissed",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "sarah",
        "type": "user"
      }
    }
  ],
  "synchronous": false
});

const xhr = new XMLHttpRequest();

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://api.pipeless.io/v1/apps/123/events/batch");
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://api.pipeless.io/v1/apps/123/events/batch"

payload = {
    "events": [
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "followed",
                "created_on": "2020-09-28T09:31:18"
            },
            "end_object": {
                "id": "kim",
                "type": "user"
            }
        },
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "dismissed",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "sarah",
                "type": "user"
            }
        }
    ],
    "synchronous": False
}
headers = {
    "accept": "application/json",
    "content-type": "application/json"
}

response = requests.request("POST", url, json=payload, headers=headers)

print(response.text)
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://api.pipeless.io/v1/apps/123/events/batch")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request.body = "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}"

response = http.request(request)
puts response.read_body
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.pipeless.io/v1/apps/123/events/batch",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}");
Request request = new Request.Builder()
  .url("https://api.pipeless.io/v1/apps/123/events/batch")
  .post(body)
  .addHeader("accept", "application/json")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
curl --request POST \
  --url https://api.pipeless.io/v1/apps/123/events/batch \
  --header 'accept: application/json' \
  --header 'content-type: application/json' \
  --data '{"events":[{"start_object":{"id":"john","type":"user"},"relationship":{"type":"followed","created_on":"2020-09-28T09:31:18"},"end_object":{"id":"kim","type":"user"}},{"start_object":{"id":"john","type":"user"},"relationship":{"type":"dismissed","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"sarah","type":"user"}}],"synchronous":false}'

Requesting Recommendations

The api request to get recommended users to follow for a user would be:

https://api.pipeless.io/v1/apps/app_id/algos/recommendations/users-to-follow

{
  "object": {
    "id": "kim",
    "type": "user"
  },
  "followed_relationship_type": "followed",
  "dismissed_relationship_type": "dismissed"
}

Alternatively, you could use the content recommendation endpoint where the api request to get recommended users to follow for a user would be:

https://api.pipeless.io/v1/apps/app_id/algos/recommendations/content

{
  "object": {
    "id": "kim",
    "type": "user"
  },
  "content_object_type": "user",
  "primary_positive_relationship_type": "followed",
  "dismissed_relationship_type": "dismissed"
}

API Reference Documentation

You can read the reference docs for these endpoints here:
Get Recommended Users to Follow (for user)
Get Recommended Content (for user)

2. Recommended Users to Follow - Users with Tags

You can add user interest tags to make recommendations more contextual. This would require that you have some interests collected from users when they create their profile, or you could use tags from content they have posted and associate those with the user. For this data schema, you need to use the "Content Recommendations" endpoint, and not "Recommended Accounts" as users will be treated as a content object with associated tags.

Data Structure

Data needed: User following user data + user interests/tags

The data schema will look like this when mapped to a Pipeless graph database:

1382

Data schema for users with interest tags

Adding event data

To achieve this data structure, you'll want to follow this data format to add your events (object-relationship-object) to Pipeless so we can map it to our graph database. You can refer to our Uploading Data doc for more details on uploading options.

https://api.pipeless.io/v1/apps/app_id/events/batch

const data = JSON.stringify({
  "events": [
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "followed",
        "created_on": "2020-09-28T09:31:18"
      },
      "end_object": {
        "id": "kim",
        "type": "user"
      }
    },
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "interestedIn",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "football",
        "type": "tag"
      }
    },
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "dismissed",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "sarah",
        "type": "user"
      }
    }
  ],
  "synchronous": false
});

const xhr = new XMLHttpRequest();

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://api.pipeless.io/v1/apps/123/events/batch");
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://api.pipeless.io/v1/apps/123/events/batch"

payload = {
    "events": [
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "followed",
                "created_on": "2020-09-28T09:31:18"
            },
            "end_object": {
                "id": "kim",
                "type": "user"
            }
        },
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "interestedIn",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "football",
                "type": "tag"
            }
        },
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "dismissed",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "sarah",
                "type": "user"
            }
        }
    ],
    "synchronous": False
}
headers = {
    "accept": "application/json",
    "content-type": "application/json"
}

response = requests.request("POST", url, json=payload, headers=headers)

print(response.text)
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://api.pipeless.io/v1/apps/123/events/batch")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request.body = "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"interestedIn\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"football\",\"type\":\"tag\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}"

response = http.request(request)
puts response.read_body
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.pipeless.io/v1/apps/123/events/batch",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"interestedIn\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"football\",\"type\":\"tag\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"interestedIn\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"football\",\"type\":\"tag\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}");
Request request = new Request.Builder()
  .url("https://api.pipeless.io/v1/apps/123/events/batch")
  .post(body)
  .addHeader("accept", "application/json")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
curl --request POST \
  --url https://api.pipeless.io/v1/apps/123/events/batch \
  --header 'accept: application/json' \
  --header 'content-type: application/json' \
  --data '{"events":[{"start_object":{"id":"john","type":"user"},"relationship":{"type":"followed","created_on":"2020-09-28T09:31:18"},"end_object":{"id":"kim","type":"user"}},{"start_object":{"id":"john","type":"user"},"relationship":{"type":"interestedIn","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"football","type":"tag"}},{"start_object":{"id":"john","type":"user"},"relationship":{"type":"dismissed","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"sarah","type":"user"}}],"synchronous":false}'

Requesting Recommendations

For this approach, you need to use the content recommendation endpoint where the api request to get recommended users to follow for a user would be:

https://api.pipeless.io/v1/apps/app_id/algos/recommendations/content

{
  "object": {
    "id": "kim",
    "type": "user"
  },
  "content_object_type": "user",
  "primary_positive_relationship_type": "followed",
  "content_tagged_relationship_type": "interestedIn",
  "content_tag_object_type": "tag",
  "dismissed_relationship_type": "dismissed"
}

This approach also lets you use our cold start tag parameter, so even if a user has never followed anyone and you still want personalized recommendations, you can pass in their own interest tags as the cold start tags. If the user "kim" had filled out in their profile their interest in "soccer", "basketball" and "baseball", then that request for recommended users who shared her interests would look like this:

{
  "object": {
    "id": "kim",
    "type": "user"
  },
  "content_object_type": "user",
  "primary_positive_relationship_type": "followed",
  "content_tagged_relationship_type": "interestedIn",
  "content_tag_object_type": "tag",
  "coldstart_tag_ids": ["soccer", "basketball", "baseball"],
  "dismissed_relationship_type": "dismissed"
}

API Reference Documentation

You can read the reference docs for this endpoint here:
Get Recommended Content (for user)

3. Recommended Users to Follow - Posted Content by Users

You can include posts and post user engagement data to increase relevance of user recommendations as the Recommended Accounts algorithm can factor in both following activity as well as user engagement with the content posted by users to suggest users that are likely to be followed and positively engage with those users' content. For this data schema, you need to use the "Recommended Accounts" endpoint.

Data Structure

Data needed: User following user data + user generated content posts + user content engagement data

The data schema will look like this when mapped to a Pipeless graph database:

1162

Data schema for posted content by users

Adding event data

To achieve this data structure, you'll want to follow this data format to add your events (object-relationship-object) to Pipeless so we can map it to our graph database. You can refer to our Uploading Data doc for more details on uploading options.

https://api.pipeless.io/v1/apps/app_id/events/batch

const data = JSON.stringify({
  "events": [
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "followed",
        "created_on": "2020-09-28T09:31:18"
      },
      "end_object": {
        "id": "kim",
        "type": "user"
      }
    },
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "posted",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "us open match up",
        "type": "post"
      }
    },
    {
      "start_object": {
        "id": "jack",
        "type": "user"
      },
      "relationship": {
        "type": "liked",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "us open match up",
        "type": "post"
      }
    },
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "dismissed",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "sarah",
        "type": "user"
      }
    }
  ],
  "synchronous": false
});

const xhr = new XMLHttpRequest();

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://api.pipeless.io/v1/apps/123/events/batch");
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://api.pipeless.io/v1/apps/123/events/batch"

payload = {
    "events": [
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "followed",
                "created_on": "2020-09-28T09:31:18"
            },
            "end_object": {
                "id": "kim",
                "type": "user"
            }
        },
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "posted",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "us open match up",
                "type": "post"
            }
        },
        {
            "start_object": {
                "id": "jack",
                "type":"user"
            },
            "relationship": {
                "type": "liked",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "us open match up",
                "type": "post"
            }
        },
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "dismissed",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "sarah",
                "type": "user"
            }
        }
    ],
    "synchronous": False
}
headers = {
    "accept": "application/json",
    "content-type": "application/json"
}

response = requests.request("POST", url, json=payload, headers=headers)

print(response.text)
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://api.pipeless.io/v1/apps/123/events/batch")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request.body = "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"posted\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"jack\",\"type\":\"user\"},\"relationship\":{\"type\":\"liked\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}"

response = http.request(request)
puts response.read_body
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.pipeless.io/v1/apps/123/events/batch",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"posted\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"jack\",\"type\":\"user\"},\"relationship\":{\"type\":\"liked\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"posted\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"jack\",\"type\":\"user\"},\"relationship\":{\"type\":\"liked\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}");
Request request = new Request.Builder()
  .url("https://api.pipeless.io/v1/apps/123/events/batch")
  .post(body)
  .addHeader("accept", "application/json")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
curl --request POST \
  --url https://api.pipeless.io/v1/apps/123/events/batch \
  --header 'accept: application/json' \
  --header 'content-type: application/json' \
  --data '{"events":[{"start_object":{"id":"john","type":"user"},"relationship":{"type":"followed","created_on":"2020-09-28T09:31:18"},"end_object":{"id":"kim","type":"user"}},{"start_object":{"id":"john","type":"user"},"relationship":{"type":"posted","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"us open match up","type":"post"}},{"start_object":{"id":"jack","type":"user"},"relationship":{"type":"liked","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"us open match up","type":"post"}},{"start_object":{"id":"john","type":"user"},"relationship":{"type":"dismissed","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"sarah","type":"user"}}],"synchronous":false}'

Requesting Recommendations

The api request to get recommended users to follow for a user would be:

https://api.pipeless.io/v1/apps/app_id/algos/recommendations/users-to-follow

{
  "object": {
    "id": "kim",
    "type": "user"
  },
  "followed_relationship_type": "followed",
  "content_created_relationship_type": "posted",
  "positive_relationship_type": "liked",
  "content_object_type": "post",
  "dismissed_relationship_type": "dismissed"
}

API Reference Documentation

You can read the reference docs for this endpoint here:
Get Recommended Users to Follow (for user)

4. Recommended Users to Follow - Posted Content with Tags

You can extend the #3 approach by including associated tags for posts, along with post user engagement data to increase the relevance of user recommendations. For this data schema, you need to use the "Recommended Accounts" endpoint.

Data Structure

Data needed: User following user data + user generated content posts + user content engagement data + content tags

The data schema will look like this when mapped to a Pipeless graph database:

1330

Data schema for posted content with tags

Adding event data

To achieve this data structure, you'll want to follow this data format to add your events (object-relationship-object) to Pipeless so we can map it to our graph database. You can refer to our Uploading Data doc for more details on uploading options.

https://api.pipeless.io/v1/apps/app_id/events/batch

const data = JSON.stringify({
  "events": [
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "followed",
        "created_on": "2020-09-28T09:31:18"
      },
      "end_object": {
        "id": "kim",
        "type": "user"
      }
    },
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "posted",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "us open match up",
        "type": "post"
      }
    },
    {
      "start_object": {
        "id": "jack",
        "type": "user"
      },
      "relationship": {
        "type": "liked",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "us open match up",
        "type": "post"
      }
    },
    {
      "start_object": {
        "id": "us open match up",
        "type": "post"
      },
      "relationship": {
        "type": "taggedWith",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "tennis",
        "type": "tag"
      }
    },
    {
      "start_object": {
        "id": "john",
        "type": "user"
      },
      "relationship": {
        "type": "dismissed",
        "created_on": "2020-09-24T14:20:11"
      },
      "end_object": {
        "id": "sarah",
        "type": "user"
      }
    }
  ],
  "synchronous": false
});

const xhr = new XMLHttpRequest();

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === this.DONE) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://api.pipeless.io/v1/apps/123/events/batch");
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);
import requests

url = "https://api.pipeless.io/v1/apps/123/events/batch"

payload = {
    "events": [
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "followed",
                "created_on": "2020-09-28T09:31:18"
            },
            "end_object": {
                "id": "kim",
                "type": "user"
            }
        },
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "posted",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "us open match up",
                "type": "post"
            }
        },
        {
            "start_object": {
                "id": "jack",
                "type":"user"
            },
            "relationship": {
                "type": "liked",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "us open match up",
                "type": "post"
            }
        },
        {
            "start_object": {
                "id": "us open match up",
                "type": "post"
            },
            "relationship": {
                "type": "taggedWith",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "tennis",
                "type": "tag"
            }
        },
        {
            "start_object": {
                "id": "john",
                "type":"user"
            },
            "relationship": {
                "type": "dismissed",
                "created_on": "2020-09-24T014:20:11"
            },
            "end_object": {
                "id": "sarah",
                "type": "user"
            }
        }
    ],
    "synchronous": False
}
headers = {
    "accept": "application/json",
    "content-type": "application/json"
}

response = requests.request("POST", url, json=payload, headers=headers)

print(response.text)
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://api.pipeless.io/v1/apps/123/events/batch")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request["accept"] = 'application/json'
request["content-type"] = 'application/json'
request.body = "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"posted\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"jack\",\"type\":\"user\"},\"relationship\":{\"type\":\"liked\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"us open match up\",\"type\":\"post\"},\"relationship\":{\"type\":\"taggedWith\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"tennis\",\"type\":\"tag\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}"

response = http.request(request)
puts response.read_body
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.pipeless.io/v1/apps/123/events/batch",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"posted\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"jack\",\"type\":\"user\"},\"relationship\":{\"type\":\"liked\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"us open match up\",\"type\":\"post\"},\"relationship\":{\"type\":\"taggedWith\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"tennis\",\"type\":\"tag\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"events\":[{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\",\"created_on\":\"2020-09-28T09:31:18\"},\"end_object\":{\"id\":\"kim\",\"type\":\"user\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"posted\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"jack\",\"type\":\"user\"},\"relationship\":{\"type\":\"liked\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"us open match up\",\"type\":\"post\"}},{\"start_object\":{\"id\":\"us open match up\",\"type\":\"post\"},\"relationship\":{\"type\":\"taggedWith\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"tennis\",\"type\":\"tag\"}},{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"dismissed\",\"created_on\":\"2020-09-24T014:20:11\"},\"end_object\":{\"id\":\"sarah\",\"type\":\"user\"}}],\"synchronous\":false}");
Request request = new Request.Builder()
  .url("https://api.pipeless.io/v1/apps/123/events/batch")
  .post(body)
  .addHeader("accept", "application/json")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
curl --request POST \
  --url https://api.pipeless.io/v1/apps/123/events/batch \
  --header 'accept: application/json' \
  --header 'content-type: application/json' \
  --data '{"events":[{"start_object":{"id":"john","type":"user"},"relationship":{"type":"followed","created_on":"2020-09-28T09:31:18"},"end_object":{"id":"kim","type":"user"}},{"start_object":{"id":"john","type":"user"},"relationship":{"type":"posted","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"us open match up","type":"post"}},{"start_object":{"id":"jack","type":"user"},"relationship":{"type":"liked","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"us open match up","type":"post"}},{"start_object":{"id":"us open match up","type":"post"},"relationship":{"type":"taggedWith","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"tennis","type":"tag"}},{"start_object":{"id":"john","type":"user"},"relationship":{"type":"dismissed","created_on":"2020-09-24T014:20:11"},"end_object":{"id":"sarah","type":"user"}}],"synchronous":false}'

Requesting Recommendations

The api request to get recommended users to follow for a user would be:

https://api.pipeless.io/v1/apps/app_id/algos/recommendations/users-to-follow

{
  "object": {
    "id": "kim",
    "type": "user"
  },
  "followed_relationship_type": "followed",
  "content_created_relationship_type": "posted",
  "positive_relationship_type": "liked",
  "content_tagged_relationship_type": "taggedWith",
  "content_object_type": "post",
  "content_tag_object_type": "tag",
  "dismissed_relationship_type": "dismissed"
}

API Reference Documentation

You can read the reference docs for this endpoint here:
Get Recommended Users to Follow (for user)

Unfollowing Users

When a user unfollows another user, you'll want to remove that followed relationship to keep the recommendation system up-to-date. We also suggest adding a "dismissed" relationship between the two users so the user who was unfollowed will not reappear in future user recommendations (you can see examples of adding a dismissed event in the code snippets above).

Deleting a follow relationship

https://api.pipeless.io/v1/apps/app_id/events

const options = {
  method: 'DELETE',
  headers: {Accept: 'application/json', 'Content-Type': 'application/json'},
  body: JSON.stringify({
    event: {
      start_object: {id: 'john', type: 'user'},
      relationship: {type: 'followed'},
      end_object: {id: 'paula', type: 'user'}
    }
  })
};

fetch('https://api.pipeless.io/v1/apps/123/events', options)
  .then(response => console.log(response))
  .catch(err => console.error(err));
import requests

url = "https://api.pipeless.io/v1/apps/123/events"

payload = {"event": {
        "start_object": {
            "id": "john",
            "type": "user"
        },
        "relationship": {"type": "followed"},
        "end_object": {
            "id": "paula",
            "type": "user"
        }
    }}
headers = {
    "Accept": "application/json",
    "Content-Type": "application/json"
}

response = requests.request("DELETE", url, json=payload, headers=headers)

print(response.text)
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://api.pipeless.io/v1/apps/123/events")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Delete.new(url)
request["Accept"] = 'application/json'
request["Content-Type"] = 'application/json'
request.body = "{\"event\":{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\"},\"end_object\":{\"id\":\"paula\",\"type\":\"user\"}}}"

response = http.request(request)
puts response.read_body
<?php

$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://api.pipeless.io/v1/apps/123/events",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "DELETE",
  CURLOPT_POSTFIELDS => "{\"event\":{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\"},\"end_object\":{\"id\":\"paula\",\"type\":\"user\"}}}",
  CURLOPT_HTTPHEADER => [
    "Accept: application/json",
    "Content-Type: application/json"
  ],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"event\":{\"start_object\":{\"id\":\"john\",\"type\":\"user\"},\"relationship\":{\"type\":\"followed\"},\"end_object\":{\"id\":\"paula\",\"type\":\"user\"}}}");
Request request = new Request.Builder()
  .url("https://api.pipeless.io/v1/apps/123/events")
  .delete(body)
  .addHeader("Accept", "application/json")
  .addHeader("Content-Type", "application/json")
  .build();

Response response = client.newCall(request).execute();
curl --request DELETE \
  --url https://api.pipeless.io/v1/apps/123/events \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data '{"event":{"start_object":{"id":"john","type":"user"},"relationship":{"type":"followed"},"end_object":{"id":"paula","type":"user"}}}'

API Reference Documentation

You can read the reference docs for this endpoint here:
Delete Event

Deleting a User

When a user (any object really) is deleted from Pipeless, the system automatically deletes all connecting relationships. That means all of their follow relationships will be removed while keeping all of the users or tags objects that might have been connected through those relationships with the deleted user.

API Reference Documentation

You can read the reference docs for this endpoint here:
Delete Object

Related Documentation

Uploading data
Estimating usage
Running algorithms
Recommended Accounts
Get Recommended Users to Follow (for user) reference docs
Restaurant dataset tutorial recommended accounts example
Content Recommendations
Get Recommended Content (for user) reference docs