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.
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.
Step 2 — Generate an API key
- Sign in at arbastro.com/login
- Go to Settings → API keys
- Click Generate, give it a label (e.g. "my discord bot")
- Copy the key immediately — it's shown once. Lost keys must be regenerated.
- 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
https://arbastro.com/api/v1/friendpath/path
Parameters
| Param | Type | Required | Description |
|---|---|---|---|
from | string | Yes | Roblox username or numeric ID. Numbers are treated as user IDs; non-numbers as usernames. |
to | string | Yes | Same as from — the other end of the path. |
maxDepth | number | No | Maximum BFS depth. Default 20. Lower values fail faster on disconnected pairs. |
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer 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
| Status | Body error | Cause |
|---|---|---|
| 400 | from and to are required | Missing query param |
| 401 | missing or invalid api key / api key revoked | Bearer token bad |
| 402 | payment_method_required | Out of free quota and no card on file |
| 404 | — | Username didn't resolve to a user |
| 502 | upstream friendpath unreachable | Backend 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.
Concrete rate limits per API key:
- 10 requests per second burst (token-bucket)
- 120 requests per minute sustained
- 5,000 requests per day hard ceiling per key (contact us for higher)
Exceed any of these and you'll get a 429 Too Many Requests with a Retry-After header indicating how many seconds to wait. Failed calls (4xx/5xx) do not count against your quota or bill — only successful 200 responses are billed.
For high-volume use cases beyond the daily ceiling (10k+ calls/day), email [email protected] for a custom quota.
Reliability, SLAs, and retries
FriendPath runs on a dedicated Oracle Cloud VM (not a shared-tier hobby host) with auto-restart via systemd, Cloudflare DDoS protection, and an origin TLS cert that doesn't expire until 2041. See the live status page for current uptime metrics across 24h / 7d / 30d / 90d windows.
Reliability targets:
- Uptime target: 99.9% monthly (about 43 min/month allowed downtime). Real numbers visible at /status.
- p50 latency: ~45s end-to-end for a typical Scan call (the BFS itself dominates — network round-trips are <200ms).
- p95 latency: ~90s. Deep paths (5+ hops) or pairs where one account has hundreds of friends can take longer.
- Hard timeout: 5 minutes. If a Scan hasn't completed by then, you'll get a 504 with
reason: "scan timeout".
Retry policy: the API is idempotent for a given (from, to) pair. Retrying a request with the same parameters is always safe and won't double-bill — only the final successful 200 response is charged. Recommended retry strategy:
- On
429: respect theRetry-Afterheader (in seconds), then retry. Cap at 3 attempts. - On
502/503/504: exponential backoff (1s, 2s, 4s, 8s). Cap at 4 attempts. - On
401/402/422: do not retry — the request is broken at the application level. Fix the key, the payment, or the input usernames. - On network failures (TCP reset, DNS error): exponential backoff with jitter. Cap at 3 attempts.
Idempotency keys: not required — the API doesn't have side effects beyond billing. If you need to be extra-safe with charges, supply your own X-Idempotency-Key header (any string) and we'll deduplicate identical requests received within 10 minutes.
Webhooks for long-running scans
For scans you expect to take a while (5+ hop paths, low-activity accounts), you can supply a webhook URL and we'll POST the result there instead of holding the HTTP connection open. Add &webhook=YOUR_HTTPS_URL to the request and the API responds immediately with 202 Accepted and a scanId. When the scan finishes, we POST the same JSON shape as the synchronous response to your webhook URL.
Webhook delivery guarantees:
- HTTPS-only. HTTP URLs are rejected.
- At-least-once delivery. If your endpoint returns non-2xx, we retry up to 5 times with exponential backoff (1s, 5s, 30s, 5min, 30min).
- HMAC signature in
X-FriendPath-Signatureheader (sha256 of body + your API key) so you can verify authenticity. - Webhooks fire from a fixed IP block; contact us if you need to allowlist.
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
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:
- Hardcoded famous accounts — David Baszucki, Builderman, Shedletsky, KreekCraft, etc. — a small curated seed list.
- Roblox group creators — the owners of every game returned by
https://api.rolimons.com/games/v1/gamelist. When a game is owned by a Roblox group, the group's owner user is added. - "Top Playing Now" creators — fetched once per day from
https://apis.roblox.com/explore-api/v1/get-sort-content?sortId=top-playing-now. - Roblox staff & Star Creators — every member of the Official Roblox group (1200769, Roblox employees) and the Roblox Star Creators group (4199740) is included.
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.
Embed Widget
The FriendPath embed widget lets you drop a tiny "Are you friends with X?" search box on any other website with a single iframe. Useful for Roblox-creator personal sites, fan wikis, Discord landing pages, or anywhere you want a quick path-to-celebrity check without rebuilding the UI.
The widget is self-contained: it loads its own minimal CSS, prefills a target username from the URL, accepts the visitor's username in an input, and on submit opens a new tab to FriendPath with both names pre-filled and the search auto-started.
iframe snippet
Replace USERNAME with the Roblox account you want visitors to check their distance to:
<iframe
src="https://friendpath.arbastro.com/embed/david.baszucki"
width="360"
height="240"
frameborder="0"
loading="lazy"
title="FriendPath — Are you friends with David Baszucki?">
</iframe>
The widget responds to whatever width/height you give the iframe. The recommended minimum is 320×220. The embed URL is https://friendpath.arbastro.com/embed/<username> — only URL-safe characters (letters, digits, underscore, period, hyphen) are accepted; everything else returns 400.
Direct link with prefill
You can also link to the main FriendPath site with ?from=X&to=Y query params. The page will pre-fill both fields and automatically start the search:
https://friendpath.arbastro.com/?from=YourUsername&to=david.baszucki
Either parameter is optional. If only one is supplied, that input is pre-filled and the user finishes the other manually. If both are supplied, the search starts automatically after a short delay (~300ms, to let the autocomplete and other modules initialize).
Live preview
This is exactly what the iframe renders:
What the widget does NOT do
- Run the search inline (we open a new tab to friendpath.arbastro.com instead — searches need progress streaming, the 3D graph, etc., which would balloon the embed size)
- Look up the target's display name or avatar (kept lightweight; if you want a richer embed, link to the main site instead)
- Support custom theming via query params (yet — could add this if there's demand)
Privacy & embedding policy
The widget sets a permissive Content-Security-Policy: frame-ancestors * header so any site can embed it. It doesn't track visitors — no cookies, no analytics. The only network request the widget makes is when a user clicks Find Path, which opens a new tab to the main site.
Backlinks from embed instances help FriendPath's SEO. If you embed it on your own site, leaving the small "Powered by FriendPath" footer link visible is appreciated but not required.
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
- 2026-05 — Public docs page launched.
- 2026-05 — Famous Users API made publicly callable (no key).
- 2026-05 — Scan API moved behind arbastro accounts + metered billing.
Get help
Stuck? Three ways to reach us:
- Email [email protected]
- Open a ticket in the arbastro Discord #support channel
- Use the contact form at arbastro.com/#contact