Tradovate API

Tradovate API Fill/Position Endpoints Return None

Your orders go through fine, but the fill price or open-position size comes back empty. Two of the three culprits are wrong-shape problems you can fix in minutes, one is a timing quirk you have to design around.

Reviewed by the PickMyTrade Trading Systems Team Last updated
· 8 min read
Tradovate fill/list API call returning HTTP 200 with an empty array in a REST client

Your orders go through fine. Then you ask the API for the one thing you actually need, the fill price, or the size of the open position, and it hands you back nothing. An empty array. A null. A clean 200 OK with [] in the body. Three calls trip up almost everyone here: fill/list (or fill/items) comes back empty, order/item works but has no fill price on it, and position/find?name=... never finds your position. Two of those are wrong-shape problems you can fix in a couple of minutes. One is a timing quirk you have to design around. And a small slice of the time it really is Tradovate returning data unreliably, in which case the right move is to cross-check a few endpoints and file it with support. Let's separate the three.

Quick Checklist

  • Query in-session. REST only shows the current trading day. After market close the session archives and older calls return empty.
  • Stop using /find on positions. The Position entity has no name field, so position/find?name=MNQZ5 will never match. Look positions up by contractId.
  • Fill price isn't on the order. Read it from the price field of the fill, not from order/item.
  • Match the endpoint to the parameter. /item?id= takes one id; /items?ids= takes a list; /list takes none. Params are lowercase.
  • Confirm host and token. A demo token on a live host, or an expired token, returns empty or unauthorized before any data problem even applies.
  • If the right calls still come back empty during a live session with known activity, capture the request and response and report it to support.

What "Returns None" Actually Means

There's an important distinction buried in these failures, and it changes how you fix them. A 200 OK with an empty array means the call was accepted, authenticated, and understood, the server just had no data to give you under those parameters. That is almost never a bug in Tradovate; it's a scope or timing mismatch on your side. A 404, by contrast, means the specific record you asked for isn't reachable right now, common on an item?id= lookup after the session has rolled. And a 401 means the server never got far enough to look for data at all. So before you conclude “the endpoint is broken,” check which of those three you're actually getting. It points straight at the cause.

Why the Fill, Order, and Position Calls Come Back Empty

1. REST Only Shows the Current Session

This is the big one, and it catches people who test after hours. Tradovate archives each day's session over market close. Once that happens, the REST query endpoints, fill/list, order/list, fillPair/list, cashBalanceLog/list and friends, only see the current session's records. Ask for yesterday's fills and you get a 200 with an empty array; ask for a specific archived order via order/item?id= and you can get a 404. Nothing is wrong with your auth or your syntax. The data simply isn't in the live cache anymore. The tell here is that the exact same call worked fine an hour earlier during the session and returns empty now.

The fix is two-part. For live activity, run your queries in the same session the trades happened in. For anything historical, end-of-day reconciliation, a P&L journal, a trade log, use the Reporting API at rpt-live.tradovateapi.com (or rpt-demo.tradovateapi.com for demo), which is built to serve archived data by date range. The general-purpose trading endpoints are not.

Diagram comparing the current-session trading API with the Reporting API for historical fills

2. /find Only Works on Entities With a Name Field

Trying to locate an open position with position/find?name=MNQZ5 will fail every single time, and not because your position is missing. The /find operation is only defined for entities that carry a name field. Contracts have one. Products have one. The Position entity does not, so a name lookup against it returns nothing no matter what you pass. Positions are keyed by contractId, an integer, not by the symbol string you see on your chart.

So do it in two steps. First resolve the symbol to a contract id: contract/find?name=MNQZ5 returns the contract object, including its numeric id. (For a partial or forward match, contract/suggest?t=MNQ&l=10 returns candidates.) Then pull your positions with position/list, or position/deps?masterid={accountId} for a specific account, and filter the results where contractId equals the id you just looked up. Read netPos for the signed size. That's the position you were trying to “find.”

Resolving a symbol to a contractId with contract/find, then matching it in the position list

3. The Order Object Doesn't Carry a Fill Price

This one is genuinely counterintuitive. order/item?id= returns the order, its status, side, quantity, order type, timestamps. What it does not return is the price you got filled at, because an order and its execution are two different records. The execution price lives on the fill entity, in its price field. One order can produce several fills at different prices, which is exactly why the number can't sit as a single value on the order.

To get the fill price for an order, go to the fills. Each fill references its parent order through an orderId field and its instrument through contractId, and carries price, qty, action (Buy/Sell) and a timestamp. Pull fill/list for the session and match on the orderId you care about, or load the order's dependent fills directly. For real-time work, the cleanest source is the executionReport / fill events on the WebSocket, which arrive the instant an execution happens. If you need realized P&L rather than raw fill prices, fillPair/list gives you matched buy/sell pairs.

4. Right Endpoint, Wrong Parameter

Tradovate's query endpoints follow a strict, case-sensitive shape, and mixing them up produces empty results or errors that look like data problems. The pattern is: /item?id=123 for a single record, /items?ids=1,2,3 for several, /list for everything in scope with no parameters, /deps?masterid=123 for one parent's dependents, and /ldeps?masterids=1,2 for several parents'. A call like fill/items?Id=XXX quietly breaks two rules at once, it uses the plural /items endpoint (which expects ids=) with a singular, capitalized Id parameter the server doesn't recognize. Either switch to fill/item?id=XXX for one record or fill/items?ids=XXX for a list, and keep the parameter lowercase.

How to Cross-Check the Endpoints Step by Step

When a fill, order, or position call comes back empty, run this sequence before you assume the platform is at fault. It isolates the cause in a few minutes.

1

Rule out auth

Hit a trivial authenticated call such as account/list. A 401 here means the problem is your token or host, not the fill/position endpoints, fix that first.

2

Confirm the environment

Make sure the host you're calling (demo vs. live) matches the token you authenticated with. A valid token pointed at the wrong environment returns no data.

3

Confirm there's activity this session

If you're testing after market close, there may genuinely be nothing in the live cache. Reproduce during an active session with a known open position or a fresh fill.

4

Resolve the symbol

Run contract/find?name=YOURSYMBOL and note the numeric id. Every position and fill filter keys off this, not the symbol text.

5

Compare three views

Pull order/list, fill/list, and position/list for the same account and cross-reference by contractId and orderId. If the order shows filled but no matching fill appears, that's the mismatch worth escalating.

Cross-checking order/list, fill/list and position/list responses side by side by contractId

Troubleshooting Table

Symptom Likely cause Fix
fill/list returns 200 + [] after hoursSession archived; REST only serves the current dayQuery in-session; use the Reporting API for history
position/find?name= always emptyPosition has no name fieldResolve symbol → contractId, then filter position/list
order/item works but no fill pricePrice lives on the fill, not the orderRead price from the matching fill (by orderId)
order/item?id= returns 404Record archived after market closeQuery in-session or pull it from the Reporting API
/items?Id= returns empty or errorsWrong endpoint/param shapeUse /item?id= (one) or /items?ids= (list), lowercase
All correct calls empty during a live sessionPossible platform-side reliability issueCapture request/response and report to support

The Reliable Pattern: Sync Once, Then Stream

Polling REST over and over for “is my position updated yet?” is the fragile way to do this, and it's a big reason people see stale or empty responses, you catch the endpoint between updates. The pattern Tradovate itself points developers toward is different: subscribe, don't poll.

Open the trading WebSocket and send a user/syncrequest. In response you get a single consolidated snapshot, accounts, positions, orders, fills, cash balances, the lot. From that point on, the socket pushes you every change as it happens: a new fill, a position update, a cash-balance move. You keep a local model in sync off those events instead of re-asking REST. For the historical side, the trades that already archived out of the live session, do the initial backfill through the Reporting API, then hand off to the WebSocket for everything going forward. That combination is what keeps a bot's view of fills and positions both complete and current.

When to Report It to Support

Most of the time these empty responses trace back to one of the four causes above, and you can fix them without anyone's help. But there's a real subset where the correct call, made during an active session, against an account with confirmed activity, still returns nothing, or an order shows filled while no fill record ever appears. That's not something you can code around, and it's worth reporting. When you do, include the exact endpoint and query string, the account id, the UTC timestamp, whether you were on demo or live, and the raw 200 response body showing the empty result. A cross-checked report like that gets triaged far faster than “the API isn't working.”

Where PickMyTrade Fits

Stitching together contract lookups, fill matching, session-aware queries, and a WebSocket sync loop is a lot of code to write and maintain just to know your fill price and your open size. PickMyTrade sits between TradingView and Tradovate and manages that layer for you:

  • Live position and fill tracking, the connection stays synced to your account state, so you're not polling endpoints that return empty between updates.
  • Symbol handling done right, contract resolution and rollover are managed under the hood, so you're never matching the wrong id.
  • Session-safe execution, orders route and confirm without you hand-managing archived-vs-live data windows.
  • No token or WebSocket code, auth, renewal, and the sync stream are handled, so your alerts just reach Tradovate and fill.

Automate Without Wrestling the API

Start your free trial, automate Tradovate without wrestling the API.

Start Your Free 5-Day Trial

Frequently Asked Questions

The REST list and item endpoints only expose the current trading session. Once the session archives over market close, calls for older activity come back as 200 OK with an empty array (or a 404 on an item lookup). Query during the same session the fills happened in, and use the Reporting API for anything historical.

The order object carries order state, not execution details. The average or per-lot fill price lives on the fill entity's price field. Pull the fills for that order id, via fill/list or the fill dependency endpoint, and read price there, or listen for the executionReport event on the WebSocket.

The /find operation only works on entities that have a name field. The Position entity has no name field, so a name lookup always returns empty regardless of your open position. Positions are keyed by contractId. Resolve the symbol to a contract id first with contract/find?name=MNQZ5, then match that id against position/list.

Contracts do have a name field, so contract/find?name=MNQZ5 returns the contract object including its numeric id. For partial matches use contract/suggest?t=MNQ&l=10. Take that contractId and use it to filter positions and fills, since both reference the contract by id rather than by symbol text.

Don't poll REST in a loop. Open the trading WebSocket and send user/syncrequest to get a one-shot snapshot of accounts, positions, orders, fills and cash balance, then let the socket stream every update after that. Use the Reporting API for the initial historical backfill.

This guide is for educational and informational purposes only and is not financial, investment, or trading advice. Trading futures and other leveraged products carries a substantial risk of loss and is not suitable for every investor. PickMyTrade is an independent third-party automation platform and is not affiliated with, endorsed by, or sponsored by Tradovate, Inc. All related names, logos, and trademarks are the property of their respective owners. Platform features and steps change over time, so always confirm the current process in the official Tradovate platform and documentation before acting.