Ten Million Rows In, One Hundred Out
Glide will store ten million rows for you. Its own documentation says so: Big Tables hold "up to 10 million rows," against 25,000 for a regular Glide Table. Four lines further down, the same page says this:
Aggregations in general are limited to 100 matching rows.
Rollups, lookups and joined lists all stop at a hundred. You may keep ten million rows and ask a question of one hundred of them.
That is not a bug and it is not stinginess. It is the same decision every business platform has made about reads, stated unusually plainly. And it is the reason your agent cannot answer the only questions you actually wanted to ask it.
The failure mode, walked through
Point an agent at your customer table and ask which accounts ordered twice in the last year and never came back. It is a reasonable question. It is also the shape of question that motivated buying an agent in the first place, because it is tedious rather than hard.
The agent has a tool that fetches a record. It has a tool that lists records, a page at a time. It does not have a tool that answers the question, and it does not have a tool that hands it the table.
So one of three things happens. It loops, which I will come back to because that is the expensive one. It gives up, which is the good outcome. Or — and this is the case worth dwelling on — it answers from the first page and sounds completely certain.
The reason it can do that is a detail in the protocol most people have never had cause to read. MCP supports pagination on exactly four operations:
resources/listresources/templates/listprompts/listtools/list
Every one of those is a directory listing. tools/call — the operation that moves actual data — is not on the list. There is no protocol-level cursor on a tool result, no protocol-level "there is more," no field an agent can check. Whether a truncated result announces itself is entirely up to whatever the server chose to put in the returned text.
Truncation is not a fact in the protocol. It is a string, and only if someone remembered to write it.
The pagination that does exist tightens the same screw. From the spec:
Page size is determined by the server, and clients MUST NOT assume a fixed page size.
The caller cannot ask for more. It cannot ask how much there is. It reads what it is given, in portions it does not control, through a channel that has no vocabulary for incompleteness. resources/read takes a single uri and returns that resource inline — no batch form, no cursor, one thing at a time.
Why the obvious answers are not answers
"Just loop over the pages." This is what everyone does, and it is metered. Make bills one operation per bundle per module — "Modules process each bundle separately, meaning each bundle triggers its own module run." Their own worked example is a trigger returning ten records through three downstream modules, which costs 31 operations rather than four. The multiplier is the row count, at every stage after the fetch. You are billed by exactly the quantity you were trying to avoid handling.
The upstream side meters it too. Airtable allows five requests per second per base, and caps egress by the month per workspace: 1,000 API calls a month on Free, 100,000 on Team, after which "any API calls will slow down to 2 requests per second until the month resets." A loop over a large table is not slow because the network is slow. It is slow because someone priced it that way, correctly, for a caller that was never supposed to want the whole table.
"Use the bulk API." Some platforms have one, and Shopify's is the good version — which makes it the most useful thing to look at, because it shows what "solved" currently costs. Bulk operations are asynchronous: you submit a query, poll or wait for a webhook, and eventually receive a signed URL to a JSONL file. The query may contain at most five connections and nest at most two levels deep. The operation gets ten days to finish. The result URL "will expire after one week."
That is a well-built batch ETL primitive. It is not something an agent can use inside a turn, and an agent that blocks for an indeterminate stretch waiting on a file is no longer having a conversation — it is a durable job that needs a runtime to survive, which is a different and harder problem than the one you were trying to solve.
"Give the model a bigger context window." This is the tempting one, and it is aimed at the wrong layer. A million-token window does not help when the vendor's read primitive tops out at a page_size of 100. The constraint is not how much the model can hold. It is that the API never emits the table in the first place, at any size. Context growth has been the answer to so many agent problems that it gets reached for reflexively here, and it buys nothing.
"Export a CSV." Glide will do this. It emails it to you. That is a sentence about a person at a desk, which is the whole point.
What the platforms actually ship
Everything below is from vendor documentation, checked on 22 August 2026.
| point read | page ceiling | bulk path | delivered as | |
|---|---|---|---|---|
| Notion | yes | page_size max 100 |
— | — |
| Airtable | yes | pageSize max 100; 5 req/s per base |
— | — |
| Google Sheets | yes | 300 reads/min per project, 60/min per user | — | — |
| Shopify | cost-metered | 100–2,000 points/s by plan, single query capped at 1,000 points | yes, async | JSONL at a URL that expires in a week |
| Glide | yes | aggregations limited to 100 matching rows | CSV export |
Read down the "bulk path" column. One entry, and it is asynchronous. Note also that Notion and Airtable landed on the same page ceiling independently: one hundred.
None of this is incompetence, and the uniformity is the interesting part. These limits are correctly sized for the caller they were designed around: an integration that syncs on a schedule, a dashboard that renders a screen, a person paging through results. A leaky bucket that absorbs bursts and punishes sustained load is a precise statement of who the vendor expected to be calling. Every one of these platforms is well run, and every one of them shipped the same shape.
The gap, as a table
Line up what an agent needs to answer a question about a whole dataset against what is purchasable today.
| bulk read inside one turn | truncation the caller can detect | cost that doesn't scale per record | aggregate computed server-side | |
|---|---|---|---|---|
| MCP (the protocol) | ❌ no pagination on tools/call |
❌ prose, not a field | n/a | ❌ no verb for it |
| Notion | ❌ | ⚠️ has_more on list endpoints only |
❌ | ❌ |
| Airtable | ❌ | ⚠️ offset on list endpoints only | ❌ metered per call, capped per month | ❌ |
| Google Sheets | ⚠️ a range read is genuinely bulk | ⚠️ you asked for the range, so you know | ⚠️ per request, not per row | ❌ |
| Shopify | ❌ async only | ✅ the job reports its own status | ✅ one job, one result | ⚠️ query-shaped, ≤2 levels deep |
| Glide | ❌ | ❌ silently 100 | ✅ | ❌ capped at 100 rows |
The first column is empty except for a spreadsheet API that predates all of this, which got there by accident: values.get takes a range because ranges are what spreadsheets are made of, not because anyone was thinking about agents.
The second column is the one that should worry you more. An agent that cannot read the whole table is inconvenient. An agent that reads a hundred rows of ten million and cannot tell that it did is wrong, confidently, in a way no retry policy catches, and the wrongness looks exactly like an answer.
What would fill it
A truncation flag in the tool-result envelope. Not prose. A boolean and an optional cursor, at the protocol level, on tools/call. This is the smallest change on the list and the one with the largest effect on correctness, because it converts a silent wrong answer into a handled condition. Everything else here is an optimisation; this one is a bug fix.
A server-side aggregate verb. The reason to want ten million rows is almost never to look at ten million rows. It is to count, group, or filter them. Moving the question to the data instead of the data to the question is the oldest idea in this business, and it is precisely what the hundred-row aggregation caps are refusing to do. A vendor that answers count where server-side sells one call instead of ten thousand — and gets to keep the rows.
A synchronous bulk read with a declared ceiling. "Up to 50,000 rows, or an error telling you to narrow the query" is a worse primitive than Shopify's async job in every respect except the one that matters: it returns before the turn ends.
Pricing the aggregate rather than the record. The per-record meter is what makes the loop expensive, and it exists partly because per-record is the only unit anyone has bothered to bill. An aggregate call is cheap to serve and valuable to buy, which is a better business than charging ten thousand operations for a question whose answer is one number.
Why it hasn't happened
The honest reason is that per-record metering is not only a billing mechanism. It is an exfiltration control. A bulk read plane is, viewed from the vendor's side, a supported way to walk out with the customer's entire dataset, and the fact that it is the customer's own data does not settle the question — the credential doing the reading is frequently not in the customer's hands at the moment it is used. Rate limits are doing real security work, and any proposal that removes them without replacing that work is not a proposal.
The aggregate verb is hard for a different and more boring reason. Answering count where over an arbitrary predicate means running an unbounded query on the customer's behalf, on shared infrastructure, with no way to know in advance what it costs. The hundred-row cap is what a query planner looks like when it has decided not to have opinions. Lifting it is a database problem, not an API problem, and it is the kind of database problem that shows up as somebody else's outage.
So who moves first. Not the platform that sells the tool — it has the most to lose and the least practice. I would expect it from whoever already sells a query engine and has a metering story for expensive queries, because for them the aggregate verb is a feature of a thing they already run rather than a new subsystem with new failure modes.
But the sequencing matters more than the identity, and it points somewhere specific. A bulk read plane is only safe if the vendor can name the principal using it, scope it below the human's own access, and revoke it per task. None of them can do that yet — every agent today is holding the user's own credential and drawing from the user's own bucket. Identity is not a parallel problem to this one. It is the precondition. The reason nobody will hand an agent the table is that nobody can yet tell which agent is holding it.
Rate limits, pagination semantics, protocol behaviour and documented caps checked against each vendor's own published documentation and the MCP specification on 22 August 2026. These numbers move; verify before relying on any of them.