External functions

External Functions

External Functions

External functions fetch data, call AI, query databases, or run work that may not finish immediately. Their results are written back into your model asynchronously.

Use external functions when a formula depends on something outside the workbook: market data, HTTP JSON, model scoring, AI prompts, or a bounded database query.


1. Built-In External Functions

Function Returns Common use
FX_RATE(base, quote) fx_rate number Currency conversion
HTTP_JSON(url) object Fetch JSON from an HTTP endpoint
ML_SCORE(features) score number Score a feature vector with a configured model
AI_PROMPT(prompt, options) ai_text string Generate text from a prompt
ASK(question, context, options) ai_answer string Ask a question over context
ASK_TEXT(question, context, options) ai_answer string Ask for a text answer
ASK_NUMERIC(question, context, options) ai_number number Ask for a numeric answer
ASK_BOOLEAN(question, context, options) ai_boolean boolean Ask for a boolean answer
ASK_JSON(question, context, options) ai_json object Ask for structured JSON
PG_SELECT(table, query) postgres_query_result Read bounded PostgreSQL results
REDIS_QUERY(connection, query, context) redis_query_result Read an allow-listed Redis value with argument and response caps
REDIS_MUTATE(connection, mutation, context) redis_mutation_result Run a fingerprinted, result-replaying idempotent Redis mutation

Your Grid workspace may expose additional model-backed functions such as CHURN_SCORE(features). Treat them like any other external function: add a fallback and make downstream formulas resilient while the value is pending.

Schema-backed APIs can add namespaced functions without extending the built-in catalog:

USE "api:example/catalog@1.0.0" AS catalog
 
items = catalog.listItems(limit: 100)

The imported package fixes the operation ID, argument names, result shape, and package digest. Only query operations are callable from formulas; effect, publish, and subscribe operations require an explicit action surface.


2. Calling An External Function

External functions are called like ordinary functions:

A1 = FX_RATE("EUR", "USD")
A2 = HTTP_JSON("https://api.example.com/widgets/42")
A3 = ML_SCORE([0.1, 0.2, 0.3, 0.4])
A4 = AI_PROMPT("Summarize this note", { temperature: 0.2 })
A5 = ASK_NUMERIC("What is the forecasted revenue?", B1:B12, { temperature: 0 } AS options)
A6 = SELECT id, amount FROM finance.public.orders WHERE amount >= 100 ORDER BY id DESC LIMIT 25
A7 = REDIS_QUERY("default", { operation: "get", key: "pricing:current" }, {})

Named arguments work too:

A1 = FX_RATE("GBP" AS base, "USD" AS quote)

The binding eventually contains the external result. Until then, it moves through a defined status lifecycle.


3. Status Lifecycle

A binding that depends on an external function can be:

Status Meaning
dirty Needs recomputation; no work has started yet
queued Waiting to be processed
running Work is in progress
ready A current value is available
stale An older value is available while a refresh is wanted
failed The latest attempt failed and no satisfactory value is available

You usually do not need to branch on these statuses inside formulas. Instead, write formulas with fallbacks so downstream bindings remain computable.


4. Eager Vs Lazy Calls

Both eager and lazy assignments can call external functions.

Eager =

rate = FX_RATE("EUR", "USD")

Use eager = when the value is central to the model and should be requested as soon as the model runs.

Lazy ~=

score ~= ML_SCORE([0.1, 0.2, 0.3, 0.4])

Use lazy ~= when the value is expensive or rarely viewed. The work starts when something reads the binding.


5. Always Add A Fallback

External calls are network- and provider-dependent. Pair them with DEFAULT, IFERROR, or WITH ... ELSE:

rate = FX_RATE("EUR", "USD")
price_usd = ROUND(price_eur * (rate DEFAULT 1.08), 2)

For longer chains:

converted = WITH rate = FX_RATE("EUR", "USD"), price = base * rate
THEN ROUND(price, 2) ELSE 0

If any step fails, WITH returns the ELSE value.


6. Nested External Calls

You can nest an external call inside a larger formula:

A1 = ROUND(FX_RATE("EUR", "USD") + 0.01, 4)

Grid tracks the external call separately from the wrapping expression. That lets the fetched value be cached and retried independently while the surrounding formula stays ordinary spreadsheet logic.

For clarity, prefer naming the boundary explicitly when the value is reused:

eur_usd = FX_RATE("EUR", "USD") DEFAULT 1.08
quoted_rate = ROUND(eur_usd + 0.01, 4)

7. Caching, TTL, And Refresh

Each external function declares a cache policy:

Field Meaning
ttlMs Intended time-to-live for a fetched value
maxStalenessMs Hard staleness bound after which a value should be refreshed before use
refreshMode "blocking" or "background" refresh behavior

Typical behavior:

  • Within ttlMs, the cached value is served as current.
  • Past ttlMs but within maxStalenessMs, background refresh may serve the cached value while asking for a new one.
  • At or past maxStalenessMs, the value is treated as expired and refreshed before it is served.
  • ttlMs: 0 means the value does not age out on its own.

Refresh is access-driven: a value is checked when the binding, or a dependent binding, is read. A fully idle model does not refresh just because wall-clock time passed.


8. Failure Handling

If an external call fails:

  1. A stale cached value may be used if one is available and the function allows stale fallback.
  2. Otherwise the binding reports an error value.

Detect failures with ordinary error tools:

ISERROR(rate)
rate IS ERROR
price = rate DEFAULT 1.08

Your model should never depend on an external value being immediately available.


9. Worked Example

MODEL "External Enrichment"
VERSION "1.0.0"
 
# Inputs
A1 is currency = 250000
A2 = "EUR"
A3 = "USD"
 
# External values
B1 = FX_RATE(A2 AS base, A3 AS quote) DEFAULT 1.08
B2 ~= ML_SCORE([0.12, 0.18, 0.27, 0.43]) DEFAULT 0
 
# Fallback-safe analytics
C1 is currency = ROUND(A1 * B1, 2)
C2 = B2 > 0.35 THEN "manual-review" ELSE "auto-approve"
 
END MODEL

B1 requests a rate as soon as the model runs. B2 waits until something reads it. Both provide fallback values so C1 and C2 stay useful.


10. Restrictions

Constraint Notes
External calls cannot supply a rule assignment RHS Bind value-producing calls at the top level or in DO
A bare external call in a rule is an effect Grid commits rule state first, then enqueues the call with a distinct firing identity
External calls in DO follow formula lifecycle Dependency staging is deterministic, but recalculation/cache invalidation may revisit the call; use a rule action for a durable per-firing mutation
Schema-backed effects cannot run as ordinary formulas Table mutations use an explicit top-level command or a post-commit rule action
External calls may time out or be rate-limited Always provide a fallback
External writebacks are revision-checked Older results cannot overwrite newer inputs

11. See Also