# TrenchyTools API
> Build, sign locally and send trades and launches on pump.fun, PumpSwap (Solana) and pons (Robinhood chain). Jito bundles, lookup tables, creator fees, quotes, balances and a live websocket feed from our own node.
Base URL: https://api.trenchytools.lol
---
# Introduction
Source: https://trenchytools-docs-production.up.railway.app/docs
Transaction building, sending, bundles, quotes, balances, creator fees and live market data for pump.fun, PumpSwap and pons.
A trading and launch API for **Solana** (pump.fun, PumpSwap) and **Robinhood chain** (pons). You send a JSON request; the API resolves the venue from the token address, reads live curve or pool state, builds the transaction with slippage and priority fee, and returns it **unsigned**. You sign locally and submit through `send` (our RPC + Jito lanes) or your own node. Keys never leave your process.
**Building with an AI assistant?** Give it [AGENTS.md](https://trenchytools-docs-production.up.railway.app/AGENTS.md), [openapi.json](https://trenchytools-docs-production.up.railway.app/openapi.json) and [llms-full.txt](https://trenchytools-docs-production.up.railway.app/llms-full.txt), or paste a ready prompt from [Build with AI](/docs/build-with-ai).
## What you can build
`trade-local` → sign → `send`. Curve or AMM resolved per token; `pool` overrides.`create` with metadata URI and dev buy in one transaction; mint keypair co-signs.Creates, migrations, per-token and per-wallet trades over one websocket, straight from our node.Create + up to 16 buyers in one Jito bundle via launch lookup tables.
## Chains and launchpads
One prefix per chain, the venue resolved from the token address (`pool` forces one). Lit marks are live; dim ones are on the roadmap, in this order.
## Request flow
`POST /api/{chain}/trade-local` returns an unsigned transaction (Solana: one v0 tx; EVM: an ordered `transactions[]`), priced from current on-chain state with your `slippage` and `priorityFee` applied.
Sign with your own key (web3.js / viem / ethers). The API never receives it.
`POST /api/{chain}/send` submits through our RPC and Jito lanes and returns the signature; or send through any node yourself. Bundles go to `POST /api/sol/bundle`.
`GET /tx/:signature`, `GET /bundle/:id`, and the `/data` websocket for creates, migrations and trades.
## Chain prefixes
One prefix per chain, same verbs on each. Account endpoints (`create-api-key`, `me`, `ipfs`, `health`) are shared.
| | Solana | Robinhood chain |
| --- | --- | --- |
| Launchpads | pump.fun bonding curve, PumpSwap after graduation | pons bonding curve, Uniswap V4 after graduation |
| Prefix | `/api/sol` | `/api/rh` |
| Fields | `publicKey`, `mint`, `denominatedInSol` | `wallet`, `token`, `denominatedInEth` |
| `trade-local` returns | one unsigned v0 transaction (raw bytes, or JSON with `?format=json`) | ordered `transactions[]` with `label`, `to`, `data`, `value`, `gas` |
| Send | `/api/sol/send`, `/api/sol/bundle` (Jito) | `/api/rh/send`, or any RPC |
---
# Quickstart
Source: https://trenchytools-docs-production.up.railway.app/docs/quickstart
Key → quote → build → sign → send.
Node 18+ or Python 3.9+, a funded wallet, and an API key.
Create one at [API keys](/docs/api-keys). Shown once; pass it as `?api-key=` or the `x-api-key` header.
No wallet required.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/quote?mint=MINT_ADDRESS&action=buy&amount=0.1&api-key=YOUR_API_KEY"
```
```bash title="cURL"
curl "https://api.trenchytools.lol/api/rh/quote?token=TOKEN_ADDRESS&action=buy&amount=0.01&api-key=YOUR_API_KEY"
```
Solana uses `@solana/web3.js` to sign. Robinhood uses `viem`.
```bash title="Terminal"
npm install @solana/web3.js bs58
```
```bash title="Terminal"
npm install viem
```
Copy the full example from the trade page and replace the placeholders.
---
# Build with AI
Source: https://trenchytools-docs-production.up.railway.app/docs/build-with-ai
Paste one prompt into Claude Code, Cursor or Codex; the assistant builds against the real API from machine-readable docs.
The API ships three files for coding agents. A prompt that points the assistant at them is enough to get a working bot without reading these docs yourself.
**Backups are a rule, not advice.** Every prompt here makes the assistant write each generated wallet to disk before funding it, and refuse to fund one that is not saved. Most lost wallets are keys that only ever existed in memory or a terminal. See [Wallet backups](/docs/wallets/backups).
| File | What it is |
| --- | --- |
| [AGENTS.md](https://trenchytools-docs-production.up.railway.app/AGENTS.md) | The rules: keys stay local, only real endpoints, simulate first, handle errors by `code`. |
| [openapi.json](https://trenchytools-docs-production.up.railway.app/openapi.json) | Every endpoint, field, response and error, generated from these docs and checked against the routes. |
| [llms-full.txt](https://trenchytools-docs-production.up.railway.app/llms-full.txt) | All pages as plain text, for the assistant to read in one go. |
| `GET https://api.trenchytools.lol/api/chains` | Live capability discovery: chains, launchpads, operations, what is live. |
Every error the API returns carries `code`, `hint` and a `docs` link, so an assistant can fix its own mistakes without guessing.
## Prompts
Copy one, edit the numbers, paste it into your assistant. Each starts with the same rules block.
```text title="Sniper bot"
Build a Solana sniper bot on the TrenchyTools API.
Before writing any code, read these three documents completely:
- https://trenchytools-docs-production.up.railway.app/AGENTS.md rules for building on this API
- https://trenchytools-docs-production.up.railway.app/openapi.json every endpoint, field, response and error code
- https://trenchytools-docs-production.up.railway.app/llms-full.txt the full documentation as text
Rules you must follow:
- Use only endpoints and fields that appear in openapi.json. Never invent an endpoint, field or response key. If something is missing, say so.
- Private keys never leave this machine. Build with trade-local, sign locally, submit with send. Never use the Lightning (trade) endpoints.
- Every wallet the app generates (trading, funding, bundle wallets, vanity mint keys) must be written to disk BEFORE it is funded: a wallets/ folder next to the project (one JSON per wallet: address, secret, createdAt, label) or .env, and print the absolute path. Refuse to fund a wallet that is not on disk. Never keep keys only in memory or a log.
- Secrets come from .env (TRENCHY_API_KEY, SOL_PRIVATE_KEY). Never hard-code or print them. Write a .env.example.
- Create the API key with GET https://api.trenchytools.lol/api/create-api-key?label= and tell me to put it in .env.
- Simulate with POST /api/sol/simulate before the first real send. Default to DRY_RUN=1 and tiny sizes (0.005 SOL).
- Handle every API error by its "code" and show me its "hint" and "docs" link. Never retry a send without checking /tx first.
- Deliverable: a TypeScript Node 20 project with README, .env.example, npm start, a dry-run mode, and clear logs.
What it must do:
- Connect to wss://…/api/sol/data and subscribe to subscribeNewToken.
- For every new pump.fun coin that passes my filters (name/symbol keywords, dev buy ≥ X SOL, no more than Y holders in the first Z seconds), build a buy with POST /api/sol/trade-local (pool "pump", slippage 25, priorityFee from GET /api/sol/priority-fee "high"), sign it, send it with POST /api/sol/send, confirm with GET /api/sol/tx/:signature.
- Subscribe to subscribeTokenTrade for each position and sell 100% when price is +X% or −Y% from entry, or after N minutes.
- Keep positions in a JSON file so a restart does not lose them. Log every decision.
```
```text title="Token launcher with bundle"
Build a pump.fun token launcher with a bundled first buy on the TrenchyTools API.
Before writing any code, read these three documents completely:
- https://trenchytools-docs-production.up.railway.app/AGENTS.md rules for building on this API
- https://trenchytools-docs-production.up.railway.app/openapi.json every endpoint, field, response and error code
- https://trenchytools-docs-production.up.railway.app/llms-full.txt the full documentation as text
Rules you must follow:
- Use only endpoints and fields that appear in openapi.json. Never invent an endpoint, field or response key. If something is missing, say so.
- Private keys never leave this machine. Build with trade-local, sign locally, submit with send. Never use the Lightning (trade) endpoints.
- Every wallet the app generates (trading, funding, bundle wallets, vanity mint keys) must be written to disk BEFORE it is funded: a wallets/ folder next to the project (one JSON per wallet: address, secret, createdAt, label) or .env, and print the absolute path. Refuse to fund a wallet that is not on disk. Never keep keys only in memory or a log.
- Secrets come from .env (TRENCHY_API_KEY, SOL_PRIVATE_KEY). Never hard-code or print them. Write a .env.example.
- Create the API key with GET https://api.trenchytools.lol/api/create-api-key?label= and tell me to put it in .env.
- Simulate with POST /api/sol/simulate before the first real send. Default to DRY_RUN=1 and tiny sizes (0.005 SOL).
- Handle every API error by its "code" and show me its "hint" and "docs" link. Never retry a send without checking /tx first.
- Deliverable: a TypeScript Node 20 project with README, .env.example, npm start, a dry-run mode, and clear logs.
What it must do:
- Take name, symbol, description, image path, twitter/telegram/website, dev buy amount and a list of buyer wallets (private keys from .env, up to 16) as config.
- Upload the image and metadata with POST /api/ipfs to get metadataUri.
- Generate the mint keypair locally. If there are more than 4 buyers, create a lookup table with POST /api/sol/lut first and wait until it is ready.
- Build the bundle: array body to POST /api/sol/trade-local — the create (with dev buy) first, then buys (use buyers[] entries with the lookup table). The first object's priorityFee is the Jito tip (read GET /api/sol/priority-fee for the current tip floor).
- Sign each transaction with its wallet (the create also with the mint keypair), send with POST /api/sol/bundle, poll GET /api/sol/bundle/:id, then confirm with GET /api/sol/tx/:signature.
- Print the mint address, each wallet's expected tokens (perBuyer from ?format=json), and what landed.
```
```text title="Take-profit / stop-loss bot"
Build a take-profit and stop-loss bot for coins I already hold on Solana, on the TrenchyTools API.
Before writing any code, read these three documents completely:
- https://trenchytools-docs-production.up.railway.app/AGENTS.md rules for building on this API
- https://trenchytools-docs-production.up.railway.app/openapi.json every endpoint, field, response and error code
- https://trenchytools-docs-production.up.railway.app/llms-full.txt the full documentation as text
Rules you must follow:
- Use only endpoints and fields that appear in openapi.json. Never invent an endpoint, field or response key. If something is missing, say so.
- Private keys never leave this machine. Build with trade-local, sign locally, submit with send. Never use the Lightning (trade) endpoints.
- Every wallet the app generates (trading, funding, bundle wallets, vanity mint keys) must be written to disk BEFORE it is funded: a wallets/ folder next to the project (one JSON per wallet: address, secret, createdAt, label) or .env, and print the absolute path. Refuse to fund a wallet that is not on disk. Never keep keys only in memory or a log.
- Secrets come from .env (TRENCHY_API_KEY, SOL_PRIVATE_KEY). Never hard-code or print them. Write a .env.example.
- Create the API key with GET https://api.trenchytools.lol/api/create-api-key?label= and tell me to put it in .env.
- Simulate with POST /api/sol/simulate before the first real send. Default to DRY_RUN=1 and tiny sizes (0.005 SOL).
- Handle every API error by its "code" and show me its "hint" and "docs" link. Never retry a send without checking /tx first.
- Deliverable: a TypeScript Node 20 project with README, .env.example, npm start, a dry-run mode, and clear logs.
What it must do:
- Read my wallet from .env and my positions from a config file (mint, entry price in SOL, take-profit %, stop-loss %, trailing %).
- Subscribe to subscribeTokenTrade for each mint on wss://…/api/sol/data and keep the latest price from the trades; also poll GET /api/sol/quote every 15 s as a fallback.
- When a rule triggers, sell with POST /api/sol/trade-local (action sell, amount "100%" or the configured share, pool "auto"), sign, send, confirm.
- Never sell twice: mark the position done the moment a send succeeds and only reopen it if /tx says the transaction failed.
- Send me a one-line summary per action (Telegram webhook URL from .env, optional).
```
```text title="Copy-trader"
Build a copy-trading bot on the TrenchyTools API.
Before writing any code, read these three documents completely:
- https://trenchytools-docs-production.up.railway.app/AGENTS.md rules for building on this API
- https://trenchytools-docs-production.up.railway.app/openapi.json every endpoint, field, response and error code
- https://trenchytools-docs-production.up.railway.app/llms-full.txt the full documentation as text
Rules you must follow:
- Use only endpoints and fields that appear in openapi.json. Never invent an endpoint, field or response key. If something is missing, say so.
- Private keys never leave this machine. Build with trade-local, sign locally, submit with send. Never use the Lightning (trade) endpoints.
- Every wallet the app generates (trading, funding, bundle wallets, vanity mint keys) must be written to disk BEFORE it is funded: a wallets/ folder next to the project (one JSON per wallet: address, secret, createdAt, label) or .env, and print the absolute path. Refuse to fund a wallet that is not on disk. Never keep keys only in memory or a log.
- Secrets come from .env (TRENCHY_API_KEY, SOL_PRIVATE_KEY). Never hard-code or print them. Write a .env.example.
- Create the API key with GET https://api.trenchytools.lol/api/create-api-key?label= and tell me to put it in .env.
- Simulate with POST /api/sol/simulate before the first real send. Default to DRY_RUN=1 and tiny sizes (0.005 SOL).
- Handle every API error by its "code" and show me its "hint" and "docs" link. Never retry a send without checking /tx first.
- Deliverable: a TypeScript Node 20 project with README, .env.example, npm start, a dry-run mode, and clear logs.
What it must do:
- Watch a list of wallets with subscribeAccountTrade on wss://…/api/sol/data.
- When a watched wallet buys a pump.fun coin, buy the same coin with a fixed SOL size (config), slippage 20, pool "auto"; when it sells, sell the same share of my position.
- Ignore coins older than N minutes (GET /api/sol/token/:mint) and skip if my balance is below a floor (GET /api/sol/balances).
- Keep a ledger of copied trades with the leader's signature and mine; expose a tiny status page on localhost.
```
```text title="Robinhood chain (pons) bot"
Build a trading bot for pons coins on Robinhood chain on the TrenchyTools API.
Before writing any code, read these three documents completely:
- https://trenchytools-docs-production.up.railway.app/AGENTS.md rules for building on this API
- https://trenchytools-docs-production.up.railway.app/openapi.json every endpoint, field, response and error code
- https://trenchytools-docs-production.up.railway.app/llms-full.txt the full documentation as text
Rules you must follow:
- Use only endpoints and fields that appear in openapi.json. Never invent an endpoint, field or response key. If something is missing, say so.
- Private keys never leave this machine. Build with trade-local, sign locally, submit with send. Never use the Lightning (trade) endpoints.
- Every wallet the app generates (trading, funding, bundle wallets, vanity mint keys) must be written to disk BEFORE it is funded: a wallets/ folder next to the project (one JSON per wallet: address, secret, createdAt, label) or .env, and print the absolute path. Refuse to fund a wallet that is not on disk. Never keep keys only in memory or a log.
- Secrets come from .env (TRENCHY_API_KEY, SOL_PRIVATE_KEY). Never hard-code or print them. Write a .env.example.
- Create the API key with GET https://api.trenchytools.lol/api/create-api-key?label= and tell me to put it in .env.
- Simulate with POST /api/sol/simulate before the first real send. Default to DRY_RUN=1 and tiny sizes (0.005 SOL).
- Handle every API error by its "code" and show me its "hint" and "docs" link. Never retry a send without checking /tx first.
- Deliverable: a TypeScript Node 20 project with README, .env.example, npm start, a dry-run mode, and clear logs.
What it must do:
- Use the /api/rh routes: trade-local returns an ordered transactions[] (approve → sell, graduate → buy); send them in order with viem and wait for the approve receipt before the sell.
- Watch wss://…/api/rh/data subscribeNewToken for launches and buy with a fixed ETH size in the first N seconds (slippage 10), then sell on +X% using GET /api/rh/quote with my wallet as holder.
- Everything else as in the rules above (dry-run, simulate with POST /api/rh/simulate, error codes).
```
## Before you trust it
Run with `DRY_RUN=1`: it should build and simulate (`POST /api/{chain}/simulate`) and send nothing.
First real run with 0.005 SOL and one coin. Check `GET /api/sol/tx/:signature` and your wallet.
Every `code` is listed on [Errors](/docs/errors) with what to change. Paste the error back to the assistant with its `docs` link.
Generated code is a draft until it has run against real transactions at small size. Nothing here guarantees a landing or a profit.
---
# Example repos
Source: https://trenchytools-docs-production.up.railway.app/docs/examples
Working projects on this API to clone, run and hand to your assistant as the pattern to follow.
Every example is TypeScript, runs with `npm start`, reads its secrets from `.env` (template in `.env.example`), has a `DRY_RUN=1` mode that builds and simulates without sending, and logs every API error `code`. An assistant that reads one of them produces the same shape.
| Repo | What it does | Status |
| --- | --- | --- |
| **pumpfun-launcher** | Full launcher UI: wallets, metadata upload, create + up to 16 bundled buyers via lookup tables, live trades, sells, creator fees. Backend talks only to this API. | Working; in this repository under `demos/pumpfun-launcher`. Public GitHub repo being prepared. |
| **sniper-bot** | Watches `subscribeNewToken`, filters, buys, takes profit / stops out, persists positions. | Coming. Until then: the [Build with AI](/docs/build-with-ai) sniper prompt produces it. |
| **take-profit-bot** | Take-profit, stop-loss and trailing rules for coins you already hold. | Coming (prompt available). |
| **copy-trader** | Mirrors buys and sells of watched wallets with `subscribeAccountTrade`. | Coming (prompt available). |
| **pons-bot** | Robinhood chain: launches and trades on pons through the ordered-transaction flow. | Coming (prompt available). |
| **sdk** | `@trenchytools/sdk`: typed client generated from `openapi.json`, retries, 429 handling, websocket helper. | In this repository under `packages/sdk` (0.1, not yet published). |
GitHub links land here as each repo is published. Until then, the prompts on [Build with AI](/docs/build-with-ai) plus [openapi.json](https://trenchytools-docs-production.up.railway.app/openapi.json) are enough for an assistant to generate any of them.
## What an example must have
- TypeScript, Node 20+, `npm start`
- `README.md` with setup in five steps
- `.env.example` with every variable; no secret anywhere else
- `DRY_RUN=1`: build + simulate, never send
- One place that maps API error `code` → what the bot does next
- No private key ever leaves the process; no Lightning (custodial) endpoints
---
# Get an API key
Source: https://trenchytools-docs-production.up.railway.app/docs/api-keys
Create your own API key in one click, see your usage, or revoke a key.
Every request needs an API key. It tells us who is calling, so we can show you your usage and keep the service fast for everyone. Keys are free to create.
## Using your key
Add it to the URL as `?api-key=YOUR_API_KEY`, or send it in an `x-api-key` header. Both work on every endpoint, including the websockets.
**Treat your key like a password.** Anyone with it can spend your request limits, and with a linked wallet, trade that wallet's funds. Never paste it into a public repo or a frontend that ships to users.
## Key only, or key with wallets?
| Option | Best for | Who signs |
| --- | --- | --- |
| **Key only** (recommended) | Bots that already have their own wallets | You sign every transaction with your own key |
| **Key + new wallets** | Getting started fast, or one wallet per user | You, with the wallet keys we show you once. If Lightning is enabled, we can also sign for you |
**Key + new wallets** shows the wallet secrets once. Download the .txt, store it in a password manager or an offline copy, and only then fund the wallets. The API keeps nothing it could give back. [Wallet backups](/docs/wallets/backups).
---
# Core concepts
Source: https://trenchytools-docs-production.up.railway.app/docs/concepts
Terms as this API uses them.
**Local** (`trade-local`): the API builds, you sign and send. **Lightning** (`trade`): the API builds, signs with a wallet bound to your key, and sends. Fees: 0.5 % local, 1 % lightning, on the SOL/ETH side of the trade.Venue selector. Solana: `pump` (curve, default), `pump-amm` (PumpSwap), `auto` (resolved from on-chain state per request). Robinhood: resolved per request, no field needed.`denominatedInSol: true` → `amount` is SOL (ETH on EVM); `false` → `amount` is tokens. Sells accept `"100%"` or any `"N%"` of the wallet balance, read at build time.Percent. Applied to the quote the API computes from live reserves when building; the on-chain guard is the resulting min-out / max-in. Over-tight slippage surfaces as pump.fun errors 6002/6003.In SOL. Converted to a compute-unit price against the tx's unit limit; `GET /api/sol/priority-fee` returns current percentiles. On Jito bundles it also sets the tip.Curve → AMM move: PumpSwap on Solana (the websocket emits `migration` with the pool), Uniswap v4 MemeHook on Robinhood (a `graduate` entry may precede your trade).`POST /api/sol/bundle`: ≤5 transactions, atomic, same slot, ordered. Multi-buyer entries pack ~4 buyers per tx via a lookup table (`POST /api/sol/lut`) → up to 16 buyers plus the create.The creator's buy inside the `create` transaction (`amount` on the create entry). Priced against the fresh curve; the API simulates the curve forward for subsequent buyers in the same bundle.pump.fun / PumpSwap creator revenue. `GET /api/sol/creator-fees?wallet=` reports what is claimable; `action: "collectCreatorFee"` builds the claim.Launch-window tax on pons buys, decaying to zero over the first blocks. Addresses in the create call's exempt list skip it.
---
# Solana overview
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/overview
pump.fun curve and PumpSwap AMM: routing, pools, trade flow.
pump.fun bonding-curve coins and graduated coins on PumpSwap. `pool`: `pump` (curve), `pump-amm` (PumpSwap), `auto` (resolved from chain state).
| You want to… | Use |
| --- | --- |
| Buy or sell | [POST /api/sol/trade-local](/docs/sol/trade) |
| Launch a coin | [Create a token](/docs/sol/create) |
| Launch and buy from several wallets at once | [Jito bundles](/docs/sol/bundles) |
| Send a signed transaction fast | [POST /api/sol/send](/docs/sol/send) |
| See a price before trading | [GET /api/sol/quote](/docs/sol/quote) |
| Watch new coins and trades live | [Websocket](/docs/sol/websocket) |
Root aliases: `/api/trade-local`, `/api/trade`, `/api/data` resolve to the Solana routes.
---
# Buy & sell
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/trade
Get an unsigned buy or sell transaction for your wallet, then sign and send it.
Builds a transaction that buys or sells a coin from **your** wallet. We never see your private key: you sign the transaction yourself, then send it with [/api/sol/send](/docs/sol/send).
## Request fields
Your wallet address (base58).`buy` or `sell`.The coin's address.How much. SOL if `denominatedInSol` is `"true"`, tokens if `"false"`. Sells also accept a share of your balance, like `"100%"` or `"50%"`.Whether `amount` is in SOL (`"true"`) or in tokens (`"false"`).Worst price you accept, in percent. Default 10. Use 15–25 for coins in their first minutes.Extra SOL to land faster. Default 0.00005.`pump` (bonding curve), `pump-amm` (PumpSwap) or `auto` (we pick). Default `pump`; `auto` resolves curve vs. AMM from chain state.
## Full example
```javascript
import { Keypair, VersionedTransaction } from '@solana/web3.js'
import bs58 from 'bs58'
const BASE = 'https://api.trenchytools.lol'
const API_KEY = 'YOUR_API_KEY'
const wallet = Keypair.fromSecretKey(bs58.decode(process.env.SOL_PRIVATE_KEY))
// 1. Ask for the transaction
const res = await fetch(`${BASE}/api/sol/trade-local?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: wallet.publicKey.toBase58(),
action: 'buy',
mint: 'MINT_ADDRESS',
amount: 0.01, // 0.01 SOL
denominatedInSol: 'true',
slippage: 15,
priorityFee: 0.0001,
pool: 'auto',
}),
})
if (!res.ok) throw new Error((await res.json()).error)
// 2. Sign it with your own key
const tx = VersionedTransaction.deserialize(new Uint8Array(await res.arrayBuffer()))
tx.sign([wallet])
// 3. Send it through every fast lane
const sent = await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }),
}).then((r) => r.json())
console.log('https://solscan.io/tx/' + sent.signature)
```
```python
import base64, os, requests
from solders.keypair import Keypair
from solders.transaction import VersionedTransaction
BASE = 'https://api.trenchytools.lol'
API_KEY = 'YOUR_API_KEY'
wallet = Keypair.from_base58_string(os.environ['SOL_PRIVATE_KEY'])
# 1. Ask for the transaction
res = requests.post(f'{BASE}/api/sol/trade-local', params={'api-key': API_KEY}, json={
'publicKey': str(wallet.pubkey()),
'action': 'buy',
'mint': 'MINT_ADDRESS',
'amount': 0.01,
'denominatedInSol': 'true',
'slippage': 15,
'priorityFee': 0.0001,
'pool': 'auto',
})
res.raise_for_status()
# 2. Sign it with your own key
tx = VersionedTransaction(VersionedTransaction.from_bytes(res.content).message, [wallet])
# 3. Send it through every fast lane
sent = requests.post(f'{BASE}/api/sol/send', params={'api-key': API_KEY},
json={'transaction': base64.b64encode(bytes(tx)).decode()}).json()
print('https://solscan.io/tx/' + sent['signature'])
```
```bash
curl -X POST "https://api.trenchytools.lol/api/sol/trade-local?api-key=YOUR_API_KEY&format=json" \
-H "Content-Type: application/json" \
-d '{
"publicKey": "YOUR_WALLET",
"action": "buy",
"mint": "MINT_ADDRESS",
"amount": 0.01,
"denominatedInSol": "true",
"slippage": 15,
"priorityFee": 0.0001,
"pool": "auto"
}'
```
## Response
By default the body is the **raw transaction bytes** (`application/octet-stream`). Add `?format=json` to get JSON instead:
```json title="Response"
{
"transaction": "AQAAAAAAAAAAAAAA…", // base64, unsigned
"encoding": "base64",
"expected": { "tokens": 291577.94, "sol": 0.01 },
"feeSol": 0.00005,
"mint": "MINT_ADDRESS"
}
```
## Selling everything
```json title="Response"
{ "publicKey": "YOUR_WALLET", "action": "sell", "mint": "MINT_ADDRESS",
"amount": "100%", "denominatedInSol": "false", "slippage": 15, "pool": "auto" }
```
Got error `6002` or `6003` when the transaction ran? The price moved more than your slippage while it was on its way. Raise `slippage`, or trade a little later when the coin has calmed down.
---
# Create a token
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/create
Launch a pump.fun coin with its image, links and a first buy, in one transaction.
Three calls: `POST /api/ipfs` for the metadata URI, a fresh mint keypair (generated locally, it co-signs), then `trade-local` with `action: "create"`. The create and the dev buy are one transaction.
Send them to [POST /api/ipfs](/docs/ipfs). You get back a `metadataUri`.
A brand-new keypair that becomes the coin's address. It must sign the create too.
Use `action: "create"` with `tokenMetadata` and the mint's public key.
## Request fields
Your wallet (the creator).`create`.The **public key** of the new mint keypair.`{ name, symbol, uri }`: `uri` is the `metadataUri` from [/api/ipfs](/docs/ipfs). Name up to 32 characters, symbol up to 10.Your dev buy, in SOL. 0 for no buy.Keep `"true"`.For the dev buy. Default 10.SOL. Default 0.00005.
## Full example
```javascript
import fs from 'node:fs'
import { Keypair, VersionedTransaction } from '@solana/web3.js'
import bs58 from 'bs58'
const BASE = 'https://api.trenchytools.lol', API_KEY = 'YOUR_API_KEY'
const wallet = Keypair.fromSecretKey(bs58.decode(process.env.SOL_PRIVATE_KEY))
// 1. Upload image + details
const form = new FormData()
form.append('file', new Blob([fs.readFileSync('./logo.png')], { type: 'image/png' }), 'logo.png')
form.append('name', 'My Coin')
form.append('symbol', 'MYC')
form.append('description', 'The best coin')
form.append('twitter', 'https://x.com/mycoin')
form.append('website', 'https://mycoin.fun')
const { metadataUri } = await fetch(`${BASE}/api/ipfs?api-key=${API_KEY}`, { method: 'POST', body: form }).then((r) => r.json())
// 2. A new mint keypair (this becomes the coin's address)
const mint = Keypair.generate()
// 3. Create + dev buy in one transaction
const res = await fetch(`${BASE}/api/sol/trade-local?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
publicKey: wallet.publicKey.toBase58(),
action: 'create',
mint: mint.publicKey.toBase58(),
tokenMetadata: { name: 'My Coin', symbol: 'MYC', uri: metadataUri },
amount: 0.1, // dev buy: 0.1 SOL
denominatedInSol: 'true',
slippage: 10,
priorityFee: 0.0005,
}),
})
const tx = VersionedTransaction.deserialize(new Uint8Array(await res.arrayBuffer()))
tx.sign([wallet, mint]) // both must sign
const sent = await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }),
}).then((r) => r.json())
console.log('https://pump.fun/coin/' + mint.publicKey.toBase58(), sent.signature)
```
```python
import base64, os, requests
from solders.keypair import Keypair
from solders.transaction import VersionedTransaction
BASE, API_KEY = 'https://api.trenchytools.lol', 'YOUR_API_KEY'
wallet = Keypair.from_base58_string(os.environ['SOL_PRIVATE_KEY'])
# 1. Upload image + details
with open('logo.png', 'rb') as f:
meta = requests.post(f'{BASE}/api/ipfs', params={'api-key': API_KEY},
files={'file': ('logo.png', f, 'image/png')},
data={'name': 'My Coin', 'symbol': 'MYC', 'description': 'The best coin'}).json()
# 2. A new mint keypair
mint = Keypair()
# 3. Create + dev buy
res = requests.post(f'{BASE}/api/sol/trade-local', params={'api-key': API_KEY}, json={
'publicKey': str(wallet.pubkey()), 'action': 'create', 'mint': str(mint.pubkey()),
'tokenMetadata': {'name': 'My Coin', 'symbol': 'MYC', 'uri': meta['metadataUri']},
'amount': 0.1, 'denominatedInSol': 'true', 'slippage': 10, 'priorityFee': 0.0005,
})
tx = VersionedTransaction(VersionedTransaction.from_bytes(res.content).message, [wallet, mint])
sent = requests.post(f'{BASE}/api/sol/send', params={'api-key': API_KEY},
json={'transaction': base64.b64encode(bytes(tx)).decode()}).json()
print('https://pump.fun/coin/' + str(mint.pubkey()), sent['signature'])
```
Coins are created as Token-2022 with the dev buy folded in, the same shape the pump.fun website makes.
---
# Jito bundles
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/bundles
Up to 5 transactions that land together, all or nothing; up to 16 buyers with lookup tables.
Send an **array** of up to 5 trade objects instead of one. You get back a list of transactions, one per object, in the same order. Sign each with its wallet, then send them together with [POST /api/sol/bundle](/docs/sol/send-bundle). They land in the same block, in order, or not at all.
The **first** object's `priorityFee` is paid as the Jito tip (that is what makes a bundle land). Priority fees on the other objects are ignored.Launching with buyers? Put the `create` first and the buys of the **same mint** after it. Each buy is priced on the curve exactly as it will be at that point in the bundle (after the dev buy and the buys before it), so the amounts match what lands. A bundle is the create plus up to 4 buyers.
Add `?format=json` to get `{ transactions: [{ transaction, expected: { tokens, sol }, feeSol, mint, action }] }` instead of a bare list: handy for showing each wallet how many tokens it will get.
Need more than 4 buyers in the block? See [Lookup tables](/docs/sol/lut): a `buyers` entry puts several wallets in one transaction, for about 16 per bundle.
## Example: launch + 2 buyers
```javascript title="JavaScript"
const txs = await fetch(`${BASE}/api/sol/trade-local?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([
{ publicKey: dev.publicKey.toBase58(), action: 'create', mint: mint.publicKey.toBase58(),
tokenMetadata: { name: 'My Coin', symbol: 'MYC', uri: metadataUri },
amount: 0.5, denominatedInSol: 'true', slippage: 10, priorityFee: 0.002, pool: 'pump' },
{ publicKey: buyer1.publicKey.toBase58(), action: 'buy', mint: mint.publicKey.toBase58(),
amount: 0.3, denominatedInSol: 'true', slippage: 25, priorityFee: 0, pool: 'pump' },
{ publicKey: buyer2.publicKey.toBase58(), action: 'buy', mint: mint.publicKey.toBase58(),
amount: 0.3, denominatedInSol: 'true', slippage: 25, priorityFee: 0, pool: 'pump' },
]),
}).then((r) => r.json()) // [base58, base58, base58]
const signed = txs.map((b58, i) => {
const tx = VersionedTransaction.deserialize(bs58.decode(b58))
tx.sign(i === 0 ? [dev, mint] : [[buyer1, buyer2][i - 1]])
return bs58.encode(tx.serialize())
})
const { bundleId } = await fetch(`${BASE}/api/sol/bundle?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transactions: signed }),
}).then((r) => r.json())
console.log('bundle', bundleId)
```
## Response
```json title="Response"
["4hX9…base58…", "2kPq…base58…", "9wLm…base58…"]
```
---
# Lookup tables
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/lut
Launch with up to 16 buyers in one block: a lookup table lets several wallets buy in one transaction.
A Jito bundle holds 5 transactions, so a plain launch bundle is the create plus 4 buyers. An **address lookup table** lists the accounts a coin's buys touch (its curve accounts, each wallet's token account), so one transaction can carry **about 4 buyers** instead of one, and a bundle about **16**. The table lives on chain under your wallet; its rent (about 0.002 SOL plus 32 bytes per address) comes back when you close it after the launch.
Order of events: create the table and wait until it is **ready** (one slot after the last extend, about a second) → build the bundle with `buyers` entries that name the table → send the bundle → later, deactivate and close the table to get the rent back.
## 1. Create the table
The wallet that owns the table and pays its rent (the dev wallet is a good choice).The coin (it may not exist yet: pass the new mint's public key).Every wallet that will buy (up to 60).The coin's creator (defaults to `authority`); needed when the coin does not exist yet.
You get the table's address and one or more unsigned transactions: the first creates the table and adds the first addresses, the others add more. Sign each with `authority` and send them **in order** with [/api/sol/send](/docs/sol/send), waiting for the first to confirm.
```javascript
const { lookupTable, transactions, rentSol } = await fetch(`${BASE}/api/sol/lut?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ authority: dev.publicKey.toBase58(), mint: mint.publicKey.toBase58(), wallets: buyers.map((b) => b.publicKey.toBase58()) }),
}).then((r) => r.json())
for (const b64 of transactions) {
const tx = VersionedTransaction.deserialize(Buffer.from(b64, 'base64'))
tx.sign([dev])
const { signature } = await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }),
}).then((r) => r.json())
// wait for it: GET /api/sol/tx/:signature until confirmed
}
// then poll GET /api/sol/lut/:lookupTable until ready === true
```
```bash
curl -X POST "https://api.trenchytools.lol/api/sol/lut?api-key=YOUR_API_KEY" -H "Content-Type: application/json" -d '{"authority":"DEV_WALLET","mint":"NEW_MINT","wallets":["WALLET_1","WALLET_2","WALLET_3"]}'
```
```json title="Response"
{ "lookupTable": "…", "addresses": 14, "rentSol": 0.0043, "transactions": ["base64…"], "encoding": "base64" }
```
## 2. Check it is ready
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/lut/LOOKUP_TABLE?api-key=YOUR_API_KEY"
```
```json title="Response"
{ "lookupTable": "…", "authority": "…", "addresses": ["…"], "lastExtendedSlot": 3812, "ready": true, "deactivated": false }
```
## 3. Build the bundle with buyers entries
In [/api/sol/trade-local](/docs/sol/bundles) a bundle entry may carry `buyers: [{ publicKey, amount }, …]` plus `lookupTable` instead of a single `publicKey`. Up to 6 buyers per entry are accepted; about 4 fit in 1232 bytes (every signer still costs an address and a signature). Each buyer signs; the first one pays the network fee. Buyers are priced in order on the same curve.
```javascript title="JavaScript"
const txs = await fetch(`${BASE}/api/sol/trade-local?format=json&api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify([
{ publicKey: dev.publicKey.toBase58(), action: 'create', mint: mint.publicKey.toBase58(),
tokenMetadata: { name: 'My Coin', symbol: 'MYC', uri: metadataUri }, amount: 0.5, denominatedInSol: 'true', slippage: 10, priorityFee: 0.002 },
{ action: 'buy', mint: mint.publicKey.toBase58(), lookupTable, denominatedInSol: 'true', slippage: 25,
buyers: buyers.slice(0, 4).map((b) => ({ publicKey: b.publicKey.toBase58(), amount: 0.2 })) },
{ action: 'buy', mint: mint.publicKey.toBase58(), lookupTable, denominatedInSol: 'true', slippage: 25,
buyers: buyers.slice(4, 8).map((b) => ({ publicKey: b.publicKey.toBase58(), amount: 0.2 })) },
]),
}).then((r) => r.json()) // { transactions: [{ transaction, expected, perBuyer: [{ publicKey, tokens, sol }] }, …] }
const signed = txs.transactions.map((t, i) => {
const tx = VersionedTransaction.deserialize(bs58.decode(t.transaction))
tx.sign(i === 0 ? [dev, mint] : buyers.slice((i - 1) * 4, i * 4))
return bs58.encode(tx.serialize())
})
// POST /api/sol/bundle { transactions: signed }
```
## 4. Get the rent back
`POST /api/sol/lut/:address/deactivate { authority }` returns a transaction to sign and send. About four minutes later (513 slots), `POST /api/sol/lut/:address/close { authority, recipient? }` returns the one that closes the table and refunds the rent. Too early gets `409 cooling_down` with the seconds left.
---
# Vanity mint
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/vanity
A mint that ends in `pump` (or another short pattern): instant from our warm pool, a bounded job for other patterns, or the SDK grinder on your own CPU.
pump.fun coins conventionally end in `pump`, letsbonk coins in `bonk`. The address is the mint keypair's public key, so a pattern means grinding keypairs until one matches (a 4-character suffix averages 11 million tries). Three tiers: **warm pool** — we keep a few `…pump` and `…bonk` keys ready, so the common request answers at once; **job** — other patterns up to 4 characters are ground on our workers (up to 10 minutes); **local** — the SDK grinder on your own CPU, no limits, the key never exists anywhere else (the recommended default for anything custom).
A job's keypair is handed out **once** and never stored or logged. Use it promptly as the `mint` of your own `create`: anyone who sees the secret first could create a token at that address.Save the keypair to disk the moment you get it (the `wallets/` pattern on [Wallet backups](/docs/wallets/backups)); a mint key that only lives in memory is a launch you cannot repeat.
## Start a job
Base58 characters the address must end with, e.g. `pump`. Case-sensitive by default.Base58 characters it must start with. `suffix` + `prefix` together ≤ 4 characters on our workers; longer patterns → `vanity_too_long`, grind locally.Default `true` (pump.fun shows the exact case). `false` matches letters in either case and is ~30 % faster per character.
```bash
curl -X POST "https://api.trenchytools.lol/api/sol/vanity?api-key=YOUR_API_KEY" \
-H "content-type: application/json" \
-d '{"suffix":"pump"}'
```
```javascript
const start = await fetch(`${BASE}/api/sol/vanity?api-key=${API_KEY}`, {
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ suffix: 'pump' }),
}).then((r) => r.json())
// pooled suffixes come back status "done" immediately; otherwise poll (a job may take minutes)
let job
do {
await new Promise((r) => setTimeout(r, 5000))
job = await fetch(`${BASE}${start.poll}?api-key=${API_KEY}`).then((r) => r.json())
} while (job.status === 'running')
if (job.status !== 'done') throw new Error(job.reason)
const mint = Keypair.fromSecretKey(bs58.decode(job.secretKey)) // use it as the mint of your create, now
```
```json title="Response (202 · pooled key: status \"done\" straight away, poll once to collect it)"
{
"jobId": "k3Jt…",
"status": "running",
"suffix": "pump",
"caseSensitive": true,
"expectedAttempts": 11316496,
"limits": { "maxChars": 4, "workersPerJob": 3, "timeoutS": 600, "maxJobs": 2, "pool": { "ready": { "pump": 5, "bonk": 3 } } },
"poll": "/api/sol/vanity/k3Jt…"
}
```
## Poll a job
`GET /api/sol/vanity/:jobId` → `status` `running` (with `attempts`, `elapsedMs`), `failed` (with `reason`, e.g. the 10-minute limit), or `done` with `publicKey` and `secretKey` — once. `pooled: true` marks a key that came from the warm pool. A second read says `collected: true`. Results are kept 10 minutes. One running job per key; when the workers are busy you get `429 vanity_busy`: retry in a minute or grind locally.
```json title="Response (done)"
{
"jobId": "k3Jt…", "status": "done", "attempts": 9412377, "elapsedMs": 171204,
"publicKey": "7gH…pump",
"secretKey": "",
"warning": "Shown once; we keep nothing. Use it only as the mint of your own create, promptly."
}
```
## Grind locally (recommended)
Same search on your own CPU, no limits, the key never leaves the process. The SDK ships it; any example repo has the worker.
```javascript title="JavaScript"
import { grindVanity } from '@trenchytools/sdk/vanity'
const { keypair, attempts } = await grindVanity({ suffix: 'pump', workers: 8 }) // ~1–2 min on 8 cores
// keypair.publicKey.toBase58() ends in "pump"; pass it as `mint` and sign the create with it
```
## What each launchpad expects
| Launchpad | Address pattern | Who makes it |
| --- | --- | --- |
| pump.fun | `…pump` (convention, optional) | You: vanity mint keypair |
| letsbonk (LaunchLab) | `…bonk` (convention, optional) | You: vanity mint keypair |
| StonkFun (LaunchLab) | none | — |
| four.meme (BNB) | `…4444` always | The launchpad (CREATE2 salt), nothing to do |
| Flap (BNB) | none known | — |
| pons (Robinhood) | derived from form + salt | Salt search; not exposed yet |
`GET /api/chains` carries the same `address` info per launchpad so an assistant knows when to grind and when not to.
---
# Creator fees
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/creator-fees
See and claim the creator fees your coins earned.
Every pump.fun coin pays its creator a share of each trade, on the bonding curve and after it moves to PumpSwap. Check what a creator wallet can claim, then claim it all in one transaction.
The creator (dev) wallet.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/creator-fees?wallet=DEV_WALLET&api-key=YOUR_API_KEY"
```
```json title="Response"
{ "wallet": "…", "lamports": "18250000", "sol": 0.01825 }
```
## Claim them
Ask [/api/sol/trade-local](/docs/sol/trade) for `action: "collectCreatorFee"`. No mint needed: one transaction claims every coin's fees. Sign it with the creator wallet and send it. There is no API fee on claims.
```javascript title="JavaScript"
const res = await fetch(`${BASE}/api/sol/trade-local?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ publicKey: dev.publicKey.toBase58(), action: 'collectCreatorFee', priorityFee: 0.0001 }),
})
const tx = VersionedTransaction.deserialize(new Uint8Array(await res.arrayBuffer()))
tx.sign([dev])
// then POST it to /api/sol/send
```
A wallet with nothing to claim gets `409 nothing_to_collect`.
---
# Lightning trade
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/lightning
We build, sign with the wallet linked to your key, and send. One request, no signing code.
Lightning is **not enabled yet** on this server. Use [Buy & sell](/docs/sol/trade) (you sign) in the meantime.
Same fields as [Buy & sell](/docs/sol/trade), but no `publicKey`: we use the Solana wallet linked to your API key (create one with **Key + new wallets** on [Get an API key](/docs/api-keys)) and send the transaction for you. Fee: 1% instead of 0.5%.
```bash title="cURL"
curl -X POST "https://api.trenchytools.lol/api/sol/trade?api-key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "buy", "mint": "MINT_ADDRESS", "amount": 0.01, "denominatedInSol": "true", "slippage": 15, "priorityFee": 0.0001, "pool": "auto" }'
```
```json title="Response"
{ "signature": "5Uf…", "lanes": ["rpc", "jito:mainnet"], "expected": { "tokens": 291577.9, "sol": 0.01 } }
```
---
# Send a transaction
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/send
Send a signed transaction through our RPC and every Jito region at once.
Takes a **signed** transaction and pushes it through our premium Solana node and all Jito regions at the same time, so it lands as fast as possible. Works for any Solana transaction, not just ours.
The signed transaction, base64 or base58.Default `false`: we simulate first and refuse a transaction that would fail. `true` sends without checking (faster).Send only through Jito.
```javascript
const sent = await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transaction: Buffer.from(signedTx.serialize()).toString('base64') }),
}).then((r) => r.json())
```
```bash
curl -X POST "https://api.trenchytools.lol/api/sol/send?api-key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "transaction": "BASE64_SIGNED_TX" }'
```
```json title="Response"
{ "signature": "5Uf…", "lanes": ["rpc", "jito:mainnet", "jito:ny", "jito:frankfurt"] }
```
Trades are relayed exactly as `trade-local` built them. A pump.fun or PumpSwap buy, sell or create whose fee transfer was removed gets `400 fee_missing`; the fee is 0.5 % of the SOL side and is already in the `feeSol` the build reports.
---
# Blockhash
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/blockhash
A recent blockhash, for transactions you build yourself.
Most transactions come ready-made from [/api/sol/trade-local](/docs/sol/trade). For the ones you build yourself (moving SOL between your wallets, closing empty token accounts) you need a recent blockhash: get it here, build and sign the transaction, then send it with [/api/sol/send](/docs/sol/send). No RPC of your own needed.
```javascript
import { SystemProgram, TransactionMessage, VersionedTransaction } from '@solana/web3.js'
const { blockhash } = await (await fetch(`${BASE}/api/sol/blockhash?api-key=${API_KEY}`)).json()
const tx = new VersionedTransaction(new TransactionMessage({
payerKey: from.publicKey,
recentBlockhash: blockhash,
instructions: [SystemProgram.transfer({ fromPubkey: from.publicKey, toPubkey: to, lamports: 10_000_000 })],
}).compileToV0Message())
tx.sign([from])
await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }),
})
```
```bash
curl "https://api.trenchytools.lol/api/sol/blockhash?api-key=YOUR_API_KEY"
```
```json title="Response"
{ "blockhash": "5tt5pLPN…", "lastValidBlockHeight": 438211042 }
```
---
# Send a bundle
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/send-bundle
Submit 1–5 signed transactions as one Jito bundle.
1 to 5 signed transactions (base58 or base64). One must pay a Jito tip account; bundles from [trade-local](/docs/sol/bundles) already do.
```json title="Response"
{ "bundleId": "a1b2…", "signatures": ["…", "…"], "region": "ny" }
```
## Check if it landed
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/bundle/BUNDLE_ID?api-key=YOUR_API_KEY"
```
```json title="Response"
{ "bundleId": "a1b2…", "status": "Landed", "landedSlot": 459926401 } // Pending | Landed | Failed | Invalid
```
Trades are relayed exactly as `trade-local` built them. A pump.fun or PumpSwap buy, sell or create whose fee transfer was removed gets `400 fee_missing`; the fee is 0.5 % of the SOL side and is already in the `feeSol` the build reports.
---
# Transaction status
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/tx
Is my transaction confirmed?
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/tx/SIGNATURE?api-key=YOUR_API_KEY"
```
```json title="Response"
{ "signature": "5Uf…", "slot": 459926401, "confirmationStatus": "confirmed", "err": null }
```
---
# Priority fee
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/priority-fee
Live suggestions for priorityFee and Jito tips.
Read this before trading to pick a `priorityFee` that lands. `suggestedPriorityFeeSol` plugs straight into the `priorityFee` field.
```json title="Response"
{
"suggestedPriorityFeeSol": { "low": 0.000006, "medium": 0.000018, "high": 0.000024, "veryHigh": 0.000252 },
"jitoTipSol": { "p25": 0.000001, "p50": 0.000012, "p75": 0.000077, "p95": 0.00055 }
}
```
Jito tip accounts (for building your own bundles): `GET /api/sol/jito/tip-accounts`.
---
# Quote
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/quote
See what a trade would give you before you make it.
The coin's address.`buy` or `sell`.SOL for a buy, tokens for a sell (by default).Override the unit.`auto` (default), `pump` or `pump-amm`.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/quote?mint=MINT_ADDRESS&action=buy&amount=0.1&api-key=YOUR_API_KEY"
```
```json title="Response"
{
"pool": "pump",
"action": "buy",
"solIn": 0.1,
"tokensOut": 2915779.4,
"avgPriceSol": 3.4e-8,
"spotPriceSol": 3.36e-8,
"priceImpactPct": 1.27,
"marketCapSol": 33.6,
"bondingProgressPct": 4.7,
"complete": false
}
```
---
# Token info
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/token
Name, image link, price, market cap and how close the coin is to graduating.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/token/MINT_ADDRESS?api-key=YOUR_API_KEY"
```
```json title="Response"
{
"mint": "Cpu7…pump",
"name": "MoonGoat",
"symbol": "MGOAT",
"uri": "https://ipfs.io/ipfs/…",
"venue": "pump", // pump | pump-amm
"curve": {
"complete": false,
"creator": "8cgW…",
"marketCapSol": 33.6,
"priceSol": 3.36e-8,
"bondingProgressPct": 4.7
}
}
```
---
# Balances
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/balances
SOL and token balances of one wallet, or of up to 100 wallets in one call.
## One wallet
Any Solana address.Comma-separated coin addresses. Leave out to list every non-zero token (slow for wallets with thousands of tokens).Also list token accounts holding 0, e.g. to close them and get the rent back.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/balances?wallet=WALLET&mints=MINT_ADDRESS&api-key=YOUR_API_KEY"
```
```json title="Response"
{ "wallet": "…", "sol": 1.25, "tokens": [ { "mint": "…", "balance": 291577.94, "raw": "291577940000", "account": "…", "program": "TokenzQd…" } ] }
```
## Many wallets
A bundler checking its dev, bundle and holder wallets: pass `wallets` instead of `wallet` and get every SOL balance (and one coin's balance with `mint`) in a single request.
Up to 100 comma-separated addresses.One coin address: each row then carries that coin's balance too.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/sol/balances?wallets=WALLET_1,WALLET_2,WALLET_3&mint=MINT_ADDRESS&api-key=YOUR_API_KEY"
```
```json title="Response"
{ "wallets": [ { "wallet": "…", "sol": 0.52, "token": { "mint": "…", "balance": 1200000, "raw": "1200000000000", "account": "…", "program": "TokenzQd…" } } ] }
```
---
# Live data (websocket)
Source: https://trenchytools-docs-production.up.railway.app/docs/sol/websocket
New coins, graduations and trades the moment they happen.
Open **one** websocket and subscribe to what you need.
| Send | You receive |
| --- | --- |
| `{"method":"subscribeNewToken"}` | Every new pump.fun coin |
| `{"method":"subscribeMigration"}` | Every coin that graduates to PumpSwap |
| `{"method":"subscribeTokenTrade","keys":["MINT"]}` | Every trade on these coins |
| `{"method":"subscribeAccountTrade","keys":["WALLET"]}` | Every trade by these wallets (copy-trading) |
To stop, send the same message with `unsubscribe…` instead of `subscribe…`.
```javascript
const ws = new WebSocket('wss://api.trenchytools.lol/api/sol/data?api-key=YOUR_API_KEY')
ws.onopen = () => {
ws.send(JSON.stringify({ method: 'subscribeNewToken' }))
}
ws.onmessage = (msg) => {
const e = JSON.parse(msg.data)
if (e.txType === 'create') {
console.log('new coin', e.symbol, e.mint, 'dev bought', e.solAmount, 'SOL')
// follow its trades too
ws.send(JSON.stringify({ method: 'subscribeTokenTrade', keys: [e.mint] }))
} else if (e.txType === 'buy' || e.txType === 'sell') {
console.log(e.txType, e.solAmount, 'SOL by', e.traderPublicKey)
}
}
```
```python
import asyncio, json, websockets
async def main():
async with websockets.connect('wss://api.trenchytools.lol/api/sol/data?api-key=YOUR_API_KEY') as ws:
await ws.send(json.dumps({'method': 'subscribeNewToken'}))
async for raw in ws:
e = json.loads(raw)
if e.get('txType') == 'create':
print('new coin', e['symbol'], e['mint'])
asyncio.run(main())
```
## Event examples
```json title="Response"
// new coin
{ "txType": "create", "signature": "…", "mint": "…pump", "traderPublicKey": "…",
"name": "MoonGoat", "symbol": "MGOAT", "uri": "https://ipfs.io/ipfs/…", "pool": "pump",
"initialBuy": 2560000, "solAmount": 0.077, "marketCapSol": 28.1 }
// trade
{ "txType": "buy", "signature": "…", "mint": "…pump", "traderPublicKey": "…",
"tokenAmount": 291577.9, "solAmount": 0.01, "marketCapSol": 33.6, "pool": "pump" }
```
New coins and graduations are free. Trade streams need your API key in the URL.
## Migration events
`subscribeMigration` delivers two moments per coin: `txType: "complete"` when the bonding curve fills (trading on the curve stops), then `txType: "migrate"` when pump.fun creates the PumpSwap pool, with `poolAddress`. Trade on PumpSwap (`pool: "pump-amm"` or `"auto"`) from the second one on.
```json title="Migration"
{ "txType": "migrate", "signature": "…", "mint": "…", "pool": "pump-amm", "poolAddress": "…", "slot": 3812, "ts": 1760000000000 }
```
Our feed subscribes upstream to exactly what connected clients ask for (creates, migrations, the mints and wallets you watch), straight from our own node. Subscribe to what you need; nothing is sampled or throttled.
---
# Robinhood overview
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/overview
pons on Robinhood chain: curve, Uniswap v4 after graduation, ordered EVM transactions.
pons coins on their bonding curve, then on the Uniswap v4 pool (MemeHook) after graduation. EVM chain 4663; sign with viem, ethers or web3.py.
`trade-local` returns an ordered `transactions[]` (an `approve` may precede a sell, a `graduate` may precede a trade). Submit in order; each entry carries `label`, `to`, `data`, `value`, `gas`.
| You want to… | Use |
| --- | --- |
| Buy or sell | [POST /api/rh/trade-local](/docs/rh/trade) |
| Launch a coin | [Create a token](/docs/rh/create) |
| Send a signed transaction | [POST /api/rh/send](/docs/rh/send) |
| See a price before trading | [GET /api/rh/quote](/docs/rh/quote) |
| Watch new coins and trades live | [Websocket](/docs/rh/websocket) |
Chain ID **4663** · native ETH · EVM field names: `wallet`, `token`, `denominatedInEth`.
---
# Buy & sell
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/trade
Get the ordered transactions for a pons buy or sell, then send them in order.
Returns the transactions your wallet needs, in order, **unsigned**. Sign and send them one after another with your own wallet. If a transaction is labeled `approve`, wait for it to confirm before sending the next one. Every buy and sell carries a min-out derived from the quote and your `slippage`, on the curve and on the v4 pool alike.
## Request fields
Your 0x address.`buy` or `sell`.The coin's 0x address.Buys: ETH. Sells: tokens, or a share of your balance like `"100%"`.Percent. Default 5.
## Full example
```javascript
import { createWalletClient, createPublicClient, http, defineChain } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
const BASE = 'https://api.trenchytools.lol', API_KEY = 'YOUR_API_KEY'
const robinhood = defineChain({ id: 4663, name: 'Robinhood Chain',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://rpc.mainnet.chain.robinhood.com'] } } })
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY)
const wallet = createWalletClient({ account, chain: robinhood, transport: http() })
const chain = createPublicClient({ chain: robinhood, transport: http() })
// 1. Ask for the transactions
const { transactions, expected } = await fetch(`${BASE}/api/rh/trade-local?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wallet: account.address, action: 'buy', token: 'TOKEN_ADDRESS', amount: 0.01, slippage: 5 }),
}).then((r) => r.json())
console.log('you should get about', expected.tokens, 'tokens')
// 2. Sign and send each one, in order
for (const tx of transactions) {
const hash = await wallet.sendTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value) })
console.log(tx.label, hash)
if (tx.label === 'approve' || tx.label === 'graduate') await chain.waitForTransactionReceipt({ hash })
}
```
```python
import os, requests
from web3 import Web3
BASE, API_KEY = 'https://api.trenchytools.lol', 'YOUR_API_KEY'
w3 = Web3(Web3.HTTPProvider('https://rpc.mainnet.chain.robinhood.com'))
acct = w3.eth.account.from_key(os.environ['EVM_PRIVATE_KEY'])
r = requests.post(f'{BASE}/api/rh/trade-local', params={'api-key': API_KEY}, json={
'wallet': acct.address, 'action': 'buy', 'token': 'TOKEN_ADDRESS', 'amount': 0.01, 'slippage': 5,
}).json()
for tx in r['transactions']:
signed = acct.sign_transaction({
'to': tx['to'], 'data': tx['data'], 'value': int(tx['value']), 'chainId': 4663,
'nonce': w3.eth.get_transaction_count(acct.address),
'gas': int(tx.get('gas') or 500000), 'gasPrice': w3.eth.gas_price,
})
h = w3.eth.send_raw_transaction(signed.raw_transaction)
print(tx['label'], h.hex())
if tx['label'] in ('approve', 'graduate'):
w3.eth.wait_for_transaction_receipt(h)
```
## Response
```json title="Response"
{
"chainId": 4663,
"transactions": [
{ "label": "approve", "to": "0x…token", "data": "0x…", "value": "0", "gas": "55971" },
{ "label": "sell", "to": "0x…curve", "data": "0x…", "value": "0" },
{ "label": "fee", "to": "0x…", "data": "0x", "value": "162873307575", "valueEth": "0.000000162873307575" }
],
"expected": { "tokens": 19761.65, "eth": 0.0000326 },
"venue": "curve" // curve | v4
}
```
| label | What it is |
| --- | --- |
| `approve` | ERC-20 approve to the curve/pool. Emitted once per token, ahead of the first sell. |
| `graduate` | Finishes a coin's move to its V4 pool when it is stuck halfway. Rare. |
| `buy` / `sell` | The trade itself. |
| `fee` | Our 0.5% service fee, in ETH. |
---
# Create a token
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/create
Launch a pons coin with a first buy in one transaction, and know its address before you sign.
Send it to [POST /api/ipfs](/docs/ipfs) and take `metadata.image` from the answer.
`action: "create"` with `tokenMetadata` and your dev buy in ETH.
The answer already contains the coin's **future address** (`predicted.token`).
Your 0x address (the creator).`create`.`{ name, symbol, image, description?, twitter?, telegram?, website? }`.Dev buy in ETH. Minimum 0.0001.Up to 31 wallets that skip the launch-window snipe tax (your other buyers).Percent. Default 5.
```javascript title="JavaScript"
const r = await fetch(`${BASE}/api/rh/trade-local?api-key=${API_KEY}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
wallet: account.address,
action: 'create',
tokenMetadata: { name: 'My Coin', symbol: 'MYC', image: imageUrl, twitter: 'https://x.com/mycoin' },
amount: 0.01, // dev buy: 0.01 ETH
slippage: 5,
}),
}).then((r) => r.json())
console.log('your coin will be', r.predicted.token, '· pons launch fee', r.launchFeeEth, 'ETH')
for (const tx of r.transactions) {
const hash = await wallet.sendTransaction({ to: tx.to, data: tx.data, value: BigInt(tx.value) })
if (tx.label === 'create') await chain.waitForTransactionReceipt({ hash })
}
```
```json title="Response"
{
"chainId": 4663,
"transactions": [ { "label": "create", "to": "0x…router", "value": "10500000000000000", … }, { "label": "fee", … } ],
"predicted": { "token": "0x9a21…", "curve": "0x…" },
"launchFeeEth": 0.0005,
"expected": { "tokens": 5823000, "eth": 0.01 }
}
```
---
# Lightning trade
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/lightning
We sign with the Robinhood wallet linked to your key and send everything in order.
Lightning is **not enabled yet** on this server. Use [Buy & sell](/docs/rh/trade) in the meantime.
Same fields as [Buy & sell](/docs/rh/trade) without `wallet`. Answer: `{ hash, hashes }`. Fee: 1%.
---
# Send a transaction
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/send
Broadcast a signed Robinhood transaction through our node.
The signed raw transaction, `0x…`.
```bash title="cURL"
curl -X POST "https://api.trenchytools.lol/api/rh/send?api-key=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "transaction": "0xSIGNED_RAW_TX" }'
```
```json title="Response"
{ "hash": "0x…" }
```
---
# Transaction status
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/tx
Did my transaction go through?
```bash title="cURL"
curl "https://api.trenchytools.lol/api/rh/tx/0xHASH?api-key=YOUR_API_KEY"
```
```json title="Response"
{ "hash": "0x…", "status": "success", "block": 94883891, "gasUsed": "113544" }
```
---
# Quote
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/quote
An exact quote, simulated as your wallet.
pons charges a launch-window snipe tax that depends on **who** buys, so the quote is simulated as your wallet. Pass `wallet` for the exact number. Graduated coins are quoted on their Uniswap v4 pool by simulating the swap (`venue: "v4"`); pass `holder` for sells.
The coin's 0x address.`buy` (amount in ETH) or `sell` (amount in tokens).How much.Your address. Required for an exact sell quote (it must hold the tokens).
```bash title="cURL"
curl "https://api.trenchytools.lol/api/rh/quote?token=TOKEN_ADDRESS&action=buy&amount=0.01&wallet=0xYOU&api-key=YOUR_API_KEY"
```
```json title="Response"
{ "venue": "curve", "action": "buy", "ethIn": 0.01, "tokensOut": 5822825.4, "snipeTaxBps": 0, "bondingProgressPct": 5.88 }
```
---
# Token info
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/token
The coin's curve, phase, price and graduation progress.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/rh/token/TOKEN_ADDRESS?api-key=YOUR_API_KEY"
```
```json title="Response"
{
"token": "0x6Fbc…", "name": "Pheal", "symbol": "PHEAL",
"venue": "pons-curve", // pons-curve | uniswap-v4
"curve": "0x…", "graduated": false, "creator": "0x…",
"curveState": { "realEth": 0.29, "graduationThresholdEth": 5, "bondingProgressPct": 5.88, "snipeTaxBps": 0, "priceEth": 2.2e-9 }
}
```
---
# Balances
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/balances
ETH and token balances of any wallet.
Any 0x address.Comma-separated token addresses.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/rh/balances?wallet=0xYOU&tokens=TOKEN_ADDRESS&api-key=YOUR_API_KEY"
```
```json title="Response"
{ "wallet": "0x…", "eth": "0.0683", "tokens": [ { "token": "0x…", "balance": "1889822.68" } ] }
```
---
# Live data (websocket)
Source: https://trenchytools-docs-production.up.railway.app/docs/rh/websocket
New pons coins, graduations and trades as they happen.
Same messages as the Solana websocket (`subscribeNewToken`, `subscribeMigration`, `subscribeTokenTrade`, `subscribeAccountTrade`). Events use EVM names.
```javascript title="JavaScript"
const ws = new WebSocket('wss://api.trenchytools.lol/api/rh/data?api-key=YOUR_API_KEY')
ws.onopen = () => ws.send(JSON.stringify({ method: 'subscribeNewToken' }))
ws.onmessage = (msg) => {
const e = JSON.parse(msg.data)
if (e.txType === 'create') console.log('new pons coin', e.symbol, e.token)
else if (e.txType === 'buy' || e.txType === 'sell') console.log(e.txType, e.ethAmount, 'ETH by', e.trader)
}
```
```json title="Events"
{ "txType": "create", "token": "0x…", "creator": "0x…", "name": "…", "symbol": "…", "image": "…", "curve": "0x…", "hash": "0x…", "block": 94883891 }
{ "txType": "buy", "token": "0x…", "trader": "0x…", "ethAmount": 0.01, "tokenAmount": 5823, "priceEth": 1.7e-6, "hash": "0x…" }
{ "txType": "migrate", "token": "0x…", "venue": "uniswap-v4", "hash": "0x…" }
```
---
# Wallet transfers
Source: https://trenchytools-docs-production.up.railway.app/docs/wallets/overview
Move value between your wallets and across chains: directly, through a centralized-exchange hop at the best available rate, through one named venue, or through a privacy-coin leg. Non-custodial throughout.
Funding a set of fresh wallets, sweeping them back, moving SOL to Robinhood chain or BNB Chain: `/api/transfer` does all of it with one request shape. You pick a **route** (where the coins travel) and a **mode** (how the exchange executes). You sign exactly one thing: a deposit from your wallet. We never hold the coins.
## Routes
| Route | What happens | When to use it |
| --- | --- | --- |
| `direct` | One signed transfer on the same chain, wallet → wallet. Nothing in between. | Moving money between wallets you are happy to see linked on chain. Same chain only. |
| `exchange` · `venue: "auto"` | **Smart routing.** The order goes to the provider with the best price, speed and reliability at that moment, drawn from centralized-exchange liquidity. The destination is paid by the exchange, not by your wallet. | Default. Best rate, no on-chain link between the two wallets. |
| `exchange` · `venue: ""` — **break wallet cluster** | Execution is forced through the exchange you name (bybit, kucoin, htx, mexc, …), so the destination is paid out by **that** exchange's hot wallet, not by the default route's. On-chain, the new wallet's funder is a different exchange from the one that funded your other wallets. `GET /api/transfer/venues` says which are open; a closed one is refused with `transfer_route_off`. The answer carries `venueHonoured`. Non-default venues cost about 1 % more (their withdrawal fee). | Fresh wallets that must not share a funder with your existing ones. |
| `exchange` · `venue: "random"` | A venue rolled per transfer (small amounts favour the cheapest route; larger ones mix venues). | Batches: fan-outs whose destinations should look independently funded. |
| cross-chain | Any exchange route with a different destination chain: SOL → ETH on Robinhood chain, SOL → BNB, ETH → SOL. The exchange does the conversion. | Moving between chains without a bridge contract or a wrapped asset. |
## Which routes are open
The exchange switches venues off from time to time. `GET /api/transfer/venues` probes every route every two minutes **without creating anything** and reports `open` / `closed` / `unknown` per venue, plus the state of the default route. `routes` includes the same status on each venue entry, `venue: "random"` only rolls open venues, and a transfer to a closed venue is refused up front rather than silently rerouted.
```json title="GET /api/transfer/venues"
{
"exchange": "on", "checkedAt": "2026-10-22T23:52:10.000Z",
"venues": {
"binance": { "status": "open", "default": true },
"bybit": { "status": "open" },
"kucoin": { "status": "closed", "reason": "the exchange refuses this route for our account" },
"htx": { "status": "closed", "reason": "switched off at the exchange right now" },
"mexc": { "status": "closed", "reason": "switched off at the exchange right now" },
"bitget": { "status": "closed", "reason": "switched off at the exchange right now" },
"gate": { "status": "closed", "reason": "switched off at the exchange right now" }
}
}
```
## Modes
| Mode | Execution | Fee | Timing |
| --- | --- | --- | --- |
| `float` (default) | Market rate at execution; output may vary slightly. | ~0.1–0.4 % | Fastest: usually under a few minutes. |
| `fixed` | Rate locked when the order is created; exact output guaranteed. | 0.8 % | Same once the deposit confirms; the deposit must land within ~15 minutes or the order expires. |
| `private` | Routed through a **Monero or Zcash leg** before paying out: the exchange converts in, hops through the privacy coin, converts out. | 1 % + network fees | Typically 5 minutes or more. |
What the private mode does: it breaks the direct on-chain path between your deposit and the destination, so a block explorer cannot walk from one to the other. What it does **not** do: make you anonymous. The exchange route sees both sides and applies its own policies. Say "reduces on-chain linkability", never "untraceable".
## Chains
| Chain | As source (`from`) | As destination (`to`) | Native unit |
| --- | --- | --- | --- |
| Solana `sol` | yes — unsigned v0 deposit transaction | yes | SOL |
| Robinhood chain `rh` | yes — `{to, value, gas}` deposit transaction | yes | ETH |
| Ethereum `eth` | not yet | yes | ETH |
| BNB Chain `bnb` | not yet | yes | BNB |
| Arc, Base | not yet | not yet (bridge route planned) | — |
## How a transfer runs
`GET /api/transfer/routes?from=sol&to=rh&amount=0.5` → every route and mode available now, with receive amount, fee, minimum and ETA. Respect `min`.
`POST /api/transfer` → the exchange opens an order; you get a **one-time deposit address** and an **unsigned deposit transaction** from `from.wallet` for exactly the deposit amount.
Sign that transaction with your wallet and submit it (`POST /api/sol/send`, or your own node). Promptly: the address is single-use and fixed orders expire.
`GET /api/transfer/:id` → `awaiting_deposit` → `exchanging` → `completed` with `payoutHash` on the destination chain. Failed or slow orders carry a `help` block with the receipt link and support contacts.
Budget: about 10 exchange orders a minute shared by every key (`429 transfer_busy`), 5 creations a minute per key, 25 legs per batch. The provider behind the exchange route is ours to run; you need no account with it.`GET /api/transfer/routes` — what is possible for a pair and amount.`POST /api/transfer` — deposit address + unsigned deposit transaction.`POST /api/transfer/batch` — fan-out and consolidate.`GET /api/transfer/:id` — status, receipt, help.
---
# Quotes
Source: https://trenchytools-docs-production.up.railway.app/docs/wallets/routes
Everything available for a pair and amount right now: direct, best-rate exchange, fixed, private — with receive amount, fee, minimum and ETA.
Call this before creating a transfer. The exchange minimum changes with the pair and the market; a create below it fails with `transfer_min_amount`.
Source chain: `sol` or `rh`.Destination chain: `sol`, `rh`, `eth`, `bnb`.In the source chain's native unit.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/transfer/routes?from=sol&to=rh&amount=0.5&api-key=YOUR_API_KEY"
```
```json title="Response"
{
"from": "sol", "to": "rh", "amount": 0.5,
"routes": [
{ "route": "exchange", "mode": "float", "venue": "auto", "receive": 0.02156, "fee": 0.000054, "networkFee": 0.00005, "etaS": 40, "min": 0.087, "max": 200300 },
{ "route": "exchange", "mode": "fixed", "venue": "auto", "receive": 0.02145, "fee": 0.00255, "networkFee": 0.00005, "etaS": 40, "min": 0.087 },
{ "route": "exchange", "mode": "float", "venue": "bybit", "status": "open", "receive": 0.02134, "fee": 0.00505, "etaS": 40, "min": 0.087, "note": "funds the destination from this exchange (breaks the wallet cluster); ~1 % extra" },
{ "route": "exchange", "mode": "float", "venue": "htx", "status": "closed", "note": "switched off at the exchange right now" },
{ "route": "exchange", "mode": "private", "venue": "auto", "receive": 0.02134, "fee": 0.00505, "networkFee": 0.00005, "etaS": 300, "min": 0.087 }
],
"venues": ["auto", "random", "binance", "bybit", "kucoin", "htx", "mexc", "bitget", "gate"],
"chains": { "sol": { "source": true, "destination": true }, "rh": { "source": true, "destination": true }, "eth": { "source": false, "destination": true }, "bnb": { "source": false, "destination": true } },
"exchange": "on"
}
```
A same-chain pair also lists `{ "route": "direct", "fee": 0 }`. `exchange: "off"` means the server has no exchange route configured; `direct` still works.
The per-venue entries are the break-wallet-cluster option: one line per exchange with its live `status`. Pass that `venue` to `POST /api/transfer`.
---
# Create a transfer
Source: https://trenchytools-docs-production.up.railway.app/docs/wallets/transfer
Open a transfer and get the deposit address plus the unsigned deposit transaction from your wallet.
One request: where from, where to, how much, which route and mode (see [Wallet transfers](/docs/wallets/overview) for what each means). The answer is a one-time deposit address and an unsigned deposit transaction from `from.wallet`; sign it, send it, then [track it](/docs/wallets/status).
Exchange routing reduces on-chain linkability between the two wallets; it is **not** anonymity, and the exchange route applies its own policies. `mode: "private"` adds a privacy-coin leg (slower, ~1 %). Never describe this to users as untraceable.
## Create a transfer
`{ chain, wallet }` — the wallet that will sign the deposit. `sol` or `rh`.`{ chain, address }` — where the coins end up. Any supported chain; validated for that chain.In the source chain's native coin (SOL on `sol`, ETH on `rh`).`auto` (default: exchange when enabled), `direct` (same chain, one signed transfer), `exchange`.`float` (default, market rate, ~0.1–0.4 %), `fixed` (locked output, 0.8 %, deposit within ~15 min), `private` (privacy-coin leg, 1 %, 5+ min).`auto` (best execution, default), `random` (an open venue rolled per transfer), or a named exchange (`bybit`, `kucoin`, `htx`, `mexc`, `bitget`, `gate`, `binance`) to force who pays the destination — the break-wallet-cluster option. Closed venues are refused (`transfer_route_off`); the answer says `venueHonoured`.Where a failed order is refunded. Default: `from.wallet`.
```javascript
const t = await fetch(`${BASE}/api/transfer?api-key=${API_KEY}`, {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ from: { chain: 'sol', wallet: wallet.publicKey.toBase58() }, to: { chain: 'sol', address: 'DEST…' }, amount: 0.5, mode: 'float', venue: 'auto' }),
}).then((r) => r.json())
// t.deposit.address is a one-time address for this transfer; t.transaction is the unsigned deposit from your wallet
const tx = VersionedTransaction.deserialize(Buffer.from(t.transaction.transaction, 'base64'))
tx.sign([wallet])
await fetch(`${BASE}/api/sol/send?api-key=${API_KEY}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ transaction: Buffer.from(tx.serialize()).toString('base64') }) })
// then poll t.poll until status is "completed" (payoutHash) or a final failure
```
```bash
curl -X POST "https://api.trenchytools.lol/api/transfer?api-key=YOUR_API_KEY" -H "content-type: application/json" \
-d '{"from":{"chain":"sol","wallet":"YOUR_WALLET"},"to":{"chain":"rh","address":"0xDEST"},"amount":0.5,"mode":"float"}'
```
```json title="Response (202)"
{
"transferId": "k9f3…", "status": "awaiting_deposit", "route": "exchange", "mode": "float", "venue": "binance",
"from": { "chain": "sol", "wallet": "…" }, "to": { "chain": "rh", "address": "0x…" }, "amount": 0.5,
"deposit": { "chain": "sol", "address": "", "amount": 0.5, "deadline": "2026-10-22T23:40:00Z" },
"transaction": { "chain": "sol", "encoding": "base64", "transaction": "" },
"poll": "/api/transfer/k9f3…"
}
```
On Robinhood chain the deposit is `transaction.evm` (`{chainId, from, to, value, gas}`): sign and send it with viem like any `trade-local` transaction. A `direct` transfer answers `200` with the same shape and no `deposit` deadline.
---
# Many wallets
Source: https://trenchytools-docs-production.up.railway.app/docs/wallets/batch
Fan one wallet out to many, or sweep many wallets back into one — one order and one unsigned deposit per leg.
The same transfer, repeated per leg, in one call. Legs are independent: a leg that fails (minimum, venue off, budget) is reported inline and the others proceed. Up to 25 legs.
## Fan-out: one source, many destinations
```javascript title="JavaScript"
const r = await fetch(`${BASE}/api/transfer/batch?api-key=${API_KEY}`, {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({
from: { chain: 'sol', wallet: funding.publicKey.toBase58() },
to: wallets.map((w) => ({ chain: 'sol', address: w.publicKey.toBase58(), amount: 0.3 })),
mode: 'float',
venue: 'random', // a venue per leg: the wallets are not all paid by the same exchange
}),
}).then((r) => r.json())
// r.legs[i].transaction is the unsigned deposit for leg i: sign each with the funding wallet and send
for (const leg of r.legs) if (!leg.error) { /* sign leg.transaction.transaction, POST /api/sol/send, remember leg.transferId */ }
```
## Consolidate: many sources, one destination
```json title="Request"
{
"from": [
{ "chain": "sol", "wallet": "WALLET_1", "amount": 0.25 },
{ "chain": "sol", "wallet": "WALLET_2", "amount": 0.25 }
],
"to": { "chain": "sol", "address": "TREASURY" },
"mode": "float", "venue": "auto"
}
```
Each source gets its own order and its own unsigned deposit (signed by that wallet). `route: "direct"` makes every leg a plain transfer instead.
```json title="Response (202)"
{
"batchId": "b_m2x…",
"legs": [
{ "transferId": "k9f3…", "status": "awaiting_deposit", "venue": "binance", "deposit": { "chain": "sol", "address": "…", "amount": 0.3 }, "transaction": { "chain": "sol", "encoding": "base64", "transaction": "…" }, "poll": "/api/transfer/k9f3…" },
{ "error": "the amount is below the exchange minimum for this pair", "code": "transfer_min_amount", "leg": { "to": { "address": "…" }, "amount": 0.01 } }
]
}
```
Keep the batch within the budget: ~10 exchange orders a minute shared by every key. A 25-leg fan-out takes two to three minutes of orders; legs refused with `transfer_busy` can be re-sent.
---
# Track and support
Source: https://trenchytools-docs-production.up.railway.app/docs/wallets/status
Where a transfer is, its receipt page, and what to do when one fails or stalls.
| status | Meaning |
| --- | --- |
| `awaiting_deposit` | Order open; waiting for your signed deposit to confirm. |
| `exchanging` | Deposit seen (`depositHash`); the exchange is executing. |
| `completed` | Paid out: `payoutHash` on the destination chain, `expectedReceive` final. |
| `failed` / `expired` / `refunded` / `cancelled` | Terminal. `reason` carries the exchange's wording; a refund goes to `refundAddress` (your wallet by default). |
| `awaiting_send` → `sent` | Direct transfers: tell us the signature with `POST /api/transfer/:id/sent {hash}`. |
```json title="Response"
{
"transferId": "k9f3…", "status": "exchanging", "route": "exchange", "mode": "float", "venue": "binance",
"from": { "chain": "sol", "wallet": "…" }, "to": { "chain": "rh", "address": "0x…" }, "amount": 0.5, "expectedReceive": 0.02156,
"deposit": { "chain": "sol", "address": "…" }, "depositHash": "5Kx…", "payoutHash": null,
"orderId": "…", "receipt": "https://…/normal-exchange/…",
"createdAt": 1792700000000, "updatedAt": 1792700300000
}
```
## If it fails or stalls
When the status is terminal, or the order has not completed 30 minutes after the deposit confirmed, the answer adds `help`: what happened, what to do, our support ([t.me/trenchytools](https://t.me/trenchytools)) and the exchange route's own support (Telegram bot and email), with the exact details to include — `orderId`, `depositHash`, the deposit address, the time, the destination. The provider's name appears only here and in `receipt`.
```json title="help"
{
"orderId": "…", "receipt": "https://…",
"why": "the deposit confirmed more than 30 minutes ago and the order has not completed",
"whatToDo": "In rare cases an order needs a manual nudge at the exchange route. Open the receipt page first; if it shows no progress, contact support with the details below.",
"support": { "trenchytools": "https://t.me/trenchytools", "exchangeRoute": { "telegram": "https://t.me/HusherOfficialBot", "email": "support@husher.io" }, "include": ["this orderId", "your deposit transaction hash (depositHash)", "the deposit address", "the approximate time", "the destination address"] },
"warning": "Never send funds, private keys or seed phrases to anyone claiming to be support."
}
```
`GET /api/transfer` lists your last 50 transfers.
---
# Wallet backups
Source: https://trenchytools-docs-production.up.railway.app/docs/wallets/backups
The rule every app on this API follows: a wallet is written to disk, in a place you can find, before it holds anything.
The API never sees your keys, which means it can never give one back. Bots create wallets constantly — funding wallets, bundle wallets, per-coin wallets, vanity mints — and the most common way money is lost is a key that only ever existed in memory, in a log line, or in a terminal that was closed. So this is a rule, enforced in [AGENTS.md](/docs/build-with-ai) and in every prompt we publish, not a tip.
**Before a wallet is funded, its secret is on disk, and the path has been printed.** An app that cannot show where a key is saved must not send to that wallet.
## The pattern
`wallets/
Log the absolute path the moment the file is written, before anything else happens with that wallet.
The funding step reads the file back (not the in-memory key) and refuses if it is missing.
A command (`npm run export-wallets`) that writes one plain-text file with every address and secret to the user's Desktop, for a password manager or an offline copy. Tell the user to run it after every session that created wallets.
For a hosted app or a shared machine, AES-256-GCM with a key from `ENCRYPTION_KEY`; keep the plain export as the user's own responsibility.
```javascript title="JavaScript"
import fs from 'node:fs'
import path from 'node:path'
import bs58 from 'bs58'
import { Keypair } from '@solana/web3.js'
const DIR = path.resolve('wallets')
export function newWallet(label) {
const kp = Keypair.generate()
const file = path.join(DIR, `${label}-${kp.publicKey.toBase58()}.json`)
fs.mkdirSync(DIR, { recursive: true })
fs.writeFileSync(file, JSON.stringify({ address: kp.publicKey.toBase58(), secretKey: bs58.encode(kp.secretKey), chain: 'sol', label, createdAt: new Date().toISOString() }, null, 2), { flag: 'wx' })
console.log(`wallet saved → ${file}`) // before the wallet is used for anything
return kp
}
export function assertBackedUp(address) {
const ok = fs.readdirSync(DIR).some((f) => f.endsWith(`-${address}.json`))
if (!ok) throw new Error(`refusing to fund ${address}: no backup in ${DIR}`)
}
```
## What the API does on its side
- `GET /api/create-wallet` and the vanity endpoints return a `backup` object (what to save, a checklist) next to the keys, and show the secret once.
- The docs key creator offers "Download as .txt" and never stores a key.
- Transfers and trades are built for addresses you give; the API cannot recover a wallet it never had.
Treat `.env` and `wallets/` as secrets: `.gitignore` them, never paste them into chat with an assistant, never into a public repo.
---
# Upload metadata
Source: https://trenchytools-docs-production.up.railway.app/docs/ipfs
Upload a coin's image and details; get the link you need to create it.
Send a normal file upload (multipart form). The answer works for both chains: Solana creates use `metadataUri`, Robinhood creates use `metadata.image`.
We pin the image and the metadata on IPFS ourselves and answer `https://ipfs.io/ipfs/…` links, the same host every pump.fun coin uses. pump.fun retired its own keyless upload in October 2026, so no Pinata account is needed on your side.PNG, JPG, GIF or WebP, up to 4 MB.Coin name.Ticker.Full links.Default `"true"`.
```bash title="cURL"
curl -X POST "https://api.trenchytools.lol/api/ipfs?api-key=YOUR_API_KEY" \
-F "file=@logo.png" -F "name=My Coin" -F "symbol=MYC" \
-F "description=The best coin" -F "twitter=https://x.com/mycoin"
```
```json title="Response"
{ "metadataUri": "https://ipfs.io/ipfs/bafk…",
"metadata": { "name": "My Coin", "symbol": "MYC", "image": "https://ipfs.io/ipfs/bafy…", … } }
```
---
# Fees
Source: https://trenchytools-docs-production.up.railway.app/docs/fees
What each request costs.
| What | Fee |
| --- | --- |
| Trades you sign (`trade-local`) | **0.5%** of the SOL / ETH side |
| Lightning trades (we sign) | **1%** |
| Creating a coin | No extra fee; the trade fee applies to your dev buy |
| Quotes, token info, balances, status | Free |
| New coins and graduations streams | Free |
| Trade streams | Free during beta |
On Solana the fee is a small transfer **inside** your transaction, so you sign it together with your trade. On Robinhood it is the last transaction in the list, labeled `fee`. Network fees, priority fees, Jito tips and launchpad fees are separate and are not ours.
---
# Usage & key info
Source: https://trenchytools-docs-production.up.railway.app/docs/account
Your key's linked wallets and the last 30 days of usage.
```bash title="cURL"
curl "https://api.trenchytools.lol/api/me?api-key=YOUR_API_KEY"
```
```json title="Response"
{ "keyPrefix": "k7Hx2Pq9", "sol": { "wallet": null }, "robinhood": { "wallet": null },
"usage30d": [ { "chain": "sol", "endpoint": "trade-local", "action": "buy", "n": 42, "volume": 3.1, "fees": 0.0155 } ] }
```
Revoke a key you no longer trust with `POST /api/keys/revoke` and `{ "apiKey": "…" }`, or on [Get an API key](/docs/api-keys).
---
# Errors & limits
Source: https://trenchytools-docs-production.up.railway.app/docs/errors
What each error means and how to fix it.
Every error is JSON: `{ "error": "what went wrong", "code": "short_code" }`.
| Status | Meaning | What to do |
| --- | --- | --- |
| 400 | Something in your request is wrong (a missing field, a bad address, a Robinhood address sent to a Solana endpoint) | Read `error`; it says which field. |
| 401 | Missing or unknown API key | Add `?api-key=…`. Create a key on [Get an API key](/docs/api-keys). |
| 404 | We could not find that coin on this launchpad | Check the address and the chain prefix. For graduated Solana coins use `pool: "auto"`. |
| 409 | The coin moved venues, or your wallet holds none of it | Use `pool: "auto"`, or check your balance. |
| 422 | The transaction would fail on chain (we simulated it), or the venue could not be quoted (`quote_failed`) | Usually slippage or not enough SOL/ETH. `quote_failed`: the pool refused the swap in simulation; a trade is never built with min-out 0 unless you pass `slippage: 100`. |
| 429 | Too many requests | Wait `retry-after` seconds. |
| 502 | A node or Jito refused it | Retry after a moment. |
## Limits
- 300 requests a minute per API key, 25 per second burst (`429` with `retry-after`).
- Up to 4 websocket connections per IP, 500 subscriptions per connection.
- 5 new API keys per hour per IP.
## Error codes
Stable `code` values. Each error also carries `hint` (what to change) and `docs` (a link to the row below).
400 · a field is missing or malformed; the message names it. Field names and types: `openapi.json`.401 · missing or revoked key. Pass `?api-key=` or `x-api-key`; create one with `GET /api/create-api-key`.404 · unknown route, or no token / pool at that address on this chain. `GET /api/chains` lists what exists.404 · the Robinhood routes take pons tokens only; Solana mints go to `/api/sol/…`.404 · not a pump.fun coin, or it graduated: use `pool: "auto"`.409 · the coin is seconds old and its curve is not readable yet; retry in 1–2 s.409 · the coin moved to the AMM; use `pool: "auto"` or `"pump-amm"`.409 · the wallet holds none of this token.409 · no PumpSwap pool yet; trade on the curve.409 · the launchpad is not accepting launches right now.409 · no creator fees are claimable for this wallet.409 · custodial mode is off on this server; use `trade-local`.409 · this key has no linked wallet; use `trade-local` with your own.422 · the venue refused the swap in simulation; a trade is never built with min-out 0 unless `slippage: 100`.422 · the transaction would fail on chain (slippage, balance, stale state). Quote again, raise slippage or lower the amount.422 · over 1232 bytes; fewer buyers per transaction or a lookup table (`POST /api/sol/lut`).400 · `send` / `bundle` relay pump.fun and PumpSwap trades as `trade-local` built them, fee transfer included. A trade with the fee removed is refused on our lanes (any other RPC still accepts it). Rebuild with `trade-local` and send that transaction.400 · the pattern exceeds what our workers grind (4 characters). Grind locally with the SDK.409 / 429 · one running vanity job per key, and a global cap on concurrent jobs. Poll the running one, retry in a minute, or grind locally.503 · exchange-routed transfers are not enabled on this server; `route: "direct"` still works.429 · the shared exchange budget (~10 orders/min) is used up; retry in a minute.400 · the destination address was refused for that network.400 · below the exchange minimum for the pair (`routes` shows it).409 / 503 · that mode or venue is switched off at the exchange right now.400 · that chain is not a supported source (sol, rh) or destination.404 · no transfer with that id under this key.502 · the exchange route failed upstream; nothing was sent.429 · wait `retry-after` seconds; 300/min and 25/s per key, 5 keys per hour per IP.502 · every lane refused the transaction. Check `tx/:signature` before retrying.502 · Jito rejected the bundle; check `bundle/:id`, rebuild with a fresh blockhash and a higher tip.502 · the EVM transaction reverted.502 · every pinning service refused; retry, images under 4 MB.500 · our bug; the message is scrubbed. Retry once, then report it with the request shape.
---
# FAQ
Source: https://trenchytools-docs-production.up.railway.app/docs/faq
Short answers to common questions.
Write every generated key to disk before it is funded and print the path; export all wallets to a file you keep. The API never has your keys and cannot recover one. See [Wallet backups](/docs/wallets/backups).Not on `trade-local`: the transaction is returned unsigned. Lightning signs with a wallet the API holds, created by you under your key.pump.fun slippage guards: 6002 `TooMuchSolRequired` (buy), 6003 `TooLittleSolReceived` (sell). The curve moved past your `slippage` between build and landing; raise it or re-build closer to send.Solana: `pool: "auto"` (or `pump-amm` explicitly). Robinhood: no change, the router targets the v4 pool once the curve has graduated.The first sell of a token is preceded by an `approve` entry. Submit the array in order; wait for the approve receipt before the sell.Telegram [t.me/trenchytools](https://t.me/trenchytools) or X [@trenchytoolz](https://x.com/trenchytoolz). Include the request shape and the error `code`; never your API key or private keys. Exchange-routed transfers that get stuck carry their own `help` block with the route's support.