CRIUS
APIExplainerTutorialPython

Robinhood Chain API for AI Agents: Live On-Chain Research

Give an AI agent live Robinhood Chain data: llms.txt and Markdown docs for context, OpenAPI-derived tools for calls, and the guardrails that keep it accurate and cheap.

Crius 7 min read

An AI agent can research Robinhood Chain in real time with two things from Crius: the documentation in machine-readable form (llms.txt, a single-file llms-full.txt and a Markdown copy of every page), and the API itself as tools built from its OpenAPI spec. The hard part isn’t the wiring. It’s keeping the agent accurate (right token, right block) and bounded (credits, rate limits, payload size), and this guide covers both with code you can run.

What an agent needs, and where to get it

Need Resource
A map of what exists https://crius.sh/llms.txt: every docs page with a one-line summary (llmstxt.org format)
The full documentation in one fetch https://crius.sh/llms-full.txt: about 170 KB of Markdown
One page, clean Any docs URL with .md, e.g. /docs/reference/holders.md
Callable endpoints https://api.crius.sh/openapi.json: OpenAPI 3.1, 47 paths
Data without a key Core endpoints (price, token, pools, trades, candles, search, stats) work anonymously at 2 requests per second

The docs and spec play different roles. The spec tells a model which calls exist and what parameters they take. The docs tell it what the response fields mean: what confidence on a price means, why holders include pools, which finality to trust. Give the agent the spec as tools and the relevant .md page as context, and it can use endpoints it has never seen.

From OpenAPI to tool definitions

Every major model API accepts tools as a name, a description and a JSON Schema for the arguments. The OpenAPI spec already contains all three, so a small converter turns endpoints into tools. Don’t hand an agent all 47 paths. Pick the read-only operations that fit its job; fewer tools means better choices and smaller prompts.

"""agent_tools.py: Crius OpenAPI -> LLM tool definitions, plus a tool runner with guardrails.
Works with any model that accepts JSON-schema tools (function calling): pass TOOLS to the model,
then send each tool call it makes to run_tool() and return the result as the tool's output.
"""
import os, re, json, requests
API = "https://api.crius.sh"
s = requests.Session()
s.headers["x-api-key"] = os.environ["CRIUS_API_KEY"]
# Guardrail 1: a short allow-list of read-only operations, not the whole API.
ALLOW = {
"/v1/{chain}/search",
"/v1/{chain}/token/{address}",
"/v1/{chain}/token/{address}/candles",
"/v1/{chain}/token/{address}/holders",
"/v1/{chain}/token/{address}/traders",
"/v1/{chain}/token/{address}/security",
}
spec = requests.get(f"{API}/openapi.json", timeout=30).json()
def tool_name(path):
return re.sub(r"[^a-z0-9]+", "_", path.replace("/v1/{chain}/", "").replace("{address}", "").lower()).strip("_") or "root"
TOOLS, ROUTES = [], {}
for path, ops in spec["paths"].items():
op = ops.get("get")
if path not in ALLOW or not op:
continue
props, required = {}, []
for p in op.get("parameters", []):
if p["name"] == "chain":
continue # always Robinhood Chain
schema = {k: v for k, v in p.get("schema", {}).items() if k in ("type", "enum", "default", "minimum", "maximum")}
props[p["name"]] = {**schema, "description": p.get("description", "")}
if p.get("required"):
required.append(p["name"])
name = tool_name(path)
ROUTES[name] = path
TOOLS.append({"name": name, "description": (op.get("summary", "") + ". " + op.get("description", "")).strip()[:500],
"parameters": {"type": "object", "properties": props, "required": required}})
class Budget:
"""Guardrail 2: a credit and call budget per conversation, read from the API's own headers."""
def __init__(self, credits=200, calls=15):
self.credits, self.calls = credits, calls
def run_tool(name, args, budget):
if name not in ROUTES:
return {"error": f"unknown tool {name}"}
if budget.calls <= 0 or budget.credits <= 0:
return {"error": "budget exhausted: answer with what you have"}
path, query = ROUTES[name], {}
for k, v in args.items():
if "{" + k + "}" in path:
if not re.fullmatch(r"0x[0-9a-fA-F]{40}", str(v)): # Guardrail 3: validate before calling
return {"error": f"{k} must be a 0x address"}
path = path.replace("{" + k + "}", str(v))
else:
query[k] = v
r = s.get(API + path.replace("{chain}", "robinhood"), params=query, timeout=30)
budget.calls -= 1
budget.credits -= int(r.headers.get("x-credits-cost", 1))
body = r.json()
if r.status_code != 200:
return {"error": body.get("error"), "retry_after_s": r.headers.get("retry-after")}
# Guardrail 4: keep answers small; the model doesn't need 500 rows.
return {"data": body["data"], "block": body["meta"]["block"]} if len(json.dumps(body)) < 12_000 \
else {"data_truncated": json.dumps(body["data"])[:12_000], "block": body["meta"]["block"]}
if __name__ == "__main__":
print(len(TOOLS), "tools:", ", ".join(t["name"] for t in TOOLS))
b = Budget()
# What a model's tool calls would look like:
hit = run_tool("search", {"q": "TSLA"}, b)
# Many tokens copy a ticker: the tokenized stock carries a stock_ label.
tsla = next(x for x in hit["data"] if (x.get("label") or "").startswith("stock_"))
print("search ->", len(hit["data"]), "results; picked", tsla["name"], tsla["address"])
sec = run_tool("token_security", {"address": tsla["address"]}, b)
print("security ->", list(sec["data"].keys())[:8])
print("bad input ->", run_tool("token", {"address": "TSLA"}, b))
print(f"budget left: {b.credits} credits, {b.calls} calls")
6 tools: search, token, token_candles, token_holders, token_security, token_traders
search -> 10 results; picked Tesla • Robinhood Token 0x322f0929c4625ed5bad873c95208d54e1c003b2d
security -> ['contract', 'flags', 'honeypot', 'hook', 'known_asset', 'limits', 'liquidity', 'primary_dex']
bad input -> {'error': 'address must be a 0x address'}
budget left: 198 credits, 13 calls

Pass TOOLS to your model provider’s tool or function-calling API, feed each tool call to run_tool, and return the result as the tool’s output. The companion guide shows that loop, plus an MCP server, in full.

Guardrails that matter for on-chain research

1. Tickers are not identities

The single most common agent error on launchpad chains is answering about the wrong token. A search for “TSLA” returns the tokenized stock first, then three other tokens using the same ticker (“memestock”, “OG Memestock” and more). A search for “tesla” puts a memecoin called “Gently Used Tesla” first. Tell the model to resolve every ticker to an address and confirm it before quoting data. For tokenized stocks, the result’s label (stock_tsla, stock_nvda…) is the reliable marker; for other tokens, prefer the one with the most liquidity, and say so.

2. Cite the block

Every Crius response includes meta.block. Have the agent quote it (“price $371.63 at block 96,727,730”). That makes answers verifiable and makes stale context obvious: an agent re-reading a cached result can compare blocks instead of guessing.

3. Bound cost explicitly

Every response reports its cost in x-credits-cost, so a budget object like the one above is exact, not estimated. Analytics endpoints such as holder cohorts or bubble maps cost 10 credits; prices cost 1. A per-conversation budget stops an agent that loops on a failing call from draining a key.

4. Respect rate limits and busy signals

On 429, wait for Retry-After (the agent can also read x-ratelimit-remaining). On 503 from a heavy endpoint, back off and retry once. Return these errors to the model as structured tool output rather than raising; models handle “busy, retry in 2 s” well when they can see it.

5. Keep payloads small

A 500-row holder list is useless in a context window. Use limit, request only the window you need, and truncate on the way back as shown. Columnar endpoints like candles are compact by design.

6. Give the agent a key of its own

Create a dedicated key for the agent in the dashboard: read scope only, a per-key rate limit, an IP allow-list if it runs on a fixed host, and an expiry. If something goes wrong, revoke that key without touching anything else.

A research workflow that works

For “research this token” style requests, a fixed sequence beats letting the model improvise:

  1. Resolve the token: search, then confirm the address.
  2. Profile: token for price, market cap, liquidity, launchpad and holder summary.
  3. Safety: token_security for honeypot simulation, taxes and contract flags.
  4. Ownership: token_holders (with infrastructure labelled) and, for launches, bundles.
  5. Flow: token_traders over 24 hours; who is making money, and are they snipers?
  6. Summarize with citations: numbers with their block, and an explicit note of anything unknown.

Put this sequence in the system prompt. It keeps runs consistent and the cost predictable: about 10–30 credits per token.

A system prompt to start from

You research Robinhood Chain (chain id 4663) using Crius tools. Rules:
- Resolve tickers to a 0x address with search before anything else; tickers are not unique.
Tokenized stocks have a label like stock_tsla.
- Quote every number with the block it came from (the "block" field of tool results).
- Prices carry a confidence; mention low confidence or thin liquidity when present.
- Holder lists include pools, lockers and burn addresses (see labels/tags); exclude them from concentration.
- If a tool returns an error, read it: retry once after retry_after_s, otherwise report the gap.
- Docs for any endpoint: https://crius.sh/docs/reference/<group>.md

Frequently asked questions

Does Crius have an llms.txt?

Yes: https://crius.sh/llms.txt indexes every docs page, llms-full.txt contains the whole documentation in one file, and every docs page is available as Markdown by adding .md to its URL.

Can an agent use the API without a key?

For core endpoints (prices, token profiles, pools, trades, candles, search and chain stats), yes, at 2 requests per second per IP. Holders, analytics and streaming need a key; the Free plan includes 1,000,000 credits a month.

How do I turn the OpenAPI spec into tools?

Each operation’s parameters become a JSON Schema, and the summary and description become the tool description. The script above does it in about 30 lines. Expose only the operations the agent needs.

How do I stop an agent from spending too many credits?

Track x-credits-cost from each response against a per-conversation budget, cap the number of calls, and give the agent its own key with a per-key rate limit so a runaway loop can’t affect anything else.

Is the data fresh enough for live research?

Yes. Data is typically under a second behind the chain head (meta.lag_ms), and each response says which block it reflects.

Build with Crius

Robinhood Chain data in one API

Free plan with 1M credits a month. Your key in under a minute.

More from the blog