Docs
Turing-complete search
Scry search computes: recursive SQL and fixpoint programs run unbounded iteration with conditional branching over the corpus, under budgets and deadlines you set. Graph walks, negation, whole-population aggregates — the answer, not a page of links.
Search that computes
A search engine retrieves; Scry also computes. The query surface carries iteration with no structural ceiling (WITH RECURSIVE, fixpoint programs), conditional branching (if, CASE, in-walk filters), and state that feeds back and grows step to step: a recursive tape that doubles each iteration reaches 4 MiB in 21 steps and 129 ms on the live engine. That is general computation running next to the data, Turing-complete in the language-theoretic sense and, like any physical machine, resource-bounded in execution. The bounds are yours to set. x-scry-max-seconds declares the deadline, x-scry-budget declares the ceiling on metered machine burden, the engine enforces both mid-flight, and the envelope’s truncations list reports which bound fired, if any. Whether an unrestricted computation halts is undecidable in general, so the contract makes halting something you declare and the outcome something you read.
Recursive SQL, measured
POST /v1/scry/query serves WITH RECURSIVE (one anchor, UNION ALL, one step; the CTE is read in the step’s FROM/JOIN). The Collatz trajectory of 27, 111 conditional iterations of branch-on-state arithmetic, returns from the live engine in ~50 ms. Each iteration rescans whatever the step joins, so recursive SQL is the right tool for computed sequences and small closures; for graph walks over big corpora, programs (below) read only the frontier.
curl https://api.scry.io/v1/scry/query \ -H "Authorization: Bearer $SCRY_API_KEY" \ -H "Content-Type: text/plain" \ --data "WITH RECURSIVE collatz AS (SELECT toUInt64(27) AS n, 0 AS step UNION ALL SELECT if(n % 2 = 0, intDiv(n, 2), 3 * n + 1), step + 1 FROM collatz WHERE n != 1) SELECT max(step) AS steps FROM collatz LIMIT 1"
Fixpoint programs: graph walks that read only the frontier
POST /v1/scry/query with Content-Type: application/json accepts a program: a closed JSON AST of named relations evaluated semi-naively to a fixpoint. The walk runs until a full round discovers nothing new, or until the depth cap stops it, and each round expands only the ids the previous round discovered, one metered statement per step, under your key. Thirty-two registered edges span ten corpora: OpenAlex citations and author/institution pivots (references, cited_by, openalex.authors), Twitter reply, quote, and follow graphs (twitter.replies, twitter.quotes, index-native at ~120 ms a step), Hacker News and forum thread trees, and actor pivots into GitHub, Bluesky, YouTube, TikTok, Instagram, and the crawl. Each is cataloged with its measured per-step cost, and naming an unknown edge returns that catalog. In-walk filters prune before expansion, which changes what the next round reads and bills; post-hoc filtering cannot do that. A live two-hop reply walk over a 374-node thread ran 4 statements in ~1 s.
{"program": {
"relations": {
"seed": {"bodies": [[{"ids": ["1928716926609445169"]}]]},
"thread": {"bodies": [[{"rel": "seed"}],
[{"rel": "thread"}, {"edge": "twitter.replies"}]]}
},
"out": [], "depth": 2}} Negation, counts, semantic ranking
not_in provides stratified negation over relations evaluated to completion before the walk that subtracts them: excluded nodes are never expanded, never billed. Every relation carries its per-depth counts whether or not out names it, so when the population’s shape is the answer, out: [] ships the histograms and nothing else crosses the wire. rank orders a derived set by exact cosine distance to an embedding handle you mint. That composition works at corpus scale, where intersecting a walk with a global ANN top-k comes back near-empty. Rows return with {id, parent, depth} provenance, so every node explains how the walk reached it.
The whole population in one statement
Between walks, plain SQL carries the algebra: UNION ALL / DISTINCT, INTERSECT, and EXCEPT compose scans (each parenthesized branch bounded by its own LIMIT); six join kinds; aggregates that consume every matching row, exact for count(DISTINCT ...) and the quantileExact family, reservoir-sampled for plain quantile, bounded-state for topK(N) (an approximate sketch) and groupArray(N) (the first N values); QUALIFY, ROLLUP, TOTALS, and WITH FILL shape the result at no extra charge. A LIMIT bounds the rows returned after the aggregate beneath it has already consumed the whole population, so the counts below are final; there is no page 2.
SELECT src, n FROM ( (SELECT 'hackernews' AS src, count() AS n FROM hackernews.items LIMIT 1) UNION ALL (SELECT 'forums' AS src, count() AS n FROM forums.posts LIMIT 1) ) LIMIT 10
Bounded by design
Programs check the x-scry-max-seconds deadline and the x-scry-budget ceiling at every statement boundary, and a single statement that crosses either is killed mid-flight, while the depth cap and per-relation row caps bound a walk independently of time and burden. The envelope’s truncations list names every bound that fired: an empty list means the walk reached its fixpoint, and a nonempty one names the bound to raise before the next run. The language places no ceiling on the computation; the deadline, the budget, the depth cap, and the row caps do, and the envelope names whichever of the four ended the run.