Watch X for contract addresses
Let the bot find a token's contract address in posts on X, check the token, and buy a small amount.
The bot watches X (accounts that you choose, or a search across all of X) for a token's contract address. It checks the token, buys a small amount, and exits on your usual rules. This guide shows three ways to build it, from the simplest to the fastest.
| Way | Tier | Speed | What you need |
|---|---|---|---|
| A. Grok searches X on a timer | Epic | every 1 to 5 minutes, plus a few seconds | an xAI API key |
| B. The X MCP server on a timer | Epic | every 1 to 5 minutes, plus a few seconds | an X API bearer token (pay-per-use), and any AI key |
| C. Your own service calls the bot's webhook | Legendary | seconds after your service sees the post | a service of your own that watches X (or anything else) |
Every way ends the same. The bot looks up the token's market (a Pons or Torch token on its launchpad, any other token in its pools on UP and Uniswap). It checks your rules with the token's own values, and buys only if they hold. With Once per token on, a token is bought at most once, however often it is posted.
Be careful
Posts on X are untrusted. Anyone can post a scam address in the hope that a bot buys it, and accounts are copied and hacked. Keep the buy small, add a check step (below), keep your live trading limits low, and run on paper first. AI strategies cannot be backtested: what a model would have answered on past days cannot be known. This guide shows how to build a strategy, not one that is known to make money.
A. Grok searches X (simplest)
Add your xAI key
On Settings, in AI keys, add your xAI (Grok) key. See AI keys.
Set the trigger
On the Spinner's Strategy tab, open the When block. In Start when, choose On a timer. Set Every to 2 minutes.
Add the AI step
Add an AI step:
- Step name:
watch. - AI provider: xAI (Grok).
- Search X on. In Only posts from, the accounts that you trust (up to 20). In Posts from, The last day.
- Answer fields:
token, which holds A token's address, to buy (or none found), andconfident, which holds Yes or no.
Set the token and the check
- In the outcome block, set The token to buy to "The token the step "watch" names".
- In Then buy if:
ai.watch.confidentis yes, andtoken.liquidityis at least 3 ETH. (The token's market has at least that much ETH. A scam posted to be bought rarely has.) - Buy: a small amount, in ETH or in dollars with the ETH or $ switch.
Add exits and limits
- Exits: a take profit, a stop loss and a time exit.
- Limits: Once per token on. Set AI runs an hour at most to 30 or more: a 2-minute timer runs 30 times an hour.
The same strategy in the JSON tab:
{
"schema": 1,
"name": "X contract watch",
"entry": {
"trigger": { "on": "timer", "every_s": 120 },
"target": { "token": "{{watch.token}}" },
"ai": {
"steps": [
{
"id": "watch",
"provider": "xai",
"search": { "x": { "handles": ["@account_one", "@account_two"], "days": 1 } },
"prompt": "Has any of these accounts posted a token contract address on Robinhood Chain in the last 5 minutes? Give the newest one exactly as posted, or null if none. Say whether you are confident it is the account's own call, not a reply or a quote of someone else.",
"output": { "token": "token", "confident": "boolean" }
}
]
},
"confirm": { "and": [{ "==": [{ "f": "ai.watch.confident" }, true] }, { ">=": [{ "f": "token.liquidity" }, 3] }] },
"buy_eth": 0.005
},
"exits": [
{ "if": { ">=": [{ "f": "position.multiple" }, 2] }, "reason": "take_profit", "sell_pct": 50 },
{ "if": { "<=": [{ "f": "position.pnl_pct" }, -30] }, "reason": "stop_loss" },
{ "if": { ">=": [{ "f": "position.age_s" }, 7200] }, "reason": "time" }
],
"limits": { "once_per_token": true, "max_open": 3, "max_ai_runs_per_hour": 30 }
}Cost: each run is one Grok request with X search, on your xAI key. See xAI's pricing. A 2-minute timer is 720 runs a day.
B. The X MCP server (exact posts from exact accounts)
Grok's search filters posts by day, and it chooses what to search. X's own MCP server returns the exact posts of the accounts that you name, with their times.
Get a bearer token
In the X developer portal, create an app on the pay-per-use plan. Copy its bearer token (app-only: it can read, never post).
Add the X server
On Settings, in MCP servers, click Add X. Paste the bearer token into API key or token, then click Connect and save. The bot gets X's read tools only: accounts, an account's posts and mentions, post search and counts, trends and news. It gets nothing that posts or sends. See MCP servers.
Change the step
Build the strategy as in A, but change the watch step: any provider, no search, and the X server as its tool. Its prompt names the accounts.
{
"id": "watch",
"tools": ["x"],
"prompt": "Read the latest posts of @account_one and @account_two from the last 5 minutes. Has either posted a token contract address on Robinhood Chain? Give the newest one exactly as posted, or null if none. Say whether you are confident it is the account's own call, not a reply or a quote of someone else.",
"output": { "token": "token", "confident": "boolean" }
}Cost: X bills each read on your bearer token (see X's pricing). The AI step costs the same as any other step on your key.
Add a check step (recommended for A and B)
Add a second step that checks the token before the bot buys it, with a rug-risk MCP server. DeFade is one click in MCP servers.
{
"id": "check",
"tools": ["defade"],
"if": { "==": [{ "f": "ai.watch.confident" }, true] },
"prompt": "Check token {{watch.token}} on Robinhood Chain: honeypot, sell tax, holders, snipers, deployer history. Is it safe to buy a small amount?",
"output": { "risk": ["low", "medium", "high"] }
}Then buy only on a low risk:
"confirm": { "and": [{ "==": [{ "f": "ai.watch.confident" }, true] }, { "==": [{ "f": "ai.check.risk" }, "low"] }, { ">=": [{ "f": "token.liquidity" }, 3] }] }The check step runs only when the first step found something (its if), so most runs cost one request. For a token that is not from a launchpad, a step can also read its contract on Blockscout: see MCP data sources.
Rules can also read the token's own values once its market is known:
token.liquidity: the ETH in its market. It is the real ETH on a Pons curve, in a Torch pool, in a graduated Pons pool and in a v2-style pool. For other Uniswap and UP pools, it is the ETH depth near the price, which a narrow position can make look deeper than it is.token.buy_tax: a Torch token's tax.token.market_cap.
A brand-new token's liquidity is known at once. See conditions for every value.
C. Your own service and the bot's webhook (Legendary)
For speed, run your own watcher (X's filtered stream, a Telegram listener, a chart alert) and let it call the bot the moment it sees something.
Set up the strategy
In Start when, choose My own service calls its webhook (Legendary). Declare the fields that it sends, for example token (a token's address) and score (a number from 0 to 1). Set The token to buy to "The token your service sends". Add a rule such as hook.score >= 0.7. You can still add AI steps: their prompts can use {{hook.token}}.
Make a secret
Click Save as new version. On the Webhook card, copy the address and click Make a secret. The card shows the secret only once. Keep it in your service's settings, never in its code.
Sign each call
// Node.js
import crypto from "node:crypto";
const body = JSON.stringify({ token: "0x0000000000000000000000000000000000000001", score: 0.9 });
const ts = Math.floor(Date.now() / 1000).toString();
const sig = "v1=" + crypto.createHmac("sha256", process.env.SPIN_HOOK_SECRET).update(`${ts}.${body}`).digest("hex");
const res = await fetch("https://spin.trading/api/hooks/123", {
method: "POST",
headers: { "content-type": "application/json", "x-spin-timestamp": ts, "x-spin-signature": sig, "x-spin-event-id": postId },
body,
});
// 202: accepted. The bot starts the entry within seconds.Sign the exact bytes that you send: the signature covers the body as sent.
The same strategy in JSON:
{
"schema": 1,
"name": "My X watcher",
"entry": {
"trigger": { "on": "webhook", "fields": { "token": "token", "score": { "number": [0, 1] } } },
"target": { "token": "{{hook.token}}" },
"if": { "and": [{ ">=": [{ "f": "hook.score" }, 0.7] }, { ">=": [{ "f": "token.liquidity" }, 3] }] },
"buy_eth": 0.005
},
"exits": [
{ "if": { ">=": [{ "f": "position.multiple" }, 2] }, "reason": "take_profit", "sell_pct": 50 },
{ "if": { "<=": [{ "f": "position.pnl_pct" }, -30] }, "reason": "stop_loss" },
{ "if": { ">=": [{ "f": "position.age_s" }, 7200] }, "reason": "time" }
],
"limits": { "once_per_token": true, "max_open": 3 }
}Webhooks has the signing in curl and Python, every answer the webhook can give, and its limits. In short:
- The webhook refuses a call with a wrong signature, a timestamp more than 5 minutes from now, fields that the strategy does not declare, a body over 8,192 bytes, or more than 30 calls a minute or 2,000 a day.
- An event id sent twice is taken once.
- A new secret ends the old one at once, and a sale of the Spinner ends it too.
- A call can only start an entry. It cannot move or withdraw funds. Every buy still passes the strategy's rules, its limits, and the live trading limits on the Spinner's guard.
- A call that is more than 5 minutes old when the bot gets to it is dropped.
Reading the activity
The Activity tab shows each run's AI answers, and why the bot did or did not buy:
| The activity says | Meaning |
|---|---|
| ai: named no token | The step found no address. |
| no market found for the token the AI named (or the webhook named) | No curve or pool that the bots can trade appeared within a minute. |
| confirm failed | A rule said no (for example, the risk check). |
| already entered | The bot bought this token before (Once per token). |
| max_ai_runs_per_hour reached | Raise AI runs an hour at most, or make the timer slower. |