Real-time rates & alerts

Streaming quotes, live rates and price alerts

Offers arrive over SSE as each exchange answers instead of after the slowest one, the rate feed regenerates every few seconds, and orders are polled to a terminal status — enough to run a price-alert service or a live order board.

  • Sourcesstream · feed · poll
  • StreamSSE, first offer in 200–400 ms
  • Rate feedregenerated every few seconds
  • Order statuspoll every 5–10 s; no webhooks yet

Built on/v1/partner/quotes-sse/v1/export/rates.xml/v1/partner/orders/{id}/v1/partner/orders/{id}/refresh

Two things are genuinely real-time, and one is not — and it is worth knowing which is which before you design around them. Quotes stream over server-sent events as each provider answers. The cross-provider rate feed regenerates every few seconds. Order status is polled: there are no webhooks yet, so the push layer your users see is one you run.

  • /v1/partner/quotes-sse — first offer in 200–400 ms, stream ends by itself.
  • /v1/export/rates.xml — the best executable rate per direction, keyless, always fresh.
  • Orders: poll every 5–10 seconds, or ask for one synchronous refresh.
  • Price alerts are built on the rate feed; status alerts on the poll loop.

Three sources, three cadences

SourceCadenceKeyGood for
GET /v1/partner/quotes-ssePush, per request. Events: offer, private_route, provider_error, ping, done. Ends at timeout_ms — default and maximum 5000.yesA live calculator, a checkout, anything where a user is waiting.
GET /v1/export/rates.xmlRegenerated every few seconds and cached.noRate boards, monitors, price alerts, "1 BTC = …" widgets.
GET /v1/partner/orders/{id}Poll every 5–10 s; our own poller refreshes each open order every 10 s.yesStatus timelines, notifications, reconciliation.
POST /v1/partner/orders/{id}/refreshSynchronous, at most once every 5 s.yes"I've sent it" — one immediate answer, not a faster loop.

The rate feed is an executable rate, not an index. Each <item> is the best cross-provider rate for a direction, with minamount, maxamount and whether it is floating or fixed. An alert built on it fires on a number the user can actually trade at — which is not what a market-data ticker gives you.

Consuming the stream

const res = await fetch(sseURL, {
  headers: { "X-API-Key": key, Accept: "text/event-stream" },
});
const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();

let buf = "";
for (;;) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += value;

  let i;
  while ((i = buf.indexOf("\n\n")) !== -1) {          // one SSE frame
    const frame = buf.slice(0, i); buf = buf.slice(i + 2);
    const event = /^event:\s*(.+)$/m.exec(frame)?.[1];
    const data = JSON.parse(/^data:\s*(.+)$/m.exec(frame)?.[1] ?? "null");

    if (event === "offer") render(data);             // draw it the moment it lands
    if (event === "provider_error") log(data);       // log, never show
    if (event === "done") rerank(data.deviations);   // final ranking of what you drew
  }
}

Building price alerts

  1. Poll the feed, not the quote endpoint

    /v1/export/rates.xml is keyless, cached and covers every direction at once — one request serves every alert you hold. Quoting each user's pair separately is what gets a partner throttled by providers.

  2. Compare against the direction, with its limits

    An alert on BTC → USDTTRC20 is only meaningful within minamount and maxamount. Store both with the threshold so you never notify someone about a rate they cannot take.

  3. Debounce before you notify

    Rates wobble. Require the threshold to hold for two consecutive reads, and re-arm an alert only after the rate has moved back by a margin — otherwise a flat market sends a hundred notifications.

  4. Turn the alert into an offer

    The moment the user acts, quote the pair properly with /quotes or /quotes-sse: the feed says what was available a few seconds ago, and only an offer is executable — for five minutes.

Status alerts work the same way in reverse: one poll loop per open order, your own fan-out to whatever your users listen on — a websocket, a webhook of your own, a Telegram message.

What is not real-time

Not availableWhat to do instead
Webhooks for order statusPoll every 5–10 seconds until terminal, and push to your users yourself. If webhooks would change your integration, say so — it is prioritised by demand.
A persistent websocketThe streaming endpoints are SSE and scoped to one quote request; they end by themselves within five seconds.
Historical rates or candlesThe feed is the current best rate. Keep your own history if you need charts.
Price alerts as a hosted serviceThe thresholds, the storage and the notifications are yours; the API supplies the number they are compared against.
Per-provider live rate streamsOffers arrive per provider inside a quote stream, but there is no standing per-provider feed.

Who runs on this

  • Monitors and aggregators reading rates.xml in the format they already parse.
  • Alert bots — Telegram, Discord, email — firing on an executable rate rather than an exchange ticker.
  • B2B partners with dashboards, streaming their own customers' order statuses out of one poll loop.
  • Wallets and calculators where the number on screen has to move while the user thinks.

Questions

Does SwapZilla send webhooks when an order changes status?

Not yet. Order status is read by polling GET /v1/partner/orders/{id} every 5–10 seconds until a terminal status, and our own poller already refreshes each open order every ten seconds. Webhooks are prioritised by partner demand, so an integration that needs them is worth telling us about.

Is the streaming endpoint a websocket?

No, it is server-sent events over an ordinary HTTP request, and it is scoped to a single quote: offers arrive as providers answer, a ping keeps proxies awake, and the stream closes itself at timeout_ms — 5000 ms by default and by maximum.

How often does the rate feed change?

/v1/export/rates.xml is regenerated every few seconds and cached, so polling it faster than that gains nothing. It publishes the best cross-provider rate per direction with the minimum and maximum amounts and the rate type, in a BestChange-style format.

Can I build price alerts without an API key?

The feed itself is public — /v1/export/rates.xml needs no key, and neither do /v1/assets or /v1/validate-address. A key is needed the moment an alert turns into an actual quote or order.

Why not just quote every user's pair on a timer?

Because providers rate-limit repeated quoting long before SwapZilla would. The feed exists precisely so that watching prices costs one request for everybody, and quoting is reserved for the moment a user is about to trade.

How current is a status I just read?

At most about ten seconds old, since the background poller refreshes every open order at that interval. When a user has just said "I've sent it", POST /v1/partner/orders/{id}/refresh goes to the provider synchronously and returns the updated order — call it once, not in a loop.

Getting a key

Register in the partner cabinet and your partner id and the api_key that goes in X-API-Key are issued at once. The key starts working once it is activated: write to @swapzilla_support_bot with a sentence about what you are building. Every endpoint, field and status is in the partner API reference.