TxShield API
Stop wasting SOL on dead transactions. Drop an unsigned tx in, get back the fee bucket your bot needs to land, the slippage the route will actually take, the simulation outcome, and a per-vector risk breakdown — typically in under 80 ms.
Overview
Every Solana transaction faces the same three execution risks before it lands: it gets dropped or stuck because the priority fee is in the wrong bucket, the route slips harder than the wallet UI estimated, or it reverts at execution for a reason the wallet never surfaced. TxShield runs all three checks pre-flight in a single /simulate call so wallets and bots can either bump the fee, switch the route, abort the tx, or proceed with confidence.
The same call also runs the safety checks — program blacklist, sandwich-attacker proximity, rug-pull / honeypot, durable-nonce, flash-loan / oracle. MEV / sandwich detection is currently production beta and treated as advisory; the execution-quality vectors (landing, slippage, simulation, fees) are GA.
Base URL: https://api.txshield.dev. All endpoints under /api/v1/. JSON in, JSON out. UTF-8. ISO 8601 timestamps in UTC.
You only need one endpoint to ship.
POST /api/v1/solana/tx/simulate drives every vector in this guide. Webhooks, batching and streaming are convenience layers over the same call.
01Quickstart
Five minutes from npm install to your first verdict.
1. Get an API key
Sign in to the dashboard and create a key under Settings → API keys. Each key is scoped to one project, lives in one of three modes (test, live, internal) and can be rotated or revoked at any time.
Never put a live key in client-side code.
Use scoped session tokens for browser/mobile origins. They expire after 15 minutes and are bound to a single user wallet.
2. Pick how you call us
The API is plain HTTP — every endpoint is a one-line curl. Official TypeScript and Python SDKs are shipping under Beta Milestone 1 of the Solana Foundation Developer Tooling grant; until they land on npm / PyPI, use curl or generate a client from the OpenAPI 3.1 spec with the generator of your choice.
# nothing to install — just curl + your keyexport TXSHIELD_KEY="txshield_live_..."
# ships under Beta Milestone 1 — track at /changelognpm install @txshield/sdk # not yet published
# ships under Beta Milestone 1 — track at /changelogpip install txshield # not yet published
3. First request
curl -X POST "https://api.txshield.dev/api/v1/solana/tx/simulate" \ -H "X-API-Key: $TXSHIELD_KEY" \ -H "Content-Type: application/json" \ -d '{"transaction": "<base64-versioned-tx>", "policy": "wallet-default"}'
// SDK shape — ships under Beta Milestone 1 (Solana Foundation grant).import { TxShield } from "@txshield/sdk";const tx = new TxShield({ key: process.env.TXSHIELD_KEY! });const v = await tx.solana.simulate({ transaction: unsignedB64, // VersionedTransaction.serialize() → base64});if (v.risk.risk_level === "critical") { throw new Error(`refused: ${v.failure_reason}`);}
# SDK shape — ships under Beta Milestone 1 (Solana Foundation grant).from txshield import TxShieldtx = TxShield(key=os.environ["TXSHIELD_KEY"])v = tx.solana.simulate(transaction=unsigned_b64)if v.risk.risk_level == "critical": raise RuntimeError(f"refused: {v.failure_reason}")
4. Read the verdict
The response envelope is the same shape on every chain.
{
"success": true,
"compute_units": 28104,
"suggested_compute_limit": 31000,
"fee_estimate_sol": 0.0000125,
"priority_fees": {
"low": 1000, "medium": 10000,
"high": 100000, "very_high": 1000000,
"samples": 312
},
"slippage": { "bps": 7, "route": "jupiter-v6" },
"risk": {
"mev_score": 0.04, "mev_level": "none", "mev_type": null,
"has_blacklisted_program": false,
"blacklisted_programs": [],
"dex_programs": ["JUP6L…AG4"],
"token_risk": null,
"flash_loan_risk": { "detected": false, "oracle_manipulation": false },
"durable_nonce": { "detected": false, "address": null },
"risk_level": "low"
},
"landing_estimate": {
"fee_percentile_bucket": "low",
"submitted_fee_microlamports": 1000,
"recommended_fee_for_high_landing": 12500
},
"recommendations": [],
"program_breakdown": […],
"call_tree": […]
}
02Authentication
Two layers, picked by call site:
- API keys (
X-API-Key: txshield_live_…) — for server-to-server traffic. Created via the dashboard orPOST /api/v1/api-keys. Three modes:test,live,internal. - JWT bearer tokens (
Authorization: Bearer eyJ…) — for the dashboard SPA itself and any human-driven flow. 24h expiry on access, 30d on refresh. Four ways to obtain one (see below).
Both authenticate the same endpoints. Where you use which is a security choice — never put an API key in client-side code.
Four sign-in methods (all issue a JWT bearer)
| Method | Flow | Endpoints |
|---|---|---|
| Solana wallet | Sign-In-With-Solana: dashboard asks the wallet (Phantom, Backpack, Solflare, Glow) to sign a server-issued nonce. We verify the ed25519 signature, upsert by wallet pubkey, and issue a JWT. No password. | POST /auth/wallet/challengePOST /auth/wallet/login |
| Google OAuth | Standard authorization-code flow. HttpOnly state cookie for CSRF. Upserts by Google subject ID, issues JWT via URL fragment redirect. | GET /auth/oauth/google/startGET /auth/oauth/google/callback |
| GitHub OAuth | Same shape as Google. Useful when devs already have a GitHub SSO session. | GET /auth/oauth/github/startGET /auth/oauth/github/callback |
| Email + password | Classic. Argon2id hashing. Refresh-token rotation. | POST /auth/registerPOST /auth/login |
Wallet sign-in — the wire shape
Two calls. First fetch a nonce; second submit the signature.
# 1. Get a challenge for this pubkeycurl -X POST "https://api.txshield.dev/auth/wallet/challenge" \ -H "Content-Type: application/json" \ -d '{"pubkey": "<base58-wallet-address>"}'# → { "nonce": "...", "message": "txshield.dev wants you to sign in...\nNonce: ...\nIssued: ..." }# 2. Sign the `message` with the wallet, then submitcurl -X POST "https://api.txshield.dev/auth/wallet/login" \ -H "Content-Type: application/json" \ -d '{"pubkey": "...", "signature": "<base58-ed25519-sig>", "nonce": "..."}'# → { "token": "eyJ...", "refresh_token": "eyJ...", "user": { ... } }
Nonces expire after 5 minutes and are single-use. The signed message follows the SIWS draft so wallet UIs render it as a human-readable prompt, not opaque bytes.
Scoped session tokens (browser-safe)
For wallet/mobile origins that already have a JWT, mint a short-lived session token server-side and hand it to the client. Tokens expire after 15 minutes and are bound to a single user wallet so leakage has a tiny blast radius.
03Solana — single simulation
POST/api/v1/solana/tx/simulateThe core of the product. Submit one unsigned VersionedTransaction serialized as base64. We simulate it against mainnet state, run the 14 detection vectors in parallel, and return a single envelope.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| transaction | string · base64 | required | Serialized VersionedTransaction (or legacy Transaction) — the bincode produced by tx.serialize(), base64-encoded. Signature slots may be empty/zero; we don't verify them. |
| policy | string | optional | Named policy preset. Default: wallet-default. Other values: strict (lower abort thresholds, suitable for high-value), bot (latency-optimised, mev + simulation only). |
| vectors[] | string[] | optional | Restrict which detection vectors run. Default: all. Common subsets: ["mev","slippage","rug","program"]. |
| timeout_ms | int | optional | Server-side timeout cap. Default 800. Anything below 80 will fall back to cached priority-fee data only. |
| abort_on_active_threat | bool | optional | Default true. When the verdict carries an active threat (drainer program, OFAC-listed account, confirmed-attacker proximity), return decision: "ABORT" instead of just risk-scoring it. Renamed from abort_on_critical_risk in May 2026. |
| abort_on_flash_loan_oracle | bool | optional | Default true. Aborts if the tx pattern matches flash-loan + oracle-read sequencing typical of price-manipulation attacks. Shipped May 2026 (beta). |
| abort_on_durable_nonce | bool | optional | Default false. Aborts if the tx uses an AdvanceNonceAccount instruction (durable nonce). Most wallets do not use durable nonces; presence often signals a delayed-broadcast attack pattern. Shipped May 2026 (beta). |
Response — top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
| success | bool | required | Did the inner Solana simulateTransaction succeed? false just means the tx would revert — the verdict envelope is still useful. |
| compute_units | int? | optional | CU consumed during the simulated execution. Null if the tx didn't reach execution. |
| suggested_compute_limit | int? | optional | CU limit we recommend you set on the real submit (typically compute_units × 1.1). |
| fee_estimate_sol | float? | optional | Estimated fee in SOL at current cluster prices, including priority fees. |
| priority_fees | object | required | P25/P50/P75/P95 of getRecentPrioritizationFees for the programs touched. Use medium as a default tip; jump to high when latency matters. |
| slippage | object? | optional | Worst-case slippage in basis points, plus the route used. Populated when a DEX program is detected. |
| risk | object | required | Per-vector breakdown — see Detection vectors. Includes flash_loan_risk and durable_nonce nested objects as of May 2026. |
| landing_estimate | object? | optional | Where your submitted priority fee sits in the current validator percentile distribution. Includes fee_percentile_bucket (one of low/medium/high/very_high/unknown), submitted_fee_microlamports, and recommended_fee_for_high_landing (a concrete uplift target if you are in the low bucket). Shipped May 2026. |
| recommendations[] | array | required | Concrete actions you can take (e.g. add a priority fee, increase slippage tolerance, switch route). Empty when nothing's wrong. |
| program_breakdown | array | optional | Per-program CU + invocation count, useful for cost attribution and debugging. |
| call_tree | array | optional | CPI tree extracted from simulation logs. Helpful when a tx fails mid-flight. |
| failure_reason | string? | optional | Human-readable explanation of why success is false (when applicable). |
Solana — batch simulation
POST/api/v1/solana/tx/simulate-batchUp to 50 transactions in one round-trip. Use it when your bot evaluates multiple candidate routes per block — saves the round-trip latency on N-1 of them.
Order is preserved.
Results return in the same order as the input array. No partial-failure quirks — if one tx errors, only that index's success is false; others continue.
Solana — streaming verdicts
WSwss://txshield.dev/api/v1/solana/tx/simulate-streamWebSocket connection for sub-50 ms verdicts. Send a JSON envelope per line; receive verdicts as they finish. Authenticate with the ?api_key=… query param at connect time. Connection idle-timeout 60s — send a ping if you've gone quiet.
Simulation history
GET/api/v1/solana/simulationsPaginated list of past simulations on this account. Query params: limit (default 25, max 200), cursor (opaque string from previous page), since/until (ISO 8601), risk_level, has_blacklisted_program.
Retention: 90 days by default; up to 7 years for volume customers (negotiated per tenant).
Aggregate stats
GET/api/v1/solana/simulations/statsPre-computed aggregates for the dashboard widget: total sims, 24h count, success rate, P50/P95/P99 latency. Refreshed every 60s.
04Detection · MEV / sandwich
Production beta — treat verdicts as advisory.
The MEV vector is fully shipped to mainnet and processing live traffic, but per-DEX precision is still being calibrated against rolling ground truth. Use mev_score and mev_level for signal, not a hard block, until the methodology paper lands under Beta Milestone 2 of the Solana Foundation grant. The execution-quality vectors (priority-fee, slippage, simulation, landing estimate) are GA.
Detects front-run + back-run pairs against monitored Solana DEX programs (Jupiter, Raydium, Orca, Meteora, Lifinity). Returns:
risk.mev_score— float 0.00–1.00. Aggregate likelihood the tx will be sandwiched at submit-time.risk.mev_level— bucketed:none,low,medium,high,critical.risk.mev_type— concrete pattern when high:"sandwich","jit","backrun_only".
Treat high/critical as a hard refusal in production. medium usually means you should re-route through a private mempool or wait for the next block.
Detection · Rug-pull / honeypot
Engaged when the tx touches an SPL-token program. Checks:
- Mint authority + freeze authority status (active = high risk).
- LP-lock duration; lock unlocks < 7 days fire
medium. - Top-10 holder concentration. > 80% fires
high. - Sell-test simulation — would a sell tx revert? If yes →
criticalhoneypot.
Detection · Slippage overflow
Pulls live route quotes from Jupiter Price API at submit-time priority fees — not the stale UI estimate. Returns slippage.bps as worst-case basis points and slippage.route as the chosen route. Use recommendations to surface alternative routes when bps > your threshold.
Detection · Program safety
Curated and customer-submitted blacklist of malicious programs anywhere in the CPI tree. Categories:
| Category | What it means |
|---|---|
drainer | Known wallet drainers — auto-block on every account. |
rug_deployer | Programs that have rugged at least once historically. |
fake_token | Counterfeit USDC/USDT/etc. minted to mimic legitimate tokens. |
honeypot | Sell-disabled programs (catch buyers who can't exit). |
phishing | Programs used by phishing kits (drain on sign). |
compromised | Once-legitimate programs whose authority has been compromised. |
Per-tenant overrides for volume customers — extend the list with your own blocklist/allowlist.
Detection · Priority fee
Live getRecentPrioritizationFees sampling per program. Returned as p25/p50/p75/p95 inside priority_fees. Use the medium percentile as the default tip; jump to high when landing latency matters more than fee cost.
Detection · Pre-flight simulation
The basis for everything else. Solana simulateTransaction with full pre-execution against mainnet state. Returns balance changes, CPI call tree, per-program compute units, and revert reasons in compute_units, program_breakdown, call_tree, failure_reason.
05Webhooks
POST/api/v1/solana/simulations/webhooksRegister a URL to receive verdict events in real-time. We POST a JSON payload to your URL whenever an event of a type you subscribed to fires. Default delivery latency: < 200 ms after the originating event.
Subscribable event types
| Event | Fires when |
|---|---|
| simulation.completed | Verdict landed for a /simulate call. Filtered by risk_level ≥ your configured threshold. |
| simulation.high_risk | Convenience alias — fires only when risk_level is high or critical. |
| tx.submitted | A /submit call cleared the abort gates and was relayed to the configured submit path (direct, staked sender, or managed-tip Jito). |
| tx.finality | Post-submit finality monitor confirms a tx is finalized, dropped, or reverted. Useful for closing out bot trade lifecycles without polling the RPC. Shipped May 2026. |
| alert.threshold_breach | A configured alert (rate-anomaly, MEV-spike, plan-quota) crossed its threshold. |
Signature verification
Every webhook delivery carries an X-TxShield-Signature header — HMAC-SHA256 over the raw body, signed with your endpoint's secret. Verify in your handler before trusting the payload:
import { createHmac, timingSafeEqual } from "node:crypto";function verify(rawBody: Buffer, signature: string, secret: string) { const expected = createHmac("sha256", secret).update(rawBody).digest("hex"); return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));}
Retry policy
If your endpoint returns a non-2xx status (or doesn't respond within 8s), we retry with exponential backoff: 30s, 2m, 10m, 1h, 6h. After the 5th attempt we mark the delivery as failed; you can manually replay it from the dashboard. Idempotency: each delivery carries a stable delivery_id in the body — use it to dedupe on your side.
06API keys
GET/api/v1/api-keys POST/api/v1/api-keys DEL/api/v1/api-keys/{id} POST/api/v1/api-keys/{id}/rotateManage keys programmatically or via the dashboard. The cleartext key is only returned once — at creation. After that you only see the prefix (e.g. txshield_live_) and metadata (requests_count, last_used_at, created_at).
Rotation is graceful: the new key activates immediately, the old one stays valid for 24h, then auto-revokes. No downtime.
07Billing
Pure pay-as-you-go. 1,000 simulations per day on the house, then €0.002 per simulation past that, billed at the end of the calendar month. No tiers, no monthly fee, no commitment, no feature gating — every account gets every detection vector, webhooks, batch and WebSocket from day one. A separate /submit relay tier charges €0.003 per billable submit-strategy call (staked sender, managed-tip Jito); direct and auto-direct submits are free. Jito tips pass through at exact cost.
| Free quota | Metered (after card added) | |
|---|---|---|
| Price | €0 | €0.002 per simulation past the daily free tier |
| Daily allowance | 1,000 sims / day · resets 00:00 UTC | Same 1,000 / day still free, then metered |
| Rate limit | 10 rps | 100 rps · burst 250 |
| Behaviour past quota | 429 with Retry-After | Continues serving · €0.002 per call |
| Detection vectors | All 14 | All 14 |
| Features | Webhooks · WebSocket · batch | Webhooks · WebSocket · batch · custom rules · per-tenant blocklist |
| SLA | Best-effort | P50 ≤ 80 ms · 99.9 % uptime |
| Support | Community Discord | Email + Slack · 24h response |
Above ~5M sims / month? Volume customers get a negotiated per-call rate, a dedicated regional cluster (99.99% SLA), SOC 2 / ISO 27001 evidence pack, and Slack-Connect with 1-hour pager response. Email hello@txshield.dev.
Refunds & disputes
Because this is pay-as-you-go, every line on your monthly invoice corresponds to a specific API call we logged. You can query the underlying records via GET /api/v1/solana/simulations (simulations) or GET /api/v1/solana/submissions (relay submits) to verify any charge against your own logs.
If a charge is wrong — double-billed, metering glitch, service degradation that materially affected your usage — email hello@txshield.dev within 14 days of the invoice date with the invoice number and a brief description. We refund or credit verified billing errors within 5 business days. Stripe disputes through your card issuer also work, but are slower and noisier; we would rather hear from you first.
Cancellation: cancel any time at /billing or via the Stripe Customer Portal. The current billing period is not prorated — you keep API access through end of month, then your account reverts to the 1,000-sim/day free tier (no data loss, no key rotation required).
EU consumer right of withdrawal (§ 312g BGB) applies only to B2C purchases. Business accounts (USt-IdNr provided at signup) are commercial contracts under § 14 BGB and not subject to the 14-day withdrawal window; the refund window above is a voluntary policy we offer regardless.
Endpoints:
GET/api/v1/billing/usage GET/api/v1/billing/subscription POST/api/v1/billing/checkout POST/api/v1/billing/portalcheckout creates a Stripe Checkout session for the metered subscription and returns a redirect URL. portal creates a Customer Portal session for managing payment methods, invoices, and cancellation. Adding a card just lifts the throttle — you're never charged for the first 1,000/day.
08Errors
HTTP status codes are load-bearing. Read them.
| Code | Meaning | What to do |
|---|---|---|
| 200 | OK | Read the verdict envelope. |
| 400 | Bad request | Validation error in request shape — check error.message. |
| 401 | Unauthenticated | Missing or invalid API key / JWT. |
| 403 | Forbidden | Plan-gated feature (e.g. custom rules on Free tier). |
| 404 | Not found | Resource doesn't exist or isn't visible to your account. |
| 409 | Conflict | Idempotency conflict — same key, different body. |
| 422 | Unprocessable | Request shape OK but content is invalid (e.g. tx fails to deserialize). |
| 429 | Rate limited | Backoff — see Rate limits. Honour Retry-After header. |
| 500 | Server error | Our problem — automatic alert fires. Retry with backoff. |
| 503 | Service unavailable | Region failover in progress. Retry with backoff. |
All error responses share a JSON shape:
{
"error": {
"code": "invalid_transaction",
"message": "failed to deserialize VersionedTransaction: …",
"request_id": "req_01HVZX9P3J4D2TJYP91D"
}
}
09Rate limits
Per-API-key rate limits, enforced at the edge. Burst window: 1 second.
| Steady-state | Burst | Behaviour | |
|---|---|---|---|
| Free quota | 10 rps | 25 rps | Throttle — 429 with Retry-After. |
| Metered (card on file) | 100 rps | 250 rps | Throttle past burst. |
| Volume | Negotiated | Negotiated | Per-tenant. |
Headers on every response:
X-RateLimit-Limit— your steady-state cap.X-RateLimit-Remaining— calls left in the current second.X-RateLimit-Reset— Unix epoch when the bucket refills.Retry-After— seconds to wait (only on 429).
10SDKs
Until the official SDKs ship under Beta Milestone 1 of the Solana Foundation Developer Tooling grant, curl + a generated client from the OpenAPI spec is the supported path. Every endpoint is doc-equivalent to a one-line curl; see the request examples above.
- TypeScript SDK (
@txshield/sdk) — ships under Beta Milestone 1. ESM + CJS,fetch-based, Node 18+ / Bun / Deno / Cloudflare Workers / Vercel Edge. Not yet on npm. - Python SDK (
txshield) — ships under Beta Milestone 1. Sync + async clients, Python 3.10+. Not yet on PyPI. - txshield-cli — also a Beta Milestone 1 deliverable. Terminal tool for ad-hoc
simulateand safety-feed queries without writing code. - curl — every endpoint is doc-equivalent to a one-line curl. See request examples above.
Other languages: use the OpenAPI spec at /openapi.json with your generator of choice. We test against openapi-typescript, openapi-python-client, and oapi-codegen (Go); pull-requests welcome for additional generators once the grant SDKs land.
11Changelog
- 2026-05-27 — Wallet-first auth shipped: Sign-In-With-Solana via Phantom / Backpack / Solflare / Glow, plus Google + GitHub OAuth. Email + password kept as the fourth option. See Authentication.
- 2026-05-27 —
landing_estimateadded to/simulateresponse: tells you which validator-fee percentile bucket your submitted priority fee falls into, with a concrete uplift target if you're in thelowbucket. - 2026-05-25 — Threat-model expansion:
flash_loan_risk+durable_nonceresponse fields, plus matchingabort_on_flash_loan_oracleandabort_on_durable_noncerequest params. Renamedabort_on_critical_risk→abort_on_active_threat(old name still accepted for one release; deprecation header set). - 2026-05-24 —
tx.finalitywebhook event: post-submit finality monitor confirmsfinalized/dropped/revertedwithout RPC polling. - 2026-05-06 — Pricing simplified to pure pay-as-you-go: 1,000 sims/day free, €0.01/sim past that. No tiers, no monthly fee. Stripe metered billing live.
- 2026-05-05 — Dashboard SPA migrated to its own niso package; nginx routing fixed via panel-proxy.
- 2026-04-24 — TxShield API adopted into niso package management. No customer impact.
- 2026-04-21 — Helius RPC degradation incident (9 min, partial latency increase). Resolved.
- 2026-04-09 — Webhook delivery backlog (22 min, no drops). Improved retry backoff curve.