Overview
Apiro sits between market data and your execution logic. It answers one question in the moments before you commit capital — is this token structurally a trap? — and returns a score, a verdict, itemized flags with reasoning, computed metrics and caveats.
- REST: four JSON endpoints under
/v1. - MCP: two tools —
assess_token_riskandscreen_tokens— for AI agents. - Structural, not directional: responses describe exit risk and never predict price.
Pre-launch. Domains, and anything marked indicative, may change before launch. Example responses are abbreviated.
Quickstart
- Request an API key.
- Score a token you already have an opinion about — the fastest way to calibrate trust in the output.
- Add the
tradeablegate to one strategy and compare outcomes against your unfiltered baseline. - For agent-based trading, add the MCP endpoint to your configuration alongside your existing trading tools.
Your first request
export APIRO_API_KEY="your_key"
curl "https://api.apiro.io/v1/tokens/0xC7110dd6343fb126865DB673284D5B67F247470F/risk" \
-H "Authorization: Bearer $APIRO_API_KEY"
import os
import httpx
API_KEY = os.environ["APIRO_API_KEY"]
r = httpx.get(
"https://api.apiro.io/v1/tokens/0xC7110dd6343fb126865DB673284D5B67F247470F/risk",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=2.0,
)
report = r.json()
print(report["score"], report["verdict"])
for flag in report["flags"]:
print(flag["severity"], flag["name"], flag["value"])
const res = await fetch(
"https://api.apiro.io/v1/tokens/0xC7110dd6343fb126865DB673284D5B67F247470F/risk",
{ headers: { Authorization: `Bearer ${process.env.APIRO_API_KEY}` } }
);
const report = await res.json();
console.log(report.score, report.verdict);
for (const flag of report.flags) {
console.log(flag.severity, flag.name, flag.value);
}
Authentication
Every request carries your API key as a bearer token in the Authorization header.
Authorization: Bearer YOUR_KEY
Keep keys server-side: don’t ship them in client-side code or commit them to source control.
Scores & verdicts
Every report has a score from 0 to 100 and a verdict. The score starts at 100; each triggered signal contributes a weighted penalty according to its severity band.
Ranking past the clamp with raw_penalty
The score clamps at zero, but metrics.raw_penalty is the uncapped total, preserving ordering past the clamp. Two tokens can both score 0 while one is substantially worse — sort on raw_penalty for granular ranking below the critical threshold.
A low score is a reason to skip or size down. It is explicitly not a short signal: tokens scored critical sometimes recover, because the score describes exit risk, not price path.
Flags
Each flag describes one structural condition. Flags appear in flags[] on the risk report, and by name in blocking_flags[] on the tradeable gate.
| Field | Type | Description |
|---|---|---|
name | string | Signal identifier, e.g. extreme_turnover. |
severity | string | Severity band, e.g. moderate, high, severe. |
value | number | The measured value that triggered the flag. |
detail | string | Plain-language explanation of what the value means and why it matters. Written to be read by people and reasoning agents. |
Signal names
| Name | Measures |
|---|---|
thin_liquidity | Pooled USD liquidity across every pool for the token |
extreme_turnover | 24h volume ÷ pooled liquidity |
volume_exceeds_mcap | 24h volume ÷ market capitalisation |
newly_launched | Hours since the earliest pool was created |
price_collapse | 24h price change |
sell_pressure | Sells as a proportion of all transactions |
micro_trade_signature | Average trade size against transaction count |
price_dispersion | Spread between the highest and lowest price across pools |
activity_cessation | Last-hour transactions against the last 24 hours |
liquidity_concentration | Share of total liquidity held in a single pool |
Caveats
Every response includes a caveats array that states the limits of the output in-band, so an agent reasoning over the result weighs it correctly rather than over-trusting it.
"caveats": [
"Wash-trading signals are statistical proxies, not proof of intent.",
"Structural risk only: this score does not predict price direction.",
"Derived from public DEX pool state; it cannot see contract-level traps."
]
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/tokens/{address}/risk | Full structural risk report |
| GET | /v1/tokens/{address}/tradeable | One-bit pre-trade gate |
| POST | /v1/tokens/risk | Batch scoring, up to 30 addresses |
| GET | /v1/health | Liveness and version |
Risk report
Full structural risk report: score, verdict, itemized flags with reasoning, computed metrics and caveats.
Path parameters
| Name | Type | Description |
|---|---|---|
address | string | Token contract address on Robinhood Chain. |
Response fields
| Field | Type | Description |
|---|---|---|
address | string | Token contract address. |
symbol | string | Token symbol. |
score | number | 0–100, clamped at 0. |
verdict | string | acceptable, caution, high_risk or critical. |
flags | object[] | Triggered signals. See Flags. |
metrics | object | Computed inputs, including raw_penalty. |
caveats | string[] | Limits of the output, returned on every response. |
Metrics
| Field | Description |
|---|---|
raw_penalty | Uncapped penalty total. Sort on it to rank tokens below the clamp. |
total_liquidity_usd | Pooled liquidity across all pools, USD. |
volume_h24_usd | 24-hour volume, USD. |
market_cap_usd | Market capitalisation, USD. |
turnover_ratio | 24h volume ÷ pooled liquidity. |
volume_to_mcap_ratio | 24h volume ÷ market cap. |
pool_age_hours | Hours since the earliest pool was created. |
txns_h24 | Transactions in the last 24 hours. |
txns_h1 | Transactions in the last hour. |
avg_trade_size_usd | Average trade size, USD. |
Example response
{
"address": "0xC7110dd6343fb126865DB673284D5B67F247470F",
"symbol": "ROBINTWINE",
"score": 0.0,
"verdict": "critical",
"flags": [
{
"name": "extreme_turnover",
"severity": "severe",
"value": 117.84,
"detail": "24h volume is 117.8x pooled liquidity. A pool cannot organically recycle its entire depth this many times in a day; this pattern is consistent with wash trading or a small set of wallets cycling the same inventory."
},
{
"name": "volume_exceeds_mcap",
"severity": "severe",
"value": 235.19,
"detail": "24h volume is 235.2x the token's entire market cap ($662.5k traded against a $2.8k cap). Every coin in existence would have to change hands several times over to produce this honestly."
},
{
"name": "activity_cessation",
"severity": "severe",
"value": 0,
"detail": "Zero trades in the last hour against 4,819 over 24h. Activity has stopped dead. Volume that switches off this abruptly was being generated, not demanded -- and with it off there is no bid to exit into at any size."
}
],
"metrics": {
"raw_penalty": 158.0,
"total_liquidity_usd": 5622.15,
"volume_h24_usd": 662519.47,
"market_cap_usd": 2817.0,
"turnover_ratio": 117.84,
"volume_to_mcap_ratio": 235.19,
"pool_age_hours": 16.8,
"txns_h24": 4821,
"txns_h1": 0,
"avg_trade_size_usd": 137.42
},
"caveats": [
"Wash-trading signals are statistical proxies, not proof of intent.",
"Structural risk only: this score does not predict price direction.",
"Derived from public DEX pool state; it cannot see contract-level traps."
]
}
Tradeable gate
A one-bit gate for bots that want a pre-trade check without parsing a full report.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
address | path | string | Token contract address on Robinhood Chain. |
min_score | query | integer | Minimum acceptable score, e.g. 45 — the floor of the caution band. |
Response fields
| Field | Type | Description |
|---|---|---|
tradeable | boolean | Whether the token passes the gate at your min_score. |
score | number | 0–100, clamped at 0. |
verdict | string | Verdict band for the score. |
blocking_flags | string[] | Names of the flags that blocked the trade. |
reason | string | Plain-language reason the gate closed. |
address, symbol | string | As in the risk report. |
Example
curl "https://api.apiro.io/v1/tokens/0xC7110dd6343fb126865DB673284D5B67F247470F/tradeable?min_score=45" \
-H "Authorization: Bearer $APIRO_API_KEY"
r = httpx.get(
"https://api.apiro.io/v1/tokens/0xC7110dd6343fb126865DB673284D5B67F247470F/tradeable",
params={"min_score": 45},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=2.0,
)
gate = r.json()
if not gate["tradeable"]:
print("blocked:", gate["blocking_flags"])
const res = await fetch(
"https://api.apiro.io/v1/tokens/0xC7110dd6343fb126865DB673284D5B67F247470F/tradeable?min_score=45",
{ headers: { Authorization: `Bearer ${process.env.APIRO_API_KEY}` } }
);
const gate = await res.json();
if (!gate.tradeable) console.log("blocked:", gate.blocking_flags);
{
"address": "0xC7110dd6343fb126865DB673284D5B67F247470F",
"symbol": "ROBINTWINE",
"tradeable": false,
"score": 0.0,
"verdict": "critical",
"blocking_flags": ["thin_liquidity", "extreme_turnover", "volume_exceeds_mcap"],
"reason": "Total pooled liquidity is $5.6k..."
}
Batch scoring
Batch scoring for screening candidate sets — up to 30 addresses per call.
Request body
| Field | Type | Description |
|---|---|---|
addresses | string[] | Token addresses to score. Maximum 30 per call. |
Example
curl -X POST "https://api.apiro.io/v1/tokens/risk" \
-H "Authorization: Bearer $APIRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"addresses": ["0xC7110dd6343fb126865DB673284D5B67F247470F"]}'
candidates = ["0xC7110dd6343fb126865DB673284D5B67F247470F"] # up to 30
r = httpx.post(
"https://api.apiro.io/v1/tokens/risk",
json={"addresses": candidates},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=5.0,
)
The batch request and response shapes will be confirmed at launch. For agents, screen_tokens returns candidates ranked safest-first with blocking flags.
Health
Liveness and version — use it for uptime checks and deployment verification.
curl "https://api.apiro.io/v1/health" \
-H "Authorization: Bearer $APIRO_API_KEY"
Rule-based bots
One call, one branch, placed immediately before order submission.
import httpx
def should_trade(address: str, min_score: int = 45) -> bool:
r = httpx.get(
f"https://api.apiro.io/v1/tokens/{address}/tradeable",
params={"min_score": min_score},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=2.0,
)
return r.json()["tradeable"]
if should_trade(token_address):
execute_buy(token_address, size)
Because the underlying signals move slowly, responses are safely cacheable for a minute or more — so the gate adds negligible latency to a hot path and negligible cost to a high-frequency strategy.
With a cache and an explicit failure mode
import time
import httpx
TTL_SECONDS = 60
_cache: dict[tuple[str, int], tuple[float, bool]] = {}
def should_trade(address: str, min_score: int = 45) -> bool:
key = (address, min_score)
hit = _cache.get(key)
if hit and time.monotonic() - hit[0] < TTL_SECONDS:
return hit[1]
try:
r = httpx.get(
f"https://api.apiro.io/v1/tokens/{address}/tradeable",
params={"min_score": min_score},
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=2.0,
)
r.raise_for_status()
tradeable = r.json()["tradeable"]
except httpx.HTTPError:
return False # fail closed: no check, no trade
_cache[key] = (time.monotonic(), tradeable)
return tradeable
MCP for AI agents
Apiro ships as an MCP (Model Context Protocol) server, so it drops into the same configuration as your agent’s trading tools — for example alongside Robinhood’s Agentic Trading, which connects third-party AI agents to a dedicated brokerage account over MCP. The agent holds both the ability to trade and the ability to check what it’s about to trade.
{
"mcpServers": {
"apiro": {
"url": "https://mcp.apiro.io/sse",
"headers": { "Authorization": "Bearer YOUR_KEY" }
}
}
}
Tools
| Tool | Use it to |
|---|---|
assess_token_risk | Get a structural assessment of one token before trading it. |
screen_tokens | Choose between up to 30 candidates, returned ranked safest-first with blocking flags. |
Why agents get more out of it
A rule-based bot can only threshold on a number; everything qualitative in the response is wasted on it. An agent reads “the deployer’s pool is sixteen hours old, volume is 235× the entire market cap, and trading stopped an hour ago” and weighs it against the user’s instructions — perhaps proceeding at reduced size on a token scored 50, and refusing outright on one scored 20, without anyone hard-coding either threshold.
Screening pipelines
For strategies that generate candidate sets — new-launch monitors, trending scans — batch scoring filters the set before any deeper analysis runs, removing the majority of candidates at a fraction of the cost of evaluating them individually.
- Score candidates in batches of up to 30.
- Drop anything below your
min_scorebefore running expensive analysis. - Rank what remains below the critical threshold by
raw_penalty.
Caching & latency
Apiro recomputes on a cadence of minutes, not milliseconds. Structural conditions move slowly, so being a few seconds behind costs nothing — and Apiro stays out of the latency arms race entirely.
- Cache responses for a minute or more.
- Use a short client timeout — the examples use two seconds — and decide explicitly what happens when a check fails.
- Prefer batch scoring for candidate sets over many single calls.
- Rate limits depend on your tier — see pricing.
Limitations
- Structural risk only. Scores don’t predict price direction.
- Statistical proxies. Manufactured-volume signals are patterns, not proof of intent.
- Pool state only. Contract-level traps — mint authority, transfer blacklists, honeypot logic, proxy upgradeability — aren’t visible yet.
Planned: contract-level risk analysis, a deployer reputation graph, wallet clustering, listing-catalyst intelligence, a historical scoring API and webhooks. See the roadmap.