Developer reference

Wedafeeds Market Data API

Authenticate over HTTPS, look up the instruments you may stream, then hold one WebSocket for every feed you are granted. Every feed speaks the same message format.

APIhttps://socket-edith.data.wedafeeds.com
WSwss://socket-echo.data.wedafeeds.com/ws

Tenants

A tenant is one feed. Your access key names the tenants you may use; the welcome frame on connect tells you what each one carries right now, so treat that frame as authoritative over this table.

IDSlugNameMarket fieldStreamsModes
1US_EquityUS Equities— (omit)trade quotefull
4CurrenciesCurrenciesforex (required)quote aggregatefull ltp
crypto (required)trade quotefull ltp

Tenant 4 carries two markets under one ID. Because a pair like DOP-USD exists in both — a currency and a token — every subscribe on tenant 4 must say which market it means. Tenant 1 carries one market and rejects a market field.

Authentication

Two credentials, two jobs. Your access key is issued to you during onboarding; it is what a client presents to the WebSocket, and it carries your tenant grants and per-client limits. A login token (JWT, from your email and password) is what the HTTPS endpoints such as the ticker list take.

Login

sendPOST /api/v1/auth/login
curl -s -X POST https://socket-edith.data.wedafeeds.com/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"your-password"}'
receive200 — keep accessToken
{"error":false,"statusCode":200,"status":"OK","message":"Login successful",
 "data":{"accessToken":"eyJhbGciOiJIUzI1NiIs…","user":{"id":"6aa7…","email":"you@example.com","name":"…"}}}

Accounts are created for you during onboarding; there is no public sign-up.

Ticker list

The instruments you may subscribe to, scoped to your grants. symbol and market in each row are exactly what a subscribe frame takes.

ParameterMeaning
tenantslug or ID; omitted lists every tenant you hold
marketforex or crypto — narrows tenant 4 to one market
searchprefix match on symbol, substring match on name
activetrue / false; omitted returns both
limit, offsetpaging; limit defaults to 100, max 1000

US Equities

sendGET /api/v1/tickers?tenant=US_Equity&search=apple
curl -s "https://socket-edith.data.wedafeeds.com/api/v1/tickers?tenant=US_Equity&search=apple&limit=5" \
  -H "Authorization: Bearer $TOKEN"
receive200 — no market on single-market rows
{"error":false,"statusCode":200,"status":"OK","message":"Tickers",
 "data":{"tenants":[{"id":"1","slug":"US_Equity"}],"total":3,"limit":5,"offset":0,
  "tickers":[
   {"id":"…","tenant":"1","symbol":"AAPL","name":"Apple Inc.","assetClass":"equity",
    "locale":"us","primaryExchange":"XNAS","type":"CS","currency":"USD","active":true,
    "identifiers":{"cik":"0000320193","compositeFigi":"BBG000B9XRY4"}},
   …]}}

Currencies — forex

sendGET /api/v1/tickers?tenant=Currencies&market=forex
curl -s "https://socket-edith.data.wedafeeds.com/api/v1/tickers?tenant=Currencies&market=forex&limit=3" \
  -H "Authorization: Bearer $TOKEN"
receive200 — 1,204 pairs
{"error":false,"statusCode":200,"status":"OK","message":"Tickers",
 "data":{"tenants":[{"id":"4","slug":"Currencies"}],"total":1204,"limit":3,"offset":0,
  "tickers":[
   {"tenant":"4","symbol":"AED-AUD","name":"United Arab Emirates dirham - Australian dollar","assetClass":"forex","market":"forex","locale":"global","currency":"AUD","active":true},
   {"tenant":"4","symbol":"AED-BHD","name":"United Arab Emirates dirham - Bahraini dinar","assetClass":"forex","market":"forex","locale":"global","currency":"BHD","active":true},
   {"tenant":"4","symbol":"AED-CAD","name":"United Arab Emirates dirham - Canadian dollar","assetClass":"forex","market":"forex","locale":"global","currency":"CAD","active":true}]}}

Currencies — crypto

sendGET /api/v1/tickers?tenant=Currencies&market=crypto&search=btc
curl -s "https://socket-edith.data.wedafeeds.com/api/v1/tickers?tenant=Currencies&market=crypto&search=btc" \
  -H "Authorization: Bearer $TOKEN"
receive200 — 624 pairs in the crypto market
{"error":false,"statusCode":200,"status":"OK","message":"Tickers",
 "data":{"tenants":[{"id":"4","slug":"Currencies"}],"total":7,"limit":100,"offset":0,
  "tickers":[
   {"tenant":"4","symbol":"BTC-AUD","name":"Bitcoin - Australian Dollar","assetClass":"crypto","market":"crypto","locale":"global","currency":"AUD","active":true},
   {"tenant":"4","symbol":"BTC-EUR","name":"Bitcoin - Euro","assetClass":"crypto","market":"crypto","locale":"global","currency":"EUR","active":true},
   {"tenant":"4","symbol":"BTC-USD","name":"Bitcoin - United States dollar","assetClass":"crypto","market":"crypto","locale":"global","currency":"USD","active":true},
   …]}}

A tenant you were not granted is 403; an unknown tenant or a misspelled market is 400. A listing never widens past your grants.

WebSocket

Connect

Present the access key as a header where you can; browsers cannot set headers on the handshake, so the query string is accepted too.

sendhandshake — any one of these
wss://socket-echo.data.wedafeeds.com/ws?apiKey=YOUR_ACCESS_KEY

# or, for non-browser clients:
X-API-Key: YOUR_ACCESS_KEY
Authorization: Bearer YOUR_ACCESS_KEY

The first frame describes what your key may use. available:false means granted but currently down — distinct from not granted. A tenant with a markets array requires market on every subscribe.

receivewelcome
{"type":"welcome","message":"connected","ts":1789390028000,
 "tenants":[
  {"id":"1","name":"US Equities","assetClass":"equity",
   "streams":["trade","quote"],"modes":["full"],"available":true},
  {"id":"4","name":"Currencies","assetClass":"currency",
   "streams":["quote","aggregate","trade"],"modes":["full","ltp"],
   "markets":[
     {"market":"forex", "assetClass":"forex", "streams":["quote","aggregate"],"modes":["full","ltp"]},
     {"market":"crypto","assetClass":"crypto","streams":["trade","quote"],    "modes":["full","ltp"]}],
   "available":true}]}

US Equities — tenant 1

One market, so there is no market field. Omitting mode means full.

sendsubscribe, full
{"action":"subscribe","tenant":"1","tickers":["AAPL","MSFT"]}
receiveack, then a trade and a quote frame per event
{"type":"ack","tenant":"1","tickers":["AAPL","MSFT"],"mode":"full","message":"subscribed","ts":1789390028000}

{"type":"data","tenant":"1","symbol":"AAPL","kind":"trade",
 "data":{"kind":"trade","symbol":"AAPL","price":"224.28","size":"100","eventTime":1788948548063,"tradeId":"52983525029262"}}

{"type":"data","tenant":"1","symbol":"AAPL","kind":"quote",
 "data":{"kind":"quote","symbol":"AAPL","bidPrice":"224.27","bidSize":"3","askPrice":"224.29","askSize":"5","eventTime":1788948548071}}

US Equities currently advertises modes:["full"] — a single print is not a consolidated last price, so ltp is not offered on this tenant. Check the welcome frame; if it lists ltp, the frame below is what you get.

Currencies — tenant 4

market is required. A slash in a pair is accepted and folded to a hyphen, so USD/JPY and USD-JPY are the same subscription — the ack shows the canonical form.

sendforex, full
{"action":"subscribe","tenant":"4","market":"forex","tickers":["EUR-USD","USD/JPY"]}
receiveack, then quote and aggregate frames — forex has no trades
{"type":"ack","tenant":"4","market":"forex","tickers":["EUR-USD","USD-JPY"],"mode":"full","message":"subscribed","ts":1789390028000}

{"type":"data","tenant":"4","market":"forex","symbol":"EUR-USD","kind":"quote",
 "data":{"kind":"quote","symbol":"EUR-USD","bidPrice":"1.15324","askPrice":"1.15329","eventTime":1789389703000}}

{"type":"data","tenant":"4","market":"forex","symbol":"EUR-USD","kind":"aggregate",
 "data":{"kind":"aggregate","symbol":"EUR-USD","open":"1.1529","high":"1.1529","low":"1.1529","close":"1.1529","volume":"1","eventTime":1789393857000}}
sendforex, last price only
{"action":"subscribe","tenant":"4","market":"forex","tickers":["EUR-USD"],"mode":"ltp"}
receiveone ltp frame per one-second bar (its close)
{"type":"ack","tenant":"4","market":"forex","tickers":["EUR-USD"],"mode":"ltp","message":"subscribed","ts":1789390028000}

{"type":"data","tenant":"4","market":"forex","symbol":"EUR-USD","kind":"ltp",
 "data":{"kind":"ltp","symbol":"EUR-USD","ltp":"1.1529","eventTime":1789393857000}}
sendcrypto, full
{"action":"subscribe","tenant":"4","market":"crypto","tickers":["BTC-USD","ETH-USD"]}
receiveack, then trade and quote frames — separate, never merged
{"type":"ack","tenant":"4","market":"crypto","tickers":["BTC-USD","ETH-USD"],"mode":"full","message":"subscribed","ts":1789390028000}

{"type":"data","tenant":"4","market":"crypto","symbol":"BTC-USD","kind":"trade",
 "data":{"kind":"trade","symbol":"BTC-USD","price":"77791.5","size":"0.00101162","eventTime":1789390028834,"tradeId":"1092498786"}}

{"type":"data","tenant":"4","market":"crypto","symbol":"BTC-USD","kind":"quote",
 "data":{"kind":"quote","symbol":"BTC-USD","bidPrice":"77739","bidSize":"0.06431702","askPrice":"77754","askSize":"0.064306","eventTime":1789389693541}}
sendcrypto, last price only
{"action":"subscribe","tenant":"4","market":"crypto","tickers":["SOL-USD"],"mode":"ltp"}
receiveone ltp frame per trade; quotes produce nothing
{"type":"ack","tenant":"4","market":"crypto","tickers":["SOL-USD"],"mode":"ltp","message":"subscribed","ts":1789390028000}

{"type":"data","tenant":"4","market":"crypto","symbol":"SOL-USD","kind":"ltp",
 "data":{"kind":"ltp","symbol":"SOL-USD","ltp":"142.31","eventTime":1789390029102}}

Mode is per subscribe, not per connection: one socket can hold BTC-USD in full and SOL-USD in ltp. Re-subscribing a symbol with a different mode switches it in place.

Unsubscribe, list, ping

sendunsubscribe — some symbols, or everything on a tenant / market
{"action":"unsubscribe","tenant":"1","tickers":["MSFT"]}
{"action":"unsubscribe","tenant":"1"}
{"action":"unsubscribe","tenant":"4","market":"crypto","tickers":["ETH-USD"]}
{"action":"unsubscribe","tenant":"4","market":"crypto"}
receiveack lists exactly what was released
{"type":"ack","tenant":"4","market":"crypto","tickers":["BTC-USD","ETH-USD"],"message":"unsubscribed","ts":1789390038058}
sendlist
{"action":"list"}
receivestatus — tenant:symbol, or tenant:market:symbol on tenant 4
{"type":"status","message":"active subscriptions",
 "tickers":["1:AAPL","4:forex:EUR-USD","4:forex:USD-JPY","4:crypto:BTC-USD"],
 "tenants":[…same shape as welcome…],"ts":1789390038058}
sendping
{"action":"ping"}
receivepong
{"type":"pong","ts":1789390038058}

The server also sends protocol-level pings every ~54 s and expects a pong within 60 s; most WebSocket libraries answer these automatically.

Data frames

Every feed emits the same envelope. Prices and sizes are strings so decimal precision survives; eventTime is epoch milliseconds from the source. Fields absent from an event are omitted, not sent as null.

tradequoteaggregateltp
Envelope fieldMeaning
typealways data for market data
tenanttenant ID
marketpresent only on multi-market tenants (tenant 4)
symbolthe symbol as you subscribed it
kindtrade · quote · aggregate · ltp
datathe event; its own kind and symbol are repeated inside for convenience
kindFields in dataNotes
tradeprice size eventTime tradeId buyerIsMaker?one frame per execution
quotebidPrice bidSize? askPrice askSize? eventTimeforex quotes carry no sizes; much higher volume than trades
aggregateopen high low close volume eventTimeone-second bar; eventTime is the window start
ltpltp eventTimeonly in mode:"ltp"; a projection of a trade or a bar close

Errors

Errors are frames, not disconnects. Each names the tenant it concerns and says what to send instead.

receivecommon error frames
// tenant not on your key
{"type":"error","tenant":"99","message":"access denied for tenant","ts":…}

// tenant granted but not running
{"type":"error","tenant":"4","message":"tenant is not available","ts":…}

// market omitted on tenant 4
{"type":"error","tenant":"4","message":"market is required for this tenant (forex, crypto)","ts":…}

// market sent to a single-market tenant
{"type":"error","tenant":"1","message":"tenant carries a single market; remove \"crypto\"","ts":…}

// typo in market or mode — never silently defaulted
{"type":"error","tenant":"4","message":"unknown market: fx (want \"forex\" or \"crypto\")","ts":…}
{"type":"error","tenant":"4","message":"unknown mode: LAST (want \"full\" or \"ltp\")","ts":…}

// over your per-connection symbol cap
{"type":"error","tenant":"4","message":"ticker limit exceeded: 98 of 100 in use","ts":…}

At the handshake, an invalid key is 401, a key with no grants 403, and a key already at its connection cap 429 — all as JSON, before the upgrade, so your client reads a real message rather than a socket that opens and closes.

Rules & limits

Symbols
Upper-cased and de-duplicated on receipt. Currency pairs are BASE-QUOTE; a slash is folded to a hyphen.
market
Required on tenant 4, refused on tenant 1. The value comes straight from the ticker list's market column.
mode
Per subscribe. Omitted means full. ltp is offered only where the welcome frame lists it.
Idempotence
Subscribing a symbol you already hold does not count against your cap; it re-acks (and may switch the mode).
Symbol cap
Concurrent symbols per connection, across all tenants — your key's maxTickers.
Connection cap
Concurrent sockets per key — your key's maxConnections. Refused at the handshake with 429; existing sockets are never dropped to make room.
Slow consumers
Each socket has a bounded outbound queue. A client that stops reading is disconnected rather than allowed to stall others.
Reconnects
Subscriptions do not survive a disconnect; re-send them after reconnecting. Read the fresh welcome first.
Inbound surface
Clients can only manage their own subscriptions. There is no publish action.