Skip to content
NoetherDocs

Oracle and prices

Read the on-chain Noeracle price for one asset or all sixteen, check oracle and keeper health, and learn the staleness rules that gate trading.

Two public endpoints return the current oracle price for Noether's markets: GET /v1/oracle/prices (all assets at once) and GET /v1/markets/{asset}/price (one asset). Both are live chain reads: the gateway queries the Noeracle shim contract's lastprice function over Soroban RPC, so you get the same price the market contract itself trades against, not an indexer projection. A third endpoint, GET /v1/oracle/health, reports how fresh those prices are.

Base URL: https://noether-api.proudmeadow-533cf0d8.germanywestcentral.azurecontainerapps.io. No authentication is required. Price responses are cached in the gateway for 3 seconds per asset. For the full price chain (who signs prices, how they land on-chain and the guards around them) see Oracle.

Price format

Every price object has the same shape:

FieldTypeMeaning
assetstringAsset symbol, for example BTC
pricestringPrice as a 7-decimal fixed-point integer, serialized as a string. Divide by 10,000,000 to get dollars: "763827177778" is $76,382.7177778
priceFloatnumberThe same price pre-converted to a float, for convenience
timestampintegerUnix seconds: the attestation time of the price currently stored on-chain

Use the price string when you need exact math (it round-trips the on-chain integer value losslessly); use priceFloat for display. This 7-decimal string convention applies to money, price and size fields across the API; the one exception is candle OHLC values, which are plain floats.

GET /v1/oracle/prices

Returns the current oracle price for all supported assets, in the same order as GET /v1/markets.

Auth: none. Params: none.

curl -s https://noether-api.proudmeadow-533cf0d8.germanywestcentral.azurecontainerapps.io/v1/oracle/prices

Response 200 (trimmed to two of the sixteen entries):

{
  "prices": [
    { "asset": "BTC", "price": "763827177778", "priceFloat": 76382.7177778, "timestamp": 1789683237 },
    { "asset": "ETH", "price": "24448744444", "priceFloat": 2444.8744444, "timestamp": 1789683237 }
  ]
}

The full response contains one entry per supported asset: BTC, ETH, XLM, SOL, XRP, ADA, BNB, TRX, HYPE, DOGE, ZEC, LINK, BCH, LTC, PUMP and UNI at the time of writing. GET /v1/markets is the source of truth for the list.

GET /v1/markets/{asset}/price

Returns the current oracle price for a single asset.

Auth: none.

ParameterInTypeNotes
assetpathstringAsset symbol, for example BTC. Case-insensitive (uppercased server-side). Validated against the supported list; unknown symbols return 404
curl -s https://noether-api.proudmeadow-533cf0d8.germanywestcentral.azurecontainerapps.io/v1/markets/BTC/price

Response 200:

{ "asset": "BTC", "price": "763827177778", "priceFloat": 76382.7177778, "timestamp": 1789683237 }

Response 404 for an unsupported asset:

{ "error": "Unknown asset", "asset": "FOO" }

The same price object is also embedded in GET /v1/markets and GET /v1/markets/{asset} as the oracle field; see Markets and market data. If you need every asset, prefer /v1/oracle/prices over N single-asset calls.

GET /v1/oracle/health

Per-source oracle health: the age of the on-chain price for every asset plus the keeper's last self-report. This is the endpoint to point an uptime monitor at.

Auth: none.

ParameterInTypeNotes
strictquerystringPass strict=1 to get HTTP 503 instead of 200 whenever status is not ok, so a status-code monitor can page
curl -s https://noether-api.proudmeadow-533cf0d8.germanywestcentral.azurecontainerapps.io/v1/oracle/health

Response 200 (trimmed):

{
  "status": "ok",
  "onchain": {
    "staleAfterSec": 60,
    "staleCount": 0,
    "assets": [
      { "asset": "BTC", "priceFloat": 76382.7177778, "ageSec": 24, "stale": false, "error": null },
      { "asset": "ETH", "priceFloat": 2444.8744444, "ageSec": 24, "stale": false, "error": null }
    ]
  },
  "keeper": {
    "configured": true,
    "ageMs": 37400,
    "stale": false,
    "lastReport": { "ts": 1789683224523, "pushed": ["BTC", "ETH", "XLM"], "stats": { "oracleUpdates": 3238, "priceSkips": 0, "errors": 0 } }
  }
}
FieldMeaning
statusok, degraded (some assets stale, or the keeper signal is stale or unknown), or down (every asset stale)
onchain.staleAfterSecThe age at which a price counts as stale here: 60 seconds, matching the market contract's staleness limit
onchain.assets[]Per asset: the current priceFloat, its ageSec, the stale flag, and error ("read_failed" when the chain read failed, with null price and age)
keeper.configuredfalse when the gateway has no keeper heartbeat configured, in which case the keeper signal is unknown rather than healthy
keeper.ageMs, keeper.staleAge of the last keeper report; stale once it is older than two minutes
keeper.lastReportThe keeper's own status body verbatim (assets pushed, cycle counters, and a custody block); its shape can change between keeper versions, so treat it as diagnostic data

The companion POST /v1/oracle/heartbeat is a machine-to-machine ingest for the keeper, authenticated by a shared secret header; it answers 401 unauthorized to anyone else and is not part of the public surface.

Staleness semantics

The timestamp field is the attestation time of the price currently stored on-chain, not the time of your HTTP request. The keeper refreshes prices continuously; /v1/oracle/health tells you the current age per asset.

Two things to know when consuming it:

  • The gateway never rejects a stale price on the read endpoints. It returns whatever the shim last stored, with its timestamp. If you need a freshness guarantee, compare timestamp against your own clock or watch onchain.assets[].stale.
  • The chain enforces freshness for trading. The market contract rejects risk-increasing operations (opening positions, executing entry orders) when the on-chain price is older than 60 seconds, returning contract error 30 (PriceStale). Risk-reducing operations, closes and liquidations, are never blocked by staleness. Trades placed through the web app go through the Noether Router, which relays a fresh signed price in the same transaction, so they cannot hit this limit. Transactions built by the API's prepare and submit flow call the market contract directly and rely on the keeper's regular price pushes instead; if the on-chain price has gone stale, a prepare fails simulation or a submit fails with error 30. Details in Oracle.

Allow up to 3 seconds of additional lag from the gateway's per-asset cache. For a streaming feed instead of polling, subscribe to the ticker.<ASSET> channel on the WebSocket API; it pushes the same shim-read price every 3 seconds.

Rate limits

All three endpoints count against the public tier: 60 requests per minute per IP. Sending a valid API key with X-Timestamp (even on these public endpoints) moves you to your wallet's bucket at 600 requests per minute. Every response carries X-RateLimit-Tier and X-RateLimit-Remaining headers; exceeding the limit returns 429 with a Retry-After header.

Next steps

On this page