The Wall of Rugs, as data
curl --request GET \
--url https://scienter.xyz/api/v1/leaderboard \
--header 'Authorization: Bearer <token>'import requests
url = "https://scienter.xyz/api/v1/leaderboard"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://scienter.xyz/api/v1/leaderboard', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scienter.xyz/api/v1/leaderboard",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://scienter.xyz/api/v1/leaderboard"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://scienter.xyz/api/v1/leaderboard")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://scienter.xyz/api/v1/leaderboard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"state": "ok",
"subject": "<string>",
"sources": [
{
"name": "scienter:intelligence",
"url": "<string>",
"as_of": 123
}
],
"as_of": 123,
"data": {
"entries": [
{
"address": "<string>",
"chain": "<string>",
"score": 123,
"first_flagged_at": "<string>",
"last_scored_at": "<string>",
"top_evidence": {},
"model_version": "<string>"
}
],
"count": 123,
"empty": true,
"empty_reason": "<string>",
"window_hours": 123,
"suppressed": 123
},
"reason": "<string>"
}{
"error": "<string>",
"reason": "no_credential",
"entitlements_durable": true
}{
"error": "<string>"
}Scienter API (paid)
Flagged wallet leaderboard
The Wall of Rugs as data. A quiet day and an unreachable producer are different answers, and this route keeps them apart.
GET
/
api
/
v1
/
leaderboard
The Wall of Rugs, as data
curl --request GET \
--url https://scienter.xyz/api/v1/leaderboard \
--header 'Authorization: Bearer <token>'import requests
url = "https://scienter.xyz/api/v1/leaderboard"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://scienter.xyz/api/v1/leaderboard', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scienter.xyz/api/v1/leaderboard",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://scienter.xyz/api/v1/leaderboard"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://scienter.xyz/api/v1/leaderboard")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://scienter.xyz/api/v1/leaderboard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"state": "ok",
"subject": "<string>",
"sources": [
{
"name": "scienter:intelligence",
"url": "<string>",
"as_of": 123
}
],
"as_of": 123,
"data": {
"entries": [
{
"address": "<string>",
"chain": "<string>",
"score": 123,
"first_flagged_at": "<string>",
"last_scored_at": "<string>",
"top_evidence": {},
"model_version": "<string>"
}
],
"count": 123,
"empty": true,
"empty_reason": "<string>",
"window_hours": 123,
"suppressed": 123
},
"reason": "<string>"
}{
"error": "<string>",
"reason": "no_credential",
"entitlements_durable": true
}{
"error": "<string>"
}This leaderboard endpoint is paid.
/api/v1/leaderboard needs an sc_ key
from an active Analyst, Desk or Enterprise plan, and returns the flagged token
contracts behind the Wall of Rugs.It is not the provider leaderboard,
which scores data vendors and is free. Two boards, similar names; asking about
the wrong one gets you a confident answer about something you did not buy.What comes back
curl
curl -H "Authorization: Bearer sc_..." \
"https://scienter.xyz/api/v1/leaderboard?limit=20"
limit defaults to 20 and is capped at 100.
{
"state": "ok",
"subject": "flagged-wallets:24h",
"data": {
"entries": [
{
"address": "0x...",
"chain": "ethereum",
"score": 87,
"first_flagged_at": "2026-09-17T04:10:00Z",
"last_scored_at": "2026-09-18T04:10:00Z",
"top_evidence": "...",
"model_version": "rug-v4"
}
],
"count": 1,
"empty": false,
"empty_reason": null,
"window_hours": 24,
"established_suppressed": 0,
"ever_written": true,
"caveat": "A flag is a model output about a token contract, not a legal finding and not an allegation against any person."
}
}
Three outcomes, and two of them look the same if you collapse them
| Response | Means |
|---|---|
ok, entries populated | Wallets crossed the flag threshold in the window |
ok, empty: true, empty_reason set | A real finding. Nothing crossed the threshold. Coverage ran and found nothing |
insufficient_evidence (not_deployed), data: null | The producer did not answer. Nothing was checked |
No wallets crossed the flag threshold in the last 24 hours. This is a real result, not a gap in coverage.
ever_written separates the two empty cases that remain: false means the flag
table has never been written at all, which is a gap rather than a quiet day, and
null means the producer could not confirm either way on this read.
The blue-chip allowlist runs on this response
Established tokens are filtered out here even though the producer is supposed to have excluded them already, andestablished_suppressed reports how many this
response dropped. The wall reads whatever SCIENTER_INTELLIGENCE_URL points at,
and one environment variable can repoint it at an older engine. USDC appearing
in an MCP tool result for an hour is not worth the saved filter.
What this does not say
A flag is a model output about a token contract. It is not a legal finding, not an allegation against any person, and not advice. See the rug model’s methodology for what the score is and is not — in particular that it is a rank, not a probability.Scienter analyses public blockchain records. A finding states what those records show and how confident we are; it is evidence, not proof of anyone’s identity, intent or wrongdoing, and an attribution we cite is the cited source’s. An empty result means not analysed, never safe. Nothing here is legal, tax or investment advice, or a recommendation to transact with any address or asset — see Disclaimers for the full text.
Authorizations
An sc_ key from https://scienter.xyz/settings/api, sent as Authorization: Bearer sc_.... The same key is accepted in the x-scienter-membership header. Prefer the header over any query form: a credential in a URL reaches access logs, proxies and browser history.
Query Parameters
Required range:
x <= 100Response
ok with the entries (possibly empty, with empty_reason), or not_deployed.
The four-state envelope. state: ok carries data as described; the three absences carry data: null and reason. See the Absent schema.