{
    "openapi": "3.1.0",
    "info": {
        "title": "BTCSwap public API",
        "version": "1.0.0",
        "summary": "Instant crypto-to-crypto exchange: catalogue, live quotes, address validation, order creation and status. No key, no sign-up.",
        "description": "The same JSON endpoints BTCSwap runs on: 1,283 asset-network pairs, live quotes with network fees and limits, checksum address validation, order creation (the one call with an effect) and live order status. No API key. Be gentle: cache the catalogue, poll status at most every ten seconds, and never build your own address check. Terms: https://btcswap.io/terms — no SLA, and we say so.",
        "termsOfService": "https://btcswap.io/terms",
        "contact": {
            "name": "BTCSwap support",
            "url": "https://btcswap.io/support"
        }
    },
    "servers": [
        {
            "url": "https://btcswap.io"
        }
    ],
    "externalDocs": {
        "description": "Human reference with runnable consoles",
        "url": "https://btcswap.io/api"
    },
    "tags": [
        {
            "name": "swap",
            "description": "List assets → quote → validate the address → create → follow."
        }
    ],
    "paths": {
        "/api/currencies": {
            "get": {
                "operationId": "currencies",
                "summary": "List the assets",
                "description": "The whole catalogue in one file: every asset on every network the desk can route, already sorted (most traded first, then stablecoins, then alphabetical). It is what the asset picker of this site is built from. It is large — fetch it once and cache it.",
                "tags": [
                    "swap"
                ],
                "responses": {
                    "200": {
                        "description": "JSON. A business refusal is still HTTP 200 with ok: false, a stable code and a message written for a human.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "v": {
                                            "type": "string",
                                            "description": "Build identifier. When it changes, the shape of the rows may have changed too."
                                        },
                                        "stats": {
                                            "type": "object",
                                            "description": "assets, networks, entries (asset · network pairs) and fixed (entries that can be rate-locked)."
                                        },
                                        "pop": {
                                            "type": "array",
                                            "items": {
                                                "type": "string"
                                            },
                                            "description": "Legacy tickers of the most traded assets, in display order."
                                        },
                                        "items.l": {
                                            "type": "string",
                                            "description": "Legacy ticker — the ID used by every other endpoint. It carries the network: usdttrc20 and usdterc20 are two entries."
                                        },
                                        "items.t · n": {
                                            "type": "string",
                                            "description": "Ticker (usdt) and full name."
                                        },
                                        "items.w · wn · wl": {
                                            "type": "string",
                                            "description": "Network code, network name, and the badge to display — wl is empty when the network adds nothing (BTC on Bitcoin)."
                                        },
                                        "items.i": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "description": "Icon file name, served from /assets/coins/."
                                        },
                                        "items.f · s · x": {
                                            "type": "string",
                                            "enum": [
                                                "0",
                                                "1"
                                            ],
                                            "description": "Can be quoted at a fixed rate · is a stablecoin · uses a memo / destination tag."
                                        },
                                        "items.b · e": {
                                            "type": "string",
                                            "enum": [
                                                "0",
                                                "1"
                                            ],
                                            "description": "Can be received · can be sent. An entry with b: 0 is send-only."
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/quote": {
            "get": {
                "operationId": "quote",
                "summary": "Get a live quote",
                "description": "One pair, one amount: what arrives, at what rate, within which limits, with both network fees and an arrival forecast. This is the call the exchange module makes on every keystroke.",
                "tags": [
                    "swap"
                ],
                "parameters": [
                    {
                        "in": "query",
                        "name": "from",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "example": "btc"
                        },
                        "description": "Legacy ticker of the asset you send."
                    },
                    {
                        "in": "query",
                        "name": "to",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "example": "xmr"
                        },
                        "description": "Legacy ticker of the asset you receive."
                    },
                    {
                        "in": "query",
                        "name": "amount",
                        "required": true,
                        "schema": {
                            "type": "number",
                            "example": "0.1"
                        },
                        "description": "In the from asset. A dot or a comma both parse."
                    },
                    {
                        "in": "query",
                        "name": "flow",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "standard",
                                "fixed-rate"
                            ],
                            "example": "standard"
                        },
                        "description": "standard (floating, default) or fixed-rate. Falls back to standard when the pair cannot be locked — read the flow that comes back."
                    },
                    {
                        "in": "query",
                        "name": "lock",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "0",
                                "1"
                            ]
                        },
                        "description": "Fixed rate only. 0 returns the locked price without reserving it — use it for display; a real reservation is made when the order is created."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "JSON. A business refusal is still HTTP 200 with ok: false, a stable code and a message written for a human.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "ok": {
                                            "type": "boolean",
                                            "description": "false is a business answer (amount too small, pair paused), still HTTP 200."
                                        },
                                        "toAmount · rate": {
                                            "type": "number",
                                            "description": "What arrives, and the effective rate. Both network fees and the margin are already inside."
                                        },
                                        "fromAmountFmt · toAmountFmt · rateFmt": {
                                            "type": "string",
                                            "description": "The same numbers, formatted for display."
                                        },
                                        "min · max": {
                                            "type": [
                                                "number",
                                                "null"
                                            ],
                                            "description": "Live limits of the pair, in the from asset. Filled in even when ok is false — that is what lets an interface offer “use the minimum”."
                                        },
                                        "depositFee · withdrawalFee": {
                                            "type": [
                                                "number",
                                                "null"
                                            ],
                                            "description": "Network fee on the deposit side (in from) and on the payout side (in to). Informational: do not subtract them again."
                                        },
                                        "speed": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "description": "End-to-end forecast in minutes, as a range: \"10-60\"."
                                        },
                                        "flow · fixedOk": {
                                            "type": "string",
                                            "description": "The rate type actually quoted, and whether this pair can be locked at all."
                                        },
                                        "validUntil": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "description": "Only on a locked fixed-rate quote: when the reservation lapses."
                                        },
                                        "from · to": {
                                            "type": "object",
                                            "description": "Both sides, ready to display: l, t, n, w, wl, i (icon URL)."
                                        },
                                        "code · message": {
                                            "type": "string",
                                            "description": "On refusal: a stable machine code and a sentence written for a human."
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/validate": {
            "get": {
                "operationId": "validate",
                "summary": "Validate an address",
                "description": "Checks an address against one asset’s network — checksum included — before anyone commits to it. This is the call worth wiring into your own interface: it is the difference between catching a wrong-network address while someone is typing and catching it never. Capped globally at a few hundred checks a minute, all callers together — over it, valid comes back null. Same-origin: no CORS header is sent. Prefer a page? The address validator is this endpoint with an interface.",
                "tags": [
                    "swap"
                ],
                "parameters": [
                    {
                        "in": "query",
                        "name": "asset",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "example": "btc"
                        },
                        "description": "Legacy ticker. The network is implied by it, which is the entire point: usdttrc20 and usdterc20 validate differently."
                    },
                    {
                        "in": "query",
                        "name": "address",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "example": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
                        },
                        "description": "The address to check, 200 characters at most. The example is the public BIP-173 test vector."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "JSON. A business refusal is still HTTP 200 with ok: false, a stable code and a message written for a human.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "valid": {
                                            "type": [
                                                "boolean",
                                                "null"
                                            ],
                                            "description": "Treat null as “unknown”, never as “invalid”. It means the checker could not answer (engine unreachable, or the global cap below). Blocking a legitimate user because a checker was down is the worse failure."
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "Empty when valid; otherwise a sentence you can show as is."
                                        },
                                        "ok": {
                                            "type": "boolean",
                                            "description": "false only when the check itself could not run."
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/create": {
            "post": {
                "operationId": "create",
                "summary": "Create an exchange",
                "description": "The one endpoint with an effect. It opens a real exchange at the desk and returns an order ID — the receipt, and the only thing to keep. Body is application/x-www-form-urlencoded. There is no Run button on purpose: calling this opens a real order and generates a deposit address. Nothing moves until a deposit is sent, and an unfunded order simply expires.",
                "tags": [
                    "swap"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "from · to · amount · flow": {
                                        "type": "string",
                                        "description": "As in /api/quote. For fixed-rate, the price is reserved at this moment."
                                    },
                                    "address": {
                                        "type": "string",
                                        "example": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
                                        "description": "The payout address. Validated again server-side, whatever your interface did first. All whitespace is stripped."
                                    },
                                    "extra": {
                                        "type": "string",
                                        "description": "Memo or destination tag, only where the receiving network uses one (x: 1 in the catalogue). Omit it rather than sending it empty."
                                    },
                                    "refund": {
                                        "type": "string",
                                        "description": "Optional, strongly recommended: where the deposit returns if the exchange cannot complete."
                                    }
                                },
                                "required": [
                                    "from · to · amount · flow",
                                    "address"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "JSON. A business refusal is still HTTP 200 with ok: false, a stable code and a message written for a human.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "ok · id": {
                                            "type": "string",
                                            "description": "On success. The exchange then lives at /order/<id> and answers on /api/status."
                                        },
                                        "code": {
                                            "type": "string",
                                            "description": "On refusal: not_valid_address, deposit_too_small, deposit_too_big, pair_is_inactive, no_rate_id…"
                                        },
                                        "message": {
                                            "type": "string",
                                            "description": "The reason, written for a human."
                                        },
                                        "field": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "description": "Set when one specific input is at fault — address today."
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "405": {
                        "description": "Only POST is accepted."
                    }
                }
            }
        },
        "/api/status": {
            "get": {
                "operationId": "status",
                "summary": "Follow an exchange",
                "description": "The live state of one exchange: status, both sides, addresses, and the transaction hashes once they exist. Never cached — a stale swap status is a lie with financial consequences. Poll it every ten seconds at most; the order page of this site does exactly that. Same-origin, and treat the ID accordingly: an order ID is a bearer credential for reading that order.",
                "tags": [
                    "swap"
                ],
                "parameters": [
                    {
                        "in": "query",
                        "name": "id",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "The order ID: 8 to 40 letters and digits."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "JSON. A business refusal is still HTTP 200 with ok: false, a stable code and a message written for a human.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": {
                                            "type": "string",
                                            "description": "new · waiting → confirming · verifying → exchanging → sending → finished. Terminal: failed, refunded, expired."
                                        },
                                        "payin · payinExtra": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "description": "The deposit address, and its memo when the deposit network needs one — mandatory in that case."
                                        },
                                        "payout": {
                                            "type": "string",
                                            "description": "Where the coins are going."
                                        },
                                        "expectFrom · expectTo": {
                                            "type": [
                                                "number",
                                                "null"
                                            ],
                                            "description": "What the order was created for."
                                        },
                                        "amountFrom · amountTo": {
                                            "type": [
                                                "number",
                                                "null"
                                            ],
                                            "description": "What was actually received and sent, once known."
                                        },
                                        "payinHash · payoutHash": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "description": "Transaction hashes, as soon as each leg is on-chain."
                                        },
                                        "createdAt · validUntil": {
                                            "type": [
                                                "string",
                                                "null"
                                            ],
                                            "description": "Creation time, and the deposit deadline of a fixed-rate order."
                                        },
                                        "notFound": {
                                            "type": "boolean",
                                            "description": "Only with ok: false: no order carries that ID."
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/tape": {
            "get": {
                "operationId": "tape",
                "summary": "Price strip",
                "description": "A compact strip of major assets quoted in USDT — the one in the hero of the home page, and at the top of this one. Cheap to poll: cached for a minute at the edge.",
                "tags": [
                    "swap"
                ],
                "responses": {
                    "200": {
                        "description": "JSON. A business refusal is still HTTP 200 with ok: false, a stable code and a message written for a human.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "at": {
                                            "type": "integer",
                                            "description": "Unix time of the answer."
                                        },
                                        "rows.t · q": {
                                            "type": "string",
                                            "description": "Asset and quote currency: BTC, USDT."
                                        },
                                        "rows.rate · fmt": {
                                            "type": "string",
                                            "description": "USDT received for one unit on a sizeable swap, network fees included — raw, and formatted."
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}