Vector Market DataAPI Reference
← Back to site

Vector Market Data API

Real-time Brazilian market data (B3) delivered over a plaintext JSON WebSocket, plus REST snapshots and native AI interfaces (MCP & A2A). One API key governs every surface, with per-plan entitlements, limits and auditing.

Base URL

https://md.vectrading.com

How you connect

  • WebSocket — live order books, the primary streaming surface. Authenticated with a short-lived connection token.
  • REST — point-in-time snapshots, catalog and history. Same connection token.
  • MCP / A2A — for LLMs and agents. Accept the API key directly (the gateway exchanges it server-side).

Data profiles

Every symbol belongs to one profile; your plan is entitled to a subset.

ProfileContents
futOutright futures and spreads (WDO, DOL, WIN, IND, DI1, …)
eventsEvent contracts and special listings (Copom, etc.)
equitiesCurated liquid B3 equities
optionsAt-the-money equity, index & dollar options — front expiration only, ~6 strikes each side of spot (WDO/WIN option series included). Tickers follow the B3 pattern (e.g. BB3#BWD… for dollar, BB3#BWI… for index).

Symbol format

Symbols use the BB3#<TICKER> form, e.g. BB3#PETR4, BB3#WDON26. A bare ticker is auto-prefixed with BB3# and upper-cased server-side.

Authentication

All access is keyed to an API key (vmd_live_…) created in your dashboard. There are two credential models depending on the surface:

SurfaceCredential
WebSocket, REST (/mdgateway/*)A short-lived connection token (JWT) obtained from the API key — see below.
MCP (/mcp), A2A (/a2a/*)The API key sent directly as Authorization: Bearer vmd_live_…; the gateway exchanges it for you.
Why a token for the stream? The long-lived API key stays in your backend. It is exchanged for a short connection token (default 5 min) that the WebSocket and REST surfaces accept. A token expiring does not drop a connection that is already open.

Connection tokens

POST/api/connection-tokens

Exchange your API key for a short-lived connection token (JWT, HS256).

Request

Send the API key in the Authorization header (or as an api_key body field). No body is required.

curl -X POST https://md.vectrading.com/api/connection-tokens \
  -H "Authorization: Bearer vmd_live_YOUR_KEY"

Response 200

{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJIUzI1Ni␣...",   // the connection token
  "expires_at": "2026-06-30T19:40:00Z",
  "expires_in": 300,
  "account": { "id": "12", "name": "Acme Quant", "status": "active" },
  "plan": {
    "slug": "beta",
    "name": "Beta",
    "max_concurrent_connections": 3,
    "max_symbols": 50,
    "min_interval_ms": 250,
    "history_days": 30,
    "profiles": ["fut", "equities"],
    "ai_interfaces": ["mcp", "a2a"]
  }
}

Use access_token as the token for the WebSocket and REST surfaces. Refresh before expires_in elapses to open new connections.

WebSocket streaming

WSS/ws/v1?token=<access_token>

Connect with the connection token either as the token query parameter or an Authorization: Bearer <access_token> header. On connect the server sends a hello frame with your effective limits; then you subscribe and receive book frames.

Minimal browser client

// 1) Get a connection token from your backend (POST /api/connection-tokens)
const token = "YOUR_CONNECTION_TOKEN";

const ws = new WebSocket("wss://md.vectrading.com/ws/v1?token=" + token);

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "subscribe", symbols: ["BB3#PETR4", "BB3#WDON26"] }));
};

ws.onmessage = (ev) => {
  const msg = JSON.parse(ev.data);
  if (msg.type === "book") {
    const bestBid = msg.book.bids[0];   // { price, size }
    const bestAsk = msg.book.asks[0];
    console.log(msg.symbol, bestBid, bestAsk);
  }
};

Conflation & keepalive

  • Book updates are conflated per symbol and flushed every min_interval_ms (from your plan) — you get the latest book per symbol per interval, not every raw tick.
  • The server sends a WebSocket ping every 20s; standard clients auto-pong. You may also send {"type":"ping"} and receive {"type":"pong"}. Idle connections are closed after ~70s without traffic.

Message reference

Client → server

CommandShape
subscribe{"type":"subscribe","symbols":["BB3#PETR4"]}
unsubscribe{"type":"unsubscribe","symbols":["BB3#PETR4"]}
ping{"type":"ping"}

Server → client

hello — sent once on connect:

{
  "type": "hello", "seq": 1,
  "stats": {
    "account_id": "12", "plan": "beta",
    "max_symbols": 50, "max_concurrent_connections": 3, "min_interval_ms": 250
  }
}

subscribed / unsubscribed — acknowledge accepted symbols:

{ "type": "subscribed", "seq": 2, "symbols": ["BB3#PETR4", "BB3#WDON26"] }

book — the full market-data snapshot for a symbol: order book depth and trade data (last/high/low, the trade tape, and a volume-by-price profile). Emitted at most once per min_interval_ms per symbol (conflated).

{
  "type": "book", "seq": 25,
  "profile": "equities", "symbol": "BB3#PETR4",
  "time": 1782848877.916,
  "book": {
    "code": "BB3#PETR4",
    "time": 1782848877.916,
    "status": "O",
    "ticksize": 0.01,
    "mid": 37.805,
    "settle": null,
    "lasttrade": 37.81,
    "hightrade": 37.85,
    "lowtrade": 37.40,
    "theoprice": null,
    "expiry": 0,
    "bids": [ { "price": 37.79, "size": 3400 }, { "price": 37.78, "size": 21000 } ],
    "asks": [ { "price": 37.80, "size": 19000 }, { "price": 37.81, "size": 14300 } ],
    "implieds": { "bids": [], "asks": [] },
    "icebergs": [ { "rowprice": 37.75, "side": 1 } ],
    "trades": [
      { "price": 37.81, "size": 200, "side": 1, "cond": "@", "time": 1782848877.910 }
    ],
    "trademap": { "prices": [3780, 3781, 3785], "vols": [431700, 122600, 186700] }
  }
}

book fields

Order book

FieldTypeMeaning
bids / asksarray{ price, size } levels, best price first.
impliedsobjectImplied levels for spreads/options: { bids, asks }, each { price, size }.
icebergsarrayDetected iceberg levels: { rowprice, side }.

Trades & prices

FieldTypeMeaning
lasttradefloatLast traded price (null if none yet).
hightrade / lowtradefloatHighest / lowest traded price of the session.
midfloatMid price (best bid/ask).
settlefloatSettlement price (null intraday).
theopricefloatTheoretical price during auction (TC_THEO).
tradesarrayThe trade tape — executions since the previous message for this symbol. [] when none occurred in the window. Element shape below.
sending_timefloatExchange-side packet clock (B3 SendingTime, epoch seconds) of the latest feed event — use it to measure end-to-end latency against time.
trademapobjectCumulative volume-by-price profile: { prices: [...], vols: [...] } (parallel arrays). prices[i] is an integer tick index — actual price = prices[i] × ticksize; vols[i] is total traded volume there (buy + sell summed, not split by side).

Metadata

FieldTypeMeaning
codestringSymbol (same as the envelope symbol).
timefloatBook timestamp (epoch seconds).
statusstringInstrument status: O open, P pre/post/paused/auction.
ticksizefloatMinimum price increment.
expiryfloatContract expiration (epoch seconds; 0 for spot equities).

Trade element (each item of trades):

{
  "price":  37.81,        // executed price
  "size":   200,          // executed quantity
  "side":   1,            // aggressor: +1 buy, -1 sell, 0 unknown
  "cond":   "0",          // trade condition (see table below)
  "time":   1782848877.91,// epoch seconds (gateway-side event clock)
  "id":     "388231",     // exchange fill id — STRING-encoded uint64;
                          // monotonic per symbol within the session;
                          // "0" for summary trades (cond "S")
  "buyer":  3,            // buyer participant/broker code (0 = unknown)
  "seller": 120           // seller participant/broker code (0 = unknown)
}

Trade conditions (cond)

condMeaning
0Regular trade
RRLP trade (Retail Liquidity Provider)
STrade summary — aggregate mirror of the classified prints. Per window, vol(S) = vol(0) + vol(R). Deduplicate by keeping only 0/R.
HTheoretical price print (auction / pre-open; feeds theoprice)
XCross trade
WSweep
1Spread leg
2Marketplace entered
3Multi-asset
IImplied
EIceberg
Notes. trademap accumulates only cond "0" volume (RLP excluded). status values observed: O open, P pre/post/auction/paused, C closed.
Note. Trade-derived fields are populated during trading. Outside trading hours (status: "P") or when no trade occurred in the refresh window, trades is [] and lasttrade/trademap may be empty/null — the book (bids/asks) is still delivered.

error — rejected commands or symbols:

{
  "type": "error", "seq": 7,
  "code": "subscribe_rejected",
  "message": "one or more symbols were rejected",
  "details": [
    { "symbol": "BB3#XYZ", "code": "unknown_symbol", "message": "symbol not found in gateway cache" }
  ]
}

REST — snapshots & catalog

Point-in-time reads of the same cache the stream uses. Authenticate with the connection token (Authorization: Bearer <access_token> or ?token=). All paths are under the /mdgateway/ prefix.

GET/mdgateway/book?symbol=BB3#PETR4
TOKEN=$(curl -s -X POST https://md.vectrading.com/api/connection-tokens \
  -H "Authorization: Bearer vmd_live_YOUR_KEY" | jq -r .access_token)

curl "https://md.vectrading.com/mdgateway/book?symbol=BB3#PETR4" \
  -H "Authorization: Bearer $TOKEN"
GET/mdgateway/snapshot?symbols=BB3#PETR4,BB3#VALE3

Returns { "snapshots": [...], "rejected": [...] } for one or more symbols.

GET/mdgateway/catalog?q=PETR&profile=equities&limit=100

Searches currently-cached symbols visible to your plan. Returns { "symbols": [{symbol, profile, time, raw_at}], "count", "limit" }.

History

Currently unavailable. The ClickHouse history plane is disabled in the current deployment. /mdgateway/history, MCP get_history and A2A getHistory return an error until it is re-enabled. Live snapshots and streaming are unaffected.

MCP

POST/mcp

JSON-RPC 2.0 Model Context Protocol endpoint (protocol 2025-06-18). Authenticate with Authorization: Bearer vmd_live_… (the API key directly). Methods: initialize, tools/list, tools/call, ping.

Tools

ToolArguments
get_gateway_status
get_coverage
search_symbolsquery, profile, limit
get_snapshotsymbol or symbols[]
get_booksymbol (req), depth, include_raw
get_historysymbol, start, end (currently disabled)
curl -X POST https://md.vectrading.com/mcp \
  -H "Authorization: Bearer vmd_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"get_book","arguments":{"symbol":"BB3#PETR4","depth":5}}}'

Returns a normalized book (summary with best bid/ask/mid/spread plus trimmed levels) — MCP does not stream the raw feed; use the WebSocket for that.

A2A

Agent-to-Agent interface. The agent card is public; capability calls require the API key.

GET/.well-known/agent-card.json public
POST/a2a/agents/marketdata

JSON-RPC message/send; the capability name goes in the message text. Capabilities: getGatewayStatus, getCoverage, listSymbols, getSnapshot, getBook, getHistory.

curl https://md.vectrading.com/a2a/agents/marketdata \
  -H "Authorization: Bearer vmd_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"message/send",
       "params":{"message":{"role":"user","parts":[{"kind":"text","text":"listSymbols"}]}}}'

Errors & limits

Connection-token errors HTTP 401

codeMeaning
missing_api_keyNo Authorization: Bearer / api_key provided.
invalid_api_keyKey not recognized.
key_inactiveKey revoked or not active.
key_expiredKey past its expiry.
account_or_plan_inactiveAccount disabled or plan not active.

WebSocket errors

WherecodeMeaning
UpgradeHTTP 401Missing/invalid connection token.
UpgradeHTTP 429Concurrent-connection limit for your account reached.
Commandbad_jsonCommand was not valid JSON.
Commandunknown_commandType is not subscribe/unsubscribe/ping.
Subscribeunknown_symbolSymbol not in the gateway cache.
Subscribeprofile_not_entitledYour plan is not entitled to the symbol's profile.
Subscribesymbol_limit_exceededPer-connection symbol limit reached.

Plan limits

LimitEffect
max_symbolsMax symbols subscribed per connection.
max_concurrent_connectionsMax simultaneous WebSocket connections per account.
min_interval_msConflation interval — minimum gap between book frames per symbol.
history_daysMaximum history look-back window (when history is enabled).
profilesWhich data profiles your plan may access.