TLDR:
search_purchasesin the QuickBooks MCP (my AI-to-QuickBooks bridge) accepts filter params, returns a clean 200, and silently ignores everything you passed. Useget_general_ledger+jqfiltering instead.
The Setup
I was closing out a cancer education business's 2025 books.
Two specific things needed fixing: a charge had landed under my consulting practice instead of the right account, and a contractor had been categorized under an unrelated company's name throughout the year.
Simple enough — or so I thought.
My tool here is my QuickBooks MCP server, an AI-to-QuickBooks bridge that lets me query and edit the cancer education business's QuickBooks account through function calls. I called search_purchases with a criteria filter to pull all transactions for that vendor.
What I Got Back
HTTP 200.
Perfectly shaped Purchase objects.
~1,000 of them.
1.16 million characters dumped to a tool-results file, sorted in no particular order, going back to TxnDate 2024-01-01 — the entire account's purchase history, not the one vendor I asked for.
My filter? Completely ignored.
The criteria, count, desc, and limit params I'd passed — silently dropped. The API just… dumped everything it had.
The Diagnostic Trick
Here's the thing that makes this particularly nasty: HTTP 200, correct response shape, real data.
If the filter had failed loudly — empty result, error code, anything — I'd have caught it in 10 seconds.
Instead it looked like it worked.
If you're ever suspicious a QuickBooks MCP filter is being dropped, pass a deliberately impossible value. If you still get a full result set back — congratulations, you've confirmed the param is a no-op.
Fix #1 — jq on the Dump (Fast, Not Complete)
The quick move: take the tool-results file, filter client-side with jq (the command-line JSON filter), and pull only your vendor's records.
This works… until you hit the cap.
The search_purchases dump maxes out at ~1,000 records. Earliest visible Purchase Id is 7675, earliest TxnDate is 2024-01-01. Anything older? Gone. So if your vendor has more than a handful of transactions across multiple years, the dump will silently truncate your history without telling you.
You'll think you're done. You're not.
The Fix That Actually Works
Pull the General Ledger for the account the vendor lives in, scoped to your date range:
get_general_ledger { account, start_date, end_date }
Then filter rows where the Name column matches your vendor.
The GL row shape: ColData[0] = date, [1].value = transaction type, [1].id = transaction Id, [3] = payee name, [5] = split account, [6] = amount. Header and summary rows have no date — filter with a ^\d{4}-\d{2}-\d{2}$ regex on ColData[0] to strip them.
No cap. No silent truncation. Full history.
Why This Matters
The search_purchases filter looks wired. It has params. It documents them. It accepts them without complaint.
That's the trap.
An API that fails loudly is a good API. You know where you stand. An API that accepts your query, returns 200, gives you real-looking data, and quietly discards your constraints? That one will burn you every time.
From here on: GL pull for anything vendor-specific in QuickBooks. search_purchases is read-only archaeology at best — a quick dump you filter yourself, with the understanding that history before Id 7675 isn't even in the room.