API Conventions

How the API is structured, and the patterns for querying and submitting data.
View as Markdown

The API is a low-level, HTTP-based interface you can use to query data, submit orders, create alerts, receive notifications — almost everything available in the trader application. It splits into two parts: endpoints that query data (typically GET) and endpoints that modify or submit data (typically POST). With rare exceptions, a path consists of an entity name and an operation type, such as account/find or order/cancelorder.

The API exposes data with fine granularity so it doesn’t limit how your application composes it. Client applications are responsible for requesting the dependencies they need and joining them. To calculate open profit and loss, for example, you’d pull from at least two domains: your positions data and real-time market data to compare those positions against live quotes.

Querying Data

Query operations use the HTTP GET method.

By ID

Every entity that the API exposes has a unique ID. The item operation retrieves an entity by that ID:

cURL
$curl -X GET --header 'Accept: application/json' 'https://demo.tradovateapi.com/v1/order/item?id=1000'
C#
1Account result = apiInstance.AccountItem(1000);
JavaScript
1const URL = 'https://demo.tradovateapi.com/v1'
2
3const response = await fetch(URL + '/order/item?id=1000', {
4 headers: {
5 Authorization: `Bearer ${myAccessToken}`,
6 'Content-Type': 'application/json'
7 }
8})
9
10const order = await response.json()

All Entities at Once

The list operation retrieves every entity of a type:

cURL
$curl -X GET --header 'Accept: application/json' 'https://demo.tradovateapi.com/v1/fill/list'
JavaScript
1const URL = 'https://demo.tradovateapi.com/v1'
2
3const response = await fetch(URL + '/fill/list', {
4 headers: {
5 Authorization: `Bearer ${myAccessToken}`,
6 'Content-Type': 'application/json'
7 }
8})
9
10const fills = await response.json()

By Master-Details Dependency

The deps operation loads only the entities owned by or dependent on another entity. Pass the master entity’s ID as the masterid parameter. For example, all positions owned by the account with ID 123:

cURL
$curl -X GET --header 'Accept: application/json' 'https://demo.tradovateapi.com/v1/position/deps?masterid=123'
JavaScript
1const URL = 'https://demo.tradovateapi.com/v1'
2
3const response = await fetch(URL + '/position/deps?masterid=123', {
4 headers: {
5 Authorization: `Bearer ${myAccessToken}`,
6 'Content-Type': 'application/json'
7 }
8})
9
10const positions = await response.json()

By Name

Some entities — such as accounts, products, contracts, and currencies — have names. Use the find operation with a name parameter, for example to find the product named “ES”:

cURL
$curl -X GET --header 'Accept: application/json' 'https://demo.tradovateapi.com/v1/product/find?name=ES'
JavaScript
1const URL = 'https://demo.tradovateapi.com/v1'
2
3const response = await fetch(URL + '/product/find?name=ES', {
4 headers: {
5 Authorization: `Bearer ${myAccessToken}`,
6 'Content-Type': 'application/json'
7 }
8})
9
10const es = await response.json()

Batch Loading

To load multiple entities at once, replace several item calls with a single items call and pass a comma-separated ids parameter. Similarly, ldeps batches deps requests with a masterids parameter.

cURL
$curl -X GET --header 'Accept: application/json' 'https://demo.tradovateapi.com/v1/contract/items?ids=840972%2C840944'
JavaScript
1const URL = 'https://demo.tradovateapi.com/v1'
2
3const response = await fetch(URL + '/contract/items?ids=840972%2C840944', {
4 headers: {
5 Authorization: `Bearer ${myAccessToken}`,
6 'Content-Type': 'application/json'
7 }
8})
9
10const contracts = await response.json()

A batch response may contain fewer entities than the IDs you requested (when an ID isn’t found), and the entities may come back in a different order than you requested them. Don’t rely on the response order matching the request order.

Submitting Data

Use the HTTP POST method to submit data, formatted as a valid JSON object in the request body. Submit-operation names don’t follow a general rule — each reflects the semantics of its use. For example, order/placeorder requests a new order and order/liquidateposition requests to cancel all orders for a position.

JavaScript
1const URL = 'https://demo.tradovateapi.com/v1'
2
3const body = {
4 accountSpec: yourUserName,
5 accountId: yourAcctId,
6 action: "Buy",
7 symbol: "MYMM1",
8 orderQty: 1,
9 orderType: "Market",
10 isAutomated: true // must be true if this isn't placed directly by a human
11}
12
13const response = await fetch(URL + '/order/placeorder', {
14 method: 'POST',
15 headers: {
16 'Accept': 'application/json',
17 'Authorization': `Bearer ${myAccessToken}`,
18 },
19 body: JSON.stringify(body)
20})
21
22const json = await response.json() // { orderId: 0000000 }

Error Handling

The API reports errors in two ways: HTTP status codes and a custom errorText field on some endpoints.

  • HTTP status codes apply mostly when data or an operation is unavailable (HTTP 404), inaccessible (HTTP 401 or HTTP 403), or rate-limited (HTTP 429 or HTTP 423). These prevent you from reaching an endpoint.
  • The errorText field describes business-level violations in text. This happens when your request succeeds at the HTTP level but server-side business logic rejects it. The text usually explains what went wrong.

Automated Orders

When you place an order through code or an automated process — a trading bot, for example — you must set the isAutomated flag in your request body. It defaults to false, which is appropriate for an order a human places by clicking. Any order placed by a bot or algorithmic process must set it to true.

The exchange takes the isAutomated requirement seriously. Failing to flag an automated order can violate exchange policies.

WebSocket Conventions

Clean Up Subscriptions

Each real-time subscription — market data, a chart, and so on — returns a subscription ID on the data object. Cache those IDs locally so you can unsubscribe later: unsubscribing from a real-time stream requires the subscription’s ID as a parameter.

You can’t unsubscribe from a user/syncRequest subscription except by closing the connection. In practice, you’ll rarely need to unsubscribe from a syncRequest for the lifetime of your application.

Use user/syncRequest

When working with real-time user data, respond to changes rather than polling. Send a user/syncRequest over a WebSocket, and every time the subscribed user’s data changes, the server pushes the updates to your client as messages. See Real-Time Data via WebSocket for the message format. This operation is intended for WebSocket use, not HTTP, and the fields available on the response object may depend on your API Key’s permissions.