FriendPath API Documentation

Programmatic access to the same engine that powers friendpath.arbastro.com. Two public APIs: the Scan API resolves the connection path between two Roblox users; the Famous Users API returns a curated list of well-known Roblox accounts. The Scan API is metered (with a generous Suite-member free tier); the Famous Users API is completely free.

Introduction

FriendPath traces degrees of separation between any two Roblox players using bidirectional BFS through Roblox's public friend graph. Maximum depth is 20. Most queries between active players resolve in 2–4 degrees and complete in under 5 seconds. Edge cases (very inactive accounts, banned users, hidden friend lists) may take up to 30 seconds or return not_found.

Two APIs, two access models: The Scan API requires an arbastro account + API key (because it's expensive to run). The Famous Users API is free for everyone, no key, no signup.

Authentication

The Scan API authenticates via API keys passed as a Bearer token in the Authorization header. API keys begin with ak_live_ and are tied to your arbastro account. Each key is shown to you exactly once at creation; we only store its SHA-256 hash. You can revoke a key at any time from your settings.

The Famous Users API requires no authentication.

Step 1 — Create an arbastro account

FriendPath shares its account system with arbastro.com. If you don't already have an arbastro account, head to arbastro.com/signup and complete the 5-step signup (email + password, link Discord, link Roblox, pick username, recommendations). The whole thing takes about 3 minutes.

Already signed up? Just log in at the top of friendpath.arbastro.com using the same credentials.

Step 2 — Generate an API key

  1. Sign in at arbastro.com/login
  2. Go to Settings → API keys
  3. Click Generate, give it a label (e.g. "my discord bot")
  4. Copy the key immediately — it's shown once. Lost keys must be regenerated.
  5. If you don't have arbastro Suite, click Add card first to enable metered billing

Step 3 (optional) — Get arbastro Suite for free daily quota

arbastro Suite is a $4.99/month (or $4.17/month yearly) subscription that includes 50 free FriendPath Scan API calls per day, ad-free FriendPath usage, priority support, and subsidiary-giveaway eligibility. New users get a 30-day free trial.

Without Suite, you can still use the API — you just pay per call from request #1. With Suite, the first 50 calls per day each UTC day are free; calls 51+ are billed at the same metered rate.

Scan API — Overview

Resolve any two Roblox usernames or numeric user IDs and receive their connection path through the friend graph.

Endpoint

GET https://arbastro.com/api/v1/friendpath/path

Parameters

ParamTypeRequiredDescription
fromstringYesRoblox username or numeric ID. Numbers are treated as user IDs; non-numbers as usernames.
tostringYesSame as from — the other end of the path.
maxDepthnumberNoMaximum BFS depth. Default 20. Lower values fail faster on disconnected pairs.

Headers

HeaderRequiredDescription
AuthorizationYesBearer ak_live_... — your API key

Response shape

Returns a JSON object describing the path between the two users:

// 200 OK — path found
{
  "from": { "input": "Builderman", "userId": 156 },
  "to":   { "input": "david.baszucki", "userId": 5208617297 },
  "result": {
    "type": "done",
    "degrees": 2,
    "visited": 14328,
    "path": [
      { "id": 156,        "username": "Builderman" },
      { "id": 119358574,  "username": "someMutual" },
      { "id": 5208617297, "username": "david.baszucki" }
    ]
  }
}

Code examples

Click a tab below to view the example for your language.

curl "https://arbastro.com/api/v1/friendpath/path?from=Builderman&to=david.baszucki" \
  -H "Authorization: Bearer ak_live_YOUR_KEY"
// Browser or Node 18+ (uses fetch)
const res = await fetch(
  "https://arbastro.com/api/v1/friendpath/path?from=Builderman&to=david.baszucki",
  { "headers": { "Authorization": "Bearer ak_live_YOUR_KEY" } }
);
const data = await res.json();
// data.result.degrees === 2
// data.result.path === [{id, username}, ...]
console.log(`degrees: ${data.result.degrees}`);
type PathResult = {
  from: { input: string; userId: number };
  to:   { input: string; userId: number };
  result: {
    type: "done" | "not_found" | "error";
    degrees?: number;
    visited?: number;
    path?: { id: number; username: string }[];
  };
};

async function scanPath(from: string, to: string): Promise<PathResult> {
  const u = new URL("https://arbastro.com/api/v1/friendpath/path");
  u.searchParams.set("from", from);
  u.searchParams.set("to", to);
  const r = await fetch(u, {
    headers: { Authorization: `Bearer ${process.env.FRIENDPATH_KEY}` }
  });
  if (!r.ok) throw new Error(`scan failed: ${r.status}`);
  return r.json();
}
import os, requests

def scan_path(a: str, b: str) -> dict:
    key = os.environ["FRIENDPATH_KEY"]
    r = requests.get(
        "https://arbastro.com/api/v1/friendpath/path",
        params={"from": a, "to": b},
        headers={"Authorization": f"Bearer {key}"},
        timeout=60,
    )
    r.raise_for_status()
    return r.json()

data = scan_path("Builderman", "david.baszucki")
print(f"degrees: {data['result']['degrees']}")
--!strict
-- ServerScript — drop in ServerScriptService
-- Game Settings → Security → Allow HTTP Requests must be ON

local HttpService = game:GetService("HttpService")

local FRIENDPATH_KEY: string = "ak_live_YOUR_KEY"
local API_BASE: string = "https://arbastro.com/api/v1/friendpath/path"

type PathNode = { Id: number, Username: string }

type ScanResult = {
    Found: boolean,
    Degrees: number?,
    Path: { PathNode }?,
    Error: string?,
}

local function ScanFriendPath(FromUser: string, ToUser: string): ScanResult
    local Url = API_BASE
        .. "?from=" .. HttpService:UrlEncode(FromUser)
        .. "&to="   .. HttpService:UrlEncode(ToUser)

    local Ok, Response = pcall(function()
        return HttpService:RequestAsync({
            Url = Url,
            Method = "GET",
            Headers = {
                ["Authorization"] = "Bearer " .. FRIENDPATH_KEY,
            },
        })
    end)

    if not Ok or not Response.Success then
        return { Found = false, Error = "http error" } :: ScanResult
    end

    local Body = HttpService:JSONDecode(Response.Body)
    local Result = Body.result

    if Result.type ~= "done" then
        return { Found = false, Error = Result.type } :: ScanResult
    end

    local Path: { PathNode } = {}
    for _, Node in ipairs(Result.path) do
        table.insert(Path, { Id = Node.id, Username = Node.username })
    end

    return {
        Found = true,
        Degrees = Result.degrees,
        Path = Path,
    } :: ScanResult
end

-- Usage:
local Result = ScanFriendPath("Builderman", "david.baszucki")
if Result.Found then
    print("degrees:", Result.Degrees)
end
// discord.js v14 — slash command /friendpath
const { SlashCommandBuilder } = require("discord.js");

module.exports = {
  data: new SlashCommandBuilder()
    .setName("friendpath")
    .setDescription("degrees of separation between two Roblox users")
    .addStringOption(o => o.setName("from").setRequired(true))
    .addStringOption(o => o.setName("to").setRequired(true)),

  async execute(i) {
    await i.deferReply();
    const u = new URL("https://arbastro.com/api/v1/friendpath/path");
    u.searchParams.set("from", i.options.getString("from"));
    u.searchParams.set("to",   i.options.getString("to"));
    const r = await fetch(u, {
      headers: { Authorization: `Bearer ${process.env.FRIENDPATH_KEY}` }
    });
    const data = await r.json();
    if (data.result?.type !== "done") return i.editReply("no path found 😕");
    await i.editReply(
      `**${data.result.degrees} degrees** apart\n` +
      data.result.path.map(n => `• ${n.username}`).join("\n")
    );
  }
};
package friendpath

import (
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
    "os"
)

type PathResult struct {
    Result struct {
        Type    string
        Degrees int
        Path    []struct{ Id int64; Username string }
    } `json:"result"`
}

func Scan(from, to string) (*PathResult, error) {
    u, _ := url.Parse("https://arbastro.com/api/v1/friendpath/path")
    q := u.Query()
    q.Set("from", from); q.Set("to", to); u.RawQuery = q.Encode()
    req, _ := http.NewRequest("GET", u.String(), nil)
    req.Header.Set("Authorization", "Bearer "+os.Getenv("FRIENDPATH_KEY"))
    resp, err := http.DefaultClient.Do(req)
    if err != nil { return nil, err }
    defer resp.Body.Close()
    out := &PathResult{}
    return out, json.NewDecoder(resp.Body).Decode(out)
}

Live runner

Test the Scan API directly from this page. You'll need an API key — paste yours below or grab one.

Try the Scan API


      

Errors

StatusBody errorCause
400from and to are requiredMissing query param
401missing or invalid api key / api key revokedBearer token bad
402payment_method_requiredOut of free quota and no card on file
404Username didn't resolve to a user
502upstream friendpath unreachableBackend overloaded — retry

Billing & rate limits

Each successful Scan API call costs $0.005. arbastro Suite members get 50 free calls per UTC day before metering kicks in. Quota resets at 00:00 UTC.

There are no per-second rate limits today, but please don't spam — we may add throttling if you start to abuse. For high-volume use cases (>10k calls/day), email contact@arbastro.com to discuss.

Heads up: failed calls (status 4xx/5xx) don't count against your quota or bill. Only successful 200 responses are billed.

Famous Users API — Overview

Returns a curated list of "famous" Roblox users, including arbastro and subsidiary group members and creators from the Rolimons gamelist. Updated daily. Used internally by FriendPath's dice-roll button. Free for anyone, no API key, no signup.

Endpoint

GET https://friendpath.arbastro.com/api/famous-users

Response shape

Returns an array of user objects:

// 200 OK
[
  {
    "id": 156,
    "name": "Builderman",
    "displayName": "Builderman",
    "followers": 5_182_341,
    "friends": 96
  },
  { "id": 5208617297, "name": "david.baszucki", ... },
  ...
]

The list contains roughly 200–500 entries depending on the day's gamelist data. Order is approximately by follower count, with the hardcoded "always-included" accounts first.

Code examples

curl "https://friendpath.arbastro.com/api/famous-users"
const famous = await fetch("https://friendpath.arbastro.com/api/famous-users")
  .then(r => r.json());
// Pick a random one
const picked = famous[Math.floor(Math.random() * famous.length)];
console.log(`random famous: ${picked.name}`);
import requests, random

famous = requests.get("https://friendpath.arbastro.com/api/famous-users").json()
picked = random.choice(famous)
print(f"random famous: {picked['name']}")
--!strict
local HttpService = game:GetService("HttpService")

type FamousUser = {
    Id: number,
    Name: string,
    DisplayName: string?,
    Followers: number?,
    Friends: number?,
}

local function FetchFamousUsers(): { FamousUser }
    local RawJson: string = HttpService:GetAsync(
        "https://friendpath.arbastro.com/api/famous-users"
    )
    local Decoded = HttpService:JSONDecode(RawJson)
    local List: { FamousUser } = {}
    for _, U in ipairs(Decoded) do
        table.insert(List, {
            Id          = U.id,
            Name        = U.name,
            DisplayName = U.displayName,
            Followers   = U.followers,
            Friends     = U.friends,
        })
    end
    return List
end

-- Usage:
local Famous = FetchFamousUsers()
local Picked = Famous[math.random(#Famous)]
print("random famous:", Picked.Name)

Live runner

Hit "Fetch" — no key needed.

Try the Famous Users API


      

How the list is built & fair use

The list is assembled at server boot from three sources, deduplicated, and refreshed daily:

The endpoint serves a cached list — please don't poll faster than once per 5 minutes. Heavy abuse may result in IP-level rate limiting. No authentication required for normal usage.

Type definitions reference

TypeScript

export type FriendPathScanResult = {
  from: { input: string; userId: number };
  to:   { input: string; userId: number };
  result: {
    type: "done" | "not_found" | "error";
    degrees?: number;
    visited?: number;
    path?: FriendPathNode[];
    message?: string;
  };
};

export type FriendPathNode = {
  id: number;
  username: string;
};

export type FamousUser = {
  id: number;
  name: string;
  displayName: string;
  followers?: number;
  friends?: number;
};

Changelog

Get help

Stuck? Three ways to reach us: