Grids
Live data

A spreadsheet that fetches the world.

An external function is just a function. You write FX_RATE("USD","EUR") the way you write SUM(...). Behind the scenes, Grid queues a worker job, tracks a status lifecycle, caches the result, and writes back to your cell — while the rest of the model keeps computing.

B7= FX_RATE("USD","EUR")
dirty
...queued
fetchingrunning
1.0824ready
01 · The five

Five external functions, built in.

Each ships with a default cache TTL, a default timeout, and a worker route. Tier 1 deployments register Python-backed functions through a model-host manifest; from inside a cell they look identical to the built-ins.

FX_RATETreasury, FX hedging, multi-currency invoicing.
HTTP_JSONAny REST endpoint that returns JSON.
ML_SCOREScore a feature vector against a registered model.
AI_PROMPTRun an LLM call from inside a cell.
PG_SELECTPull rows from Postgres on a schedule or trigger.
02 · The lifecycle

Every external value has a status.

dirty → queued → running → ready on the happy path. stale when a newer revision is desired. failedwhen the latest attempt didn't land. Read it through the API or stream it over the websocket.

Pair the call with DEFAULT and the rest of the model keeps computing while the cell rides the lifecycle. Use ?= to write only on success — a transient failure won't blank a previously good value.

03 · Eager vs lazy

One operator decides when work happens.

= queues the work the moment the model deploys. ~= waits until something reads the cell. Same syntax everywhere else — but a single character chooses between a warm dashboard at deploy time and a cold pool of cells that fire only when asked.

=

Eager — at deploy

Worker starts immediately. Downstream cells use DEFAULT until ready. Best for hot paths and live dashboards.

B7 = FX_RATE("USD", "EUR") B8 = ROUND(B7 DEFAULT 1.08, 4)On deploy, B7 enqueues at once. B8 reads the default until B7 is ready.
queue · time
~=

Lazy — on demand

No work until something asks. Eliminates the "deploy creates a thundering herd of jobs" problem. Best for rare-read or expensive paths.

P1 ~= ML_SCORE(features) P2 = P1 DEFAULT 0P1 is idle until a read. Read materializes; subsequent reads hit cache.

Each external call lowers to a deterministic synthetic symbol — so a cell that wraps FX_RATE in ROUND is cached at the boundary, not at the outer expression. One fetch, many reads.

The cell is the wire. The world is the value.

Live FX in front of a treasury model. HTTP JSON streamed into a config sheet. An ML model scoring every row of a CRM. An AI prompt that returns a typed cell. None of it is glue code — it's just a formula.

And when an external call fails, you keep computing. Pair DEFAULT with ?= for the last-good-value pattern, and the rest of the sheet stays alive.

treasury.gridgrid
# eager: warm at deploy
B7 = FX_RATE("USD", "EUR")
last_good ?= B7
rate = last_good DEFAULT 1.08

# lazy: only when read
P1 ~= ML_SCORE(features)
risk = P1 DEFAULT 0

# defensive multi-step
user_email = WITH u = HTTP_JSON("https://api.x/me"),
  THEN u.email ELSE "unavailable"
NextType a sentence. Get a model.