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.
| ID | Slug | Name | Market field | Streams | Modes |
|---|---|---|---|---|---|
1 | US_Equity | US Equities | — (omit) | trade quote | full |
4 | Currencies | Currencies | forex (required) | quote aggregate | full ltp |
crypto (required) | trade quote | full 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
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"}'
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.
| Parameter | Meaning |
|---|---|
tenant | slug or ID; omitted lists every tenant you hold |
market | forex or crypto — narrows tenant 4 to one market |
search | prefix match on symbol, substring match on name |
active | true / false; omitted returns both |
limit, offset | paging; limit defaults to 100, max 1000 |
US Equities
curl -s "https://socket-edith.data.wedafeeds.com/api/v1/tickers?tenant=US_Equity&search=apple&limit=5" \ -H "Authorization: Bearer $TOKEN"
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
curl -s "https://socket-edith.data.wedafeeds.com/api/v1/tickers?tenant=Currencies&market=forex&limit=3" \ -H "Authorization: Bearer $TOKEN"
{"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
curl -s "https://socket-edith.data.wedafeeds.com/api/v1/tickers?tenant=Currencies&market=crypto&search=btc" \ -H "Authorization: Bearer $TOKEN"
{"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.
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.
{"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.
{"action":"subscribe","tenant":"1","tickers":["AAPL","MSFT"]}
{"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.
{"action":"subscribe","tenant":"4","market":"forex","tickers":["EUR-USD","USD/JPY"]}
{"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}}
{"action":"subscribe","tenant":"4","market":"forex","tickers":["EUR-USD"],"mode":"ltp"}
{"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}}
{"action":"subscribe","tenant":"4","market":"crypto","tickers":["BTC-USD","ETH-USD"]}
{"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}}
{"action":"subscribe","tenant":"4","market":"crypto","tickers":["SOL-USD"],"mode":"ltp"}
{"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
{"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"}
{"type":"ack","tenant":"4","market":"crypto","tickers":["BTC-USD","ETH-USD"],"message":"unsubscribed","ts":1789390038058}
{"action":"list"}
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}
{"action":"ping"}
{"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.
| Envelope field | Meaning |
|---|---|
type | always data for market data |
tenant | tenant ID |
market | present only on multi-market tenants (tenant 4) |
symbol | the symbol as you subscribed it |
kind | trade · quote · aggregate · ltp |
data | the event; its own kind and symbol are repeated inside for convenience |
| kind | Fields in data | Notes |
|---|---|---|
trade | price size eventTime tradeId buyerIsMaker? | one frame per execution |
quote | bidPrice bidSize? askPrice askSize? eventTime | forex quotes carry no sizes; much higher volume than trades |
aggregate | open high low close volume eventTime | one-second bar; eventTime is the window start |
ltp | ltp eventTime | only 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.
// 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
marketcolumn. mode- Per subscribe. Omitted means
full.ltpis 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
welcomefirst. - Inbound surface
- Clients can only manage their own subscriptions. There is no publish action.