Crypto and Digital-Asset Data APIs for AI Agents
Reading a price and moving an asset are not the same risk. How to split market data from custody, and why a crypto transfer is the one call you never let an agent make alone.
Every API an agent touches has an undo button somewhere, except this one. A wrong Stripe refund can be reversed. A wrong email can be followed by an apology. A wrong on-chain transfer is just gone, and there is no endpoint, support ticket or escalation path that brings it back.
That single fact should shape how you wire crypto APIs to an agent. The good news is that most of what people actually want from a crypto agent is reading, not moving, and those two things can be kept completely separate.
Two categories that get lumped together
When someone says "crypto API" they usually mean one of two very different things.
- Market and chain data. Prices, order books, historical candles, on-chain balances, transaction lookups. Read-only. The worst case if the credential leaks is that someone else reads public-ish data on your quota.
- Custody and transfer. Platforms like Paxos and BitGo that hold assets and can move them. The worst case if that credential leaks is the number on your balance sheet.
Teams tend to set up one integration, one credential, one agent, and then wonder why security review stalls for six weeks. Split them. Different credentials, different allowlists, different policies, and ideally different agents entirely.
Start with the data side, because it is genuinely easy
A read-only agent is a nice first project. Set the project to sandbox, pull the provider, connect the key, and allow only the read methods.
npx swytchcode
swy login
swy init --mode=sandbox
swy get paxos # or bitgo, or your data provider
swy auth connect paxos # pastes an API key, or opens OAuth
swy auth status
# find the read methods only
swy discover "get balances" --project paxos
swy discover "list transactions" --project paxos
swy list methods paxos
swy info <canonical_id> # check what it takes and returns
swy add method <canonical_id> # allow ONLY reads at this stageNotice what is missing: you never added a transfer or withdrawal method. That is not a setting you can talk the model out of. The capability does not exist in this project, so no prompt injection, no confused reasoning step and no clever instruction in a document the agent reads can produce a transfer. It is an absent capability, not a rule.
Keep the credentials separate while you are at it. Swytchcode stores them in a local SQLite store outside your project folder, encrypted at rest with the key in the OS keychain, and injects them at call time so they never enter the model's context. A prompt asking the agent to print its API key finds nothing, because there is nothing there to print. More on that split in credential brokering for AI agents and the auth docs.
If you must allow a transfer, make it boring
Some workflows genuinely need movement: sweeping a hot wallet into cold storage on a schedule, settling a payout run, rebalancing. Three rules we would not ship without.
- Approval on every transfer, no threshold exceptions. For reversible systems a value threshold makes sense. For irreversible ones, start with all of them and relax later if the volume genuinely demands it.
- Destination allowlisting in the policy, not in the prompt. The agent should be structurally unable to send to an address that is not on a known list.
- A daily cap. Not because you expect a runaway loop, but because the cost of being wrong about that assumption is unbounded.
{
"id": "all-transfers-need-a-human",
"target": ["<custody transfer canonical_id>"],
"when": { "field": "amount", "operator": ">", "value": 0 },
"action": {
"type": "REQUIRES_APPROVAL",
"message": "Agent is requesting an on-chain transfer. This cannot be undone."
},
"approval_timeout": "1h"
}The > 0 condition is deliberate: it catches every transfer rather than only big ones. Held calls go to Slack or Telegram and fail closed, so if nobody responds in the window the transfer simply does not happen. That is the correct default for something irreversible. Approval setup is in the human approval docs, and the design argument is in human-in-the-loop approval for AI agent tool calls.
Idempotency, where it matters more than usual
A duplicated read costs you a bit of quota. A duplicated transfer costs you the transfer. And the dangerous case is not a deliberate retry, it is the ambiguous one: the request left, the response never came back, and nothing in your process knows whether the money moved.
Swytchcode will only auto-retry a POST or PATCH when an idempotency key is set on it, so an unprotected transfer fails loudly instead of quietly running twice. Set a deterministic key derived from the actual operation, something like the payout ID plus the amount, so a retry from anywhere in your stack collapses onto the original request rather than creating a second one. The reasoning is in the complete idempotency guide and the implementation in the idempotency docs.
Also: check what your specific custody provider supports before relying on it. Support for idempotency keys varies, and so do the retention windows. Verify in their current docs rather than assuming the pattern from Stripe applies.
Rate limits and the chatty price agent
Market data endpoints are where agents get themselves rate limited, because polling feels free until you have a loop checking six assets every few seconds. Rate-limit errors get retried with exponential backoff automatically, which is the right behaviour for a read. The thing to watch is your own design: cache a price for a few seconds rather than fetching it fresh for every reasoning step. A model does not know that calling the same endpoint eleven times in one turn is rude.
Watch for the soft failure too. Some data APIs return a 200 with an error object or a stale-data flag in the body, which standard monitoring reads as success. Response validation catches that case; the pattern is written up in 200 OK but it failed.
Running it from an MCP client
If you want this in Claude Code or Cursor rather than in your own service, connect through the MCP server and the same allowlist and policies apply. This is a good fit for the read-only data agent and a bad fit for anything with transfer access, because an editor session is not where you want an irreversible operation to be one tool call away. Setup is in the MCP setup guide, the reference in the MCP docs, and the command conventions for a coding agent in skills.md.
What you can build
- A treasury reporting agent. Reads balances across custody accounts and chains every morning, writes a position summary, flags anything that moved more than a threshold. Read-only, genuinely useful, ships in an afternoon. Pairs with the thinking in using bank APIs with treasury AI agents.
- A reconciliation agent. Compares on-chain transactions against your internal ledger and produces a list of unmatched items for a human. It finds the discrepancy; it does not fix it.
- A monitoring and alerting agent. Watches for large inbound deposits, unusual withdrawal patterns or a balance crossing a floor, and posts to Slack with context. Still no write access to anything financial.
- A sweep agent, with approval. Moves funds from a hot wallet to cold storage when a threshold is crossed, every single time asking a human first and only to a pre-allowlisted destination. This is the most write access we would give, and it is deliberately dull.
What a security reviewer will ask
Three questions, in this order, and it is worth having the answers ready. Where do the credentials physically live? What can the agent reach at all? How would you prove what it did? For the last one, swy audit network gives you every call that ran and swy audit policy gives you every one that was refused, which together is the evidence package. More on what auditors actually accept in API compliance for AI agent integrations.
FAQ
Can I use one credential for both data and custody?
You can, and we would not. Two credentials in two projects means the read-only agent physically cannot move an asset even if something goes very wrong with it. One credential means your only protection is configuration, and configuration drifts.
What if the agent needs to quote a price before a transfer?
Let it read freely and propose the transfer as a held call. The human approving it sees the amount and destination in the approval message, which is a better review than reading a reasoning trace afterwards.
Is sandbox mode enough for testing?
For wiring and logic, yes. Set it once with the project rather than through an environment variable, since mode lives in the project config. Just remember that test and live keys for custody providers often differ by a few characters in the middle of a long string, so verify which one is actually connected before you trust a successful call.
Related reading
More content
How to Debug an AI Agent That Called the Wrong API
Working backwards from the response your agent got to the tool-selection decision that caused it, which is where most of the real debugging time goes.
iPaaS Was Built for Integrations, Not Agents
Deterministic trigger-action versus non-deterministic tool choice, and why the governance model that works for one doesn't transfer cleanly to the other.
Using the Airtable API from an AI Agent
Someone renames a field in the UI and your agent breaks with an error saying the field does not exist, while you are looking right at it. Airtable's schema drift problem, solved.
