Plug SpanSwap into your product.
Everything the swap page does, cross-chain quotes, a one-time deposit address per order, live status and refunds to an address you name, over a plain JSON API. Your users never make an account with us and never connect a wallet: your product asks for a quote, creates an order, shows the deposit address, and we tell you when it settles.
The whole swap page, as four calls
Quote
Ask for a price between any two of our 20 networks (13 can be sent from, all of them received). The quote is already net of every cost, so the figure you show your user is the figure that arrives.
Order
Turn a quote into an order and get back a deposit address made for that order alone, the exact amount, the network, and a countdown. Show it, or send your user to our hosted page.
Status
Poll the order, or register a webhook and get every state change pushed to you, with the deposit, bridge and payout transaction hashes as they happen.
Your fee
Add your own fee to each order in basis points. It is folded into the quote your user sees and paid to the address on your account, per order, on the destination chain.
Three steps to a first order
Base URL https://spanswap.com/v1. JSON in, JSON out, HTTPS only. Amounts are decimal strings, never floats; times are ISO 8601 in UTC; chains are lower-case slugs.
Get a key through onboarding
Keys are issued by a person, not a sign-up form, because every key can create orders that move real funds. Tell us what you are integrating and where your fee should be paid, and you get a live key and a dashboard for your orders. It takes one conversation; the form is at the bottom of this page. Already onboarded? Sign in.
Authenticate every call
Send the key as a bearer token. Keys start sk_live_, are shown once, and can be rotated from the dashboard without downtime: the old key keeps working for an hour after the new one is issued. Calls from a browser are refused; the key belongs on your server.
Quote, then order, then listen
A quote is a price with an expiry and costs nothing. Creating an order from it returns the deposit address; from then on the order moves through the same states as on the swap page, and your webhook hears each one. Give every order an Idempotency-Key so a retried request can never create a second deposit address.
Small on purpose
Eight routes cover the whole flow. Everything the swap page can do, and nothing it cannot: there is no endpoint that moves funds on its own, no custody call, and no way to change an order once its deposit address has been issued.
| Method | Route | What it does |
|---|---|---|
| GET | /v1/chains | The networks currently switched on, with whether each can be sent from, its deposit window, and its address format. Read it at start-up rather than hard-coding the list; it changes without notice. |
| GET | /v1/tokens?chain=base | The tokens available on a chain, with decimals, contract address and the current minimum for that token as an output. |
| POST | /v1/quote | A price for from, to and an amount, net of every cost and of your fee, with the estimated arrival, the time to settle and a quote_id that stays valid until expires_at. |
| POST | /v1/orders | Turn a quote into an order. Takes the quote_id, a receiving address on the destination chain and a refund address on the origin chain, and returns the one-time deposit address. Requires Idempotency-Key. |
| GET | /v1/orders/{id} | The order right now: its state, the amounts, and the deposit, bridge and payout transaction hashes as each exists. |
| GET | /v1/orders/{id}/events | Every state change the order has been through, timestamped, oldest first. The same list the webhook delivers, for replay and for reconciliation. |
| GET | /v1/orders?from=…&to=… | Your orders in a time range, paginated, for settlement and for your own fee statements. |
| HOOK | /v1/webhooks | Register the URL we call on every state change, and the secret we sign each delivery with. Deliveries are retried with backoff for 24 hours until your endpoint answers 2xx. |
Quote, order, webhook
The three messages an integration exchanges with us for every order. Field names are stable; new fields are added, never renamed, and a client that ignores fields it does not know keeps working.
The same lifecycle your user sees
An order only ever moves forward, and every state is written to the event log before the webhook fires, so what you replay is what happened. The three terminal states are completed, refunded and expired.
awaiting_deposit
The deposit address is issued and the clock is running. Nothing has been sent yet; if the window closes first the order becomes expired and nothing is lost.
deposit_confirmed
We have seen the deposit and it has enough confirmations. The amount is checked against the quote here; a small variance is bridged as-is, a large one is re-quoted or refunded.
bridging
The swap is in flight on the bridge. The re-check happens just before this state: if a fresh quote would pay more than 1 % less than was quoted, the order stops rather than paying less.
awaiting_fill
The bridge has accepted the transfer and the destination transaction has not landed yet. Usually seconds to minutes; the quote's eta_seconds is the guide.
completed
Funds are at the receiving address and payout_tx is set. Your fee for the order is settled at the same time.
refunded · held
refunded means the deposit went back to the refund address, minus network fees already spent, with refund_tx set. held means a person is looking at the order before it goes further; it resumes or refunds, never silently.
Two ways to earn, one way to hand off
A fee on each order
Set it per quote, from zero up to the cap agreed at onboarding. It is folded into the rate your user sees, never shown as a separate line, and paid to your fee address on the destination chain as each order completes. Your dashboard and GET /v1/orders both show it per order.
Or just a link
No code at all: send users to the swap page with your referral code and earn a share of our fee on the orders they make. Their price is the same either way; the share comes out of our side. Get a code.
The hosted deposit page
Every order comes with a page of ours showing the address, the exact amount, the QR code, the countdown and live status, the same page our own customers use. Send your user there and you need no UI of your own for the deposit step.
What a refusal looks like, and why
Every error is a 4xx with a JSON body: a stable code your integration can branch on and a message written to be shown to a person. We never answer with a bare 5xx page.
| Status | Meaning |
|---|---|
400 | Malformed JSON or a missing field. |
401 | No key, a revoked key, or a key used from a browser. |
404 | No such order on your account. |
409 | The Idempotency-Key was used before with a different body; the original order is in the response. |
422 | A refusal we can explain: below the minimum, above the $500,000 maximum, a bad checksum, an expired quote, no route. |
424 | A network or bridge we depend on did not answer. Safe to retry after a moment. |
429 | Over your rate limit; Retry-After says how long. Quotes and orders have separate limits, agreed at onboarding. |
Idempotency
Every POST /v1/orders carries an Idempotency-Key you choose. The same key with the same body returns the same order, for 24 hours, however many times you send it. A retry can never issue a second deposit address.
Signed webhooks
Each delivery is signed with your webhook secret over the raw body, HMAC-SHA256 in X-SpanSwap-Signature, with a timestamp header to refuse replays. Verify before you act; fetch the order if in doubt.
Addresses checked on our side too
Every address is checksum-validated for its chain before an order is created, EIP-55 on EVM networks included, and a wrong one is refused with a sentence that says so. Validate on yours as well; a transfer to the wrong address cannot be reversed by anyone.
Contact us for a key
One conversation sets everything up: your live key, the address your fee is paid to, your webhook URL and secret, and the limits for your account. Tell us what you are integrating and roughly how much, and it goes straight into the support chat, where a person reads it.
Support chat
The fastest route. A person reads it, and the conversation stays attached to this page.
What onboarding covers
Your key, your fee address, your webhook URL and secret, and your limits. Keys are only ever issued this way: there is no self-serve sign-up, because every key can create orders that move real funds.
See the flow the API wraps
A quote commits you to nothing. The swap page is the same three steps every endpoint above describes.