Skip to main content

GET Fill History

Durable fill history Tradovate

Returns your Tradovate fill history from CrossTrade's continuous capture. Unlike /v1/api/tv/fills, which mirrors Tradovate's session-scoped fill list, this endpoint is served from the same durable store that powers the CrossTrade Trade Journal, so fills remain available after the daily session reset, over weekends, and across any date range you have been linked.

Use this endpoint for order and fill reconciliation. Rows are returned as a flat list (not per-identity envelopes), oldest first, with cursor pagination.

Coverage

Capture begins when you link a Tradovate identity and runs continuously while the link stays authorized. Trades placed before linking are not available. Manual trades and trades placed through other platforms are included, since capture reads the same fill stream Tradovate reports for the identity. Accounts you trade through NinjaTrader are journaled from the NT8 add-on instead and are served by the executions endpoints, not this one.

Endpoint

GET /v1/api/tv/fills/history

Headers

NameValue
Content-Typeapplication/json
AuthorizationBearer <token>

Query parameters

NameTypeRequiredDescription
fromstringOptionalInclusive lower bound, ISO-8601 date or datetime (UTC). Date-only values start at 00:00Z.
tostringOptionalExclusive upper bound, ISO-8601 date or datetime (UTC). Date-only values cover the whole day.
accountstringOptionalFilter to one account by display name, such as DEMO12345678.
environmentstringOptionalFilter to one environment: demo or live.
limitintOptionalMaximum rows per page (1-1000, default 500).
cursorstringOptionalOpaque pagination cursor from the previous page's nextCursor.

Code examples

import requests

token = 'my-secret-token'

url = "https://app.crosstrade.io/v1/api/tv/fills/history"
params = {"from": "2026-08-01", "to": "2026-08-08", "limit": 500}
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}

while True:
response = requests.get(url, params=params, headers=headers)
payload = response.json()
for fill in payload.get("data", []):
print(fill["executionId"], fill["instrument"], fill["action"],
fill["qty"], "@", fill["price"])
cursor = payload.get("nextCursor")
if not cursor:
break
params["cursor"] = cursor

Response

{
"success": true,
"count": 2,
"nextCursor": null,
"data": [
{
"executionId": "tv:demo:987654321",
"fillId": 987654321,
"orderId": 123456789,
"accountId": 1234567,
"accountName": "DEMO12345678",
"environment": "demo",
"instrument": "MNQ 09-26",
"root": "MNQ",
"action": "Buy",
"qty": 1,
"price": 23150.25,
"commission": 0.35,
"fees": 1.02,
"timestamp": "2026-08-07T14:32:05.184Z",
"tradeDate": "2026-08-07"
},
{
"executionId": "tv:demo:987654400",
"fillId": 987654400,
"orderId": 123456810,
"accountId": 1234567,
"accountName": "DEMO12345678",
"environment": "demo",
"instrument": "MNQ 09-26",
"root": "MNQ",
"action": "Sell",
"qty": 1,
"price": 23162.75,
"commission": 0.35,
"fees": 1.02,
"timestamp": "2026-08-07T15:05:41.020Z",
"tradeDate": "2026-08-07"
}
]
}

executionId is globally unique and stable, so it is the recommended dedup key for reconciliation. fillId and orderId are the raw Tradovate ids, valid within one environment. timestamp is the fill time in UTC; tradeDate is Tradovate's session trade date, which is what daily statements group by. When more rows match than limit, nextCursor carries the position of the last returned row; pass it back as cursor to fetch the next page. A null value for nextCursor means you have reached the end.

Platform nuances

  • This is a CrossTrade-side read. It costs none of your Tradovate API budget and works while markets are closed.
  • Rows are flat and oldest-first; there are no per-identity envelopes. Use the environment and accountName fields (or the query filters) to separate identities.
  • The account filter matches the display name as it appears in your journal, so accounts that are no longer linked remain queryable.
  • Fills you delete in the Trade Journal are excluded here as well.
  • If you disable Tradovate journal import in your journal settings, capture stops and this endpoint stops accruing new rows.
  • A missed trading day (for example, an expired Tradovate authorization that was not re-linked before the session closed) is a permanent gap in the capture; CrossTrade alerts you when the link needs attention.

Authentication uses the same bearer token as the rest of the CrossTrade API and requires a plan with API access (Pro) plus a linked Tradovate account. Unlinked users receive 403 tradovate_not_linked.

This endpoint is REST-only and has no WebSocket equivalent.