DD
Shady's Research Bot API v1
API reference · v1

Read what a coin's holders say, from your own code

The same reads /dd gives you on Discord and Telegram, as JSON. One call per coin returns what it is according to the people holding it, who they say is behind it, why they are in, and what argues against.

Base URL https://api.shadysresearchbot.com/v1
Auth Bearer key, made with /api
Plans Pro and Max

Get a key

  1. Run /api in Discord, or in a private chat with the bot on Telegram.
  2. Press Create key and give it a name. Copy the key straight away: it is shown once, and only a fingerprint of it is kept.
  3. Send it with every request, as below.

Keys come with Pro and Max. A key belongs to the account that made it: one made on Discord spends that Discord account's plan, and one made on Telegram spends the Telegram account's. Each account can hold three keys at once. Keys start with srb_; one made before the bot was renamed starts with ddb_ and keeps working.

Authentication

Send the key as a bearer token on every request.

curl https://api.shadysresearchbot.com/v1/usage \
  -H "Authorization: Bearer srb_7Hq2kLm9xR4vTn8pWb3cYs6dFg1hJz5aQe0uNo"

Keep keys on a server. Anyone holding one spends your reads, so never put it in a browser app, a public repo or a shared notebook. If one leaks, revoke it from /api and it stops working at once.

Endpoints

Four endpoints, all GET. Only the coin report spends a read.

Endpoint Returns Costs
/v1/coins/{address} A coin's report 1 read
/v1/coins/{address}/holders Top holders who also posted free
/v1/jobs/{job} A report still being read free
/v1/usage Your plan and what is left free
GET

/v1/coins/{address}

1 read

The full report, the same one the bot draws. A coin somebody has already read comes back at once from the store. One nobody has read yet is read now, which takes a while: the request waits up to 60 seconds and, if the read is still going, answers 202 with a job to poll.

Parameter In What it does
address path A Solana mint, or a 0x address on an EVM chain.
chain query, optional solana, ethereum, base, bsc, arbitrum, robinhood or arc. Leave it out and the chain is worked out for you: a Solana address by its shape, a 0x address by looking it up. If it cannot be placed you get COIN_NOT_PLACED, never a guess.
fresh query, optional true skips the stored report and reads the coin again now. Always charged, even on a coin you read today. Once per coin every 5 minutes.
wait query, optional Seconds to hold the request open for a read in progress, 0 to 60. Default 60. Send 0 to get a job straight away.
Request
curl https://api.shadysresearchbot.com/v1/coins/4kM2pB8cQyRZ3n5WdT1sHvLx6eJfGa9uN7Vqpump \
  -H "Authorization: Bearer $SRB_KEY"
200 OK · trimmed
{
  "report": {
    "address": "4kM2pB8cQyRZ3n5WdT1sHvLx6eJfGa9uN7Vqpump",
    "chain": "solana",
    "verdict": "clear",
    "headline": "A cat coin held for its community, with no product claimed.",
    "story": "Launched on pump.fun as a joke about ...",
    "utility": "",
    "team": "Holders say the dev is anonymous and has not sold.",
    "whyBuying": ["the community", "early holders are still in"],
    "concerns": ["no product", "one wallet holds a large share"],
    "links": [
      { "url": "https://x.com/...", "host": "x.com", "kind": "tweet", "count": 9, "authors": 7 }
    ],
    "callers": [ ... ],
    "stats": { "callers": 42, "distinctAuthors": 31, "medianPositionUsd": 380 },
    "cached": true,
    "generatedAt": "2026-10-08T15:02:11Z"
  },
  "quota": {
    "charged": true,
    "freeRepeat": false,
    "day": { "used": 7, "limit": 15 },
    "month": { "used": 59, "limit": 200 }
  }
}
202 Accepted · still reading
{
  "status": "reading",
  "job": "job_9fQ2xLw4",
  "poll": "/v1/jobs/job_9fQ2xLw4",
  "retryAfter": 5
}
GET

/v1/coins/{address}/holders

free

The holders who also posted, from pump.fun and FOMO, as the bot's Holders button shows them. Each row's rank is its place on that app's own list, not among every wallet on chain, so ranks of 1, 4 and 5 mean the 2nd and 3rd biggest positions never wrote a word. A snapshot stands for five minutes. Takes the same chain parameter.

200 OK · trimmed
{
  "address": "4kM2pB8cQyRZ3n5WdT1sHvLx6eJfGa9uN7Vqpump",
  "chain": "solana",
  "fetchedAt": "2026-10-08T15:04:00Z",
  "pumpfun": {
    "total": 590,
    "totalIs": "positions",
    "holders": [
      { "rank": 1, "handle": "catdad", "valueUsd": 4210, "thesis": "Here since 20k ..." }
    ]
  },
  "fomo": { "total": 212, "totalIs": "holders", "holders": [ ... ] }
}
GET

/v1/jobs/{job}

free

Poll a read that was still going when /coins answered 202. While it runs you get 202 again, with retryAfter. When it is done you get exactly what /coins would have returned. The read is charged once, when it finishes, and a read that fails is never charged. Jobs are kept for 15 minutes.

GET

/v1/usage

free

Your plan, what is left, and when it comes back. Call it as often as you like: it only counts toward the speed limit.

200 OK
{
  "account": { "platform": "discord", "plan": "pro", "expiresAt": "2026-11-08T14:02:00Z" },
  "day": { "used": 6, "limit": 15, "resetsAt": "2026-10-09T00:00:00Z" },
  "month": { "used": 58, "limit": 200, "resetsAt": "2026-10-21T09:14:00Z", "throughApi": 23 },
  "perMinute": { "limit": 10, "remaining": 9 },
  "keys": 2
}

What counts as a read

  • One read per coin, per account, per day. Asking for a coin again the same day is free, wherever you read it first: /dd or the API, on the same account.
  • A fresh read is always charged. fresh=true here, or Re-read in the bot, pays for a new read even on a coin you read today.
  • A read that fails is never charged. If a coin cannot be placed or nothing could be read, nothing is taken.
  • Holders, jobs and usage never spend a read. They only count toward the speed limit.
  • Reads are shared with the bot. Pro's 200 a month is 200 between /dd and the API together.

A day ends at midnight UTC. A month is 30 days, counted from when you first bought your plan, and changing plan never restarts it.

Limits

Plan Reads a day Reads a month Requests a minute
Pro · $40 a month 15 200 10
Max · $99 a month 50 1,000 30

The speed limit is per account, across all its keys. Every response says what is left, so you never have to guess:

X-Reads-Day-Remaining: 9
X-Reads-Month-Remaining: 142
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
Retry-After: 6              (only on a 429)

Errors

Every error is JSON, with a code to switch on and a sentence a person can read.

{
  "error": {
    "code": "DAILY_LIMIT",
    "message": "You have used today's 15 reads. They come back at midnight UTC.",
    "resetsAt": "2026-10-09T00:00:00Z"
  }
}
Status Code When
400BAD_ADDRESSThat is not a coin address on any chain this reads.
401INVALID_KEYThe key is missing, mistyped or revoked.
403PLAN_REQUIREDThe account is on Free, or its plan ran out. Its keys work again once it is renewed.
403ACCOUNT_BLOCKEDThe account was shut out by the bot's owner. Nothing was charged.
404JOB_NOT_FOUNDNo such job, or it is older than 15 minutes.
405METHOD_NOT_ALLOWEDAnything but GET on a /v1 route, HEAD included. Nothing was read or charged.
422COIN_NOT_PLACEDA 0x address no supported chain knows. Pass chain if you know it.
429RATE_LIMITEDOver your requests a minute. Wait retryAfter seconds.
429DAILY_LIMITToday's reads are used. resetsAt says when they come back.
429MONTHLY_LIMITThis month's reads are used. resetsAt says when they come back.
429COOLDOWNfresh=true on a coin read fresh in the last 5 minutes. The answer would be the same anyway.
503UNAVAILABLEThe API is down or paused. Nothing was charged.
tool resultNOT_READcoin_links or coin_sources on a coin this account has not read. Call read_coin first.
tool resultBAD_QUERYfind_coin with fewer than two characters, or a chain it does not know.

Use it from an AI agent

The API is also an MCP server, so Claude and other agents can read coins for you with the same key, the same reads and the same limits. Seven tools; only read_coin spends a read, and only tool calls count toward the speed limit. An agent waits out a fresh read itself, so there is no job to poll.

One prompt
Connect me to Shady's Research Bot, an MCP server that reads what a coin's holders say about it.

  Name:       shadysresearchbot
  Transport:  Streamable HTTP
  URL:        https://api.shadysresearchbot.com/mcp
  Header:     Authorization: Bearer srb_YOUR_KEY

If you are Claude Code, Cursor, Codex or another client that can edit its own config, add it yourself:
  Claude Code:  claude mcp add --transport http --scope user shadysresearchbot https://api.shadysresearchbot.com/mcp --header "Authorization: Bearer srb_YOUR_KEY"
  mcp.json:     {"mcpServers": {"shadysresearchbot": {"type": "http", "url": "https://api.shadysresearchbot.com/mcp", "headers": {"Authorization": "Bearer srb_YOUR_KEY"}}}}
Then call its get_usage tool and tell me which plan I am on and how many reads I have left today.

If you are claude.ai or Claude Desktop, you cannot add connectors yourself, so walk me through it step by step:
  1. Open Customize, then Connectors, press + Add, then Add custom connector.
  2. Name: Shady's Research Bot
  3. URL: https://api.shadysresearchbot.com/mcp/srb_YOUR_KEY
  4. Authentication: No sign-in. Leave every OAuth field empty.
  5. Add it, start a new chat, and ask me to call get_usage.
That URL carries my key, so treat it like a password.

Do not commit the key or write it into any file tracked by git.

Paste it into Claude Code, Cursor or Codex and the agent adds the server itself, then calls get_usage. Paste it into claude.ai or Claude Desktop and Claude walks you through adding it as a custom connector. Replace ddb_YOUR_KEY with your key: the Key created screen in the bot shows this prompt with your key already in it.

claude.ai and Claude Desktop

  1. Open Customize, then Connectors, press + Add, then Add custom connector.
  2. Name: Shady's Research Bot
  3. URL: your personal connector URL
  4. Authentication: No sign-in. Leave every OAuth field empty.
  5. Add it, start a new chat, and ask Claude to call get_usage.

Your personal connector URL is https://api.shadysresearchbot.com/mcp/<your key>. The key rides in the address because those apps cannot send a header without a sign-in flow, so treat the URL like a password. Revoking the key from /api kills it at once, and a bad or revoked key answers 401. GET and DELETE on it answer 405.

By hand

Claude Code
claude mcp add --transport http --scope user shadysresearchbot https://api.shadysresearchbot.com/mcp --header "Authorization: Bearer srb_YOUR_KEY"
Any MCP client
{
  "mcpServers": {
    "shadysresearchbot": {
      "type": "http",
      "url": "https://api.shadysresearchbot.com/mcp",
      "headers": {
        "Authorization": "Bearer srb_YOUR_KEY"
      }
    }
  }
}

The tools

Tool What it does Costs
read_coin A coin's compact report: headline, story, what it does and who is behind it as holders tell it, why they are in, what argues against, counts, and what its own pages say. 1 read
coin_holders The holders who also posted, with their rank on each app's own list and their own words. free
coin_links Every link holders shared, ranked by people before mentions, a page at a time. After read_coin. free
coin_sources The coin's own site and X account as read for the report, what could not be read and why, and which claims rest on them. After read_coin. free
find_coin Candidate addresses for a ticker, a name or the start of an address, from the coins the bot already holds posts for. free
recent_reads The coins this account read lately, and which are free to ask again today. free
get_usage The plan, the reads left today and this month, and the speed limit. free

A refused tool call is a result the agent can read, not a transport error: a JSON body with code and message, the same codes as above, plus NOT_READ for coin_links and coin_sources on a coin this account has not read yet, and BAD_QUERY for a find_coin query it cannot search. A bad key is still 401 before anything runs.