VineVerse

API

Everything on this site is available as JSON. No key, no sign-up, no rate limit worth mentioning. It is the same data the pages render from, so the two can never disagree.

Endpoints

GET /api/v1

The service index.

Every route with a one-line description, and every collection with the URL that pages through it. The cheapest possible first request.

/api/v1
GET /api/v1/stats

What is in the knowledge base.

The catalogue: every collection with its document count, folder, example path and what it holds, plus graph totals and the commonest tags. Start here.

/api/v1/stats
GET /api/v1/list

Page through any collection.

How you find out what exists rather than looking one thing up. Epochs and events default to chronological order, so one call answers "what happened, in sequence".

type
Person, Place, Theme, Lexeme, Commandment, Event, Epoch, Book, Chapter, Source, Dataset, Relation Vocabulary
tag
any tag from /api/v1/tags
folder
bundle path prefix — people, bible/john, sa/events
sort
title · degree · canonical · chronological
limit
50 by default, 1–200
offset
0 by default, 0–100,000
/api/v1/list?type=Epoch&limit=20
GET /api/v1/search

Concept search across every collection.

Names and their variants — aliases, alternate spellings, Strong’s numbers, transliterations — plus descriptions, tags, type names and paths. All terms must match, so adding a word narrows the result. Filter by type: there are ~3,000 people and ~3,000 themes, so an unfiltered name query is a coin flip.

q
required
type
restrict to one collection
tag
restrict to one tag
limit
25 by default, 1–100
offset
0 by default
/api/v1/search?q=faith&type=Theme
GET /api/v1/scripture

Full-text search over the text of Scripture.

For finding a passage you half-remember. Every word must appear in the verse; double quotes require them adjacent as a phrase. Covers all 31,102 verses less the handful the Berean edition omits on manuscript grounds — those carry no text, so nothing can match them.

q
required
book
slug or OSIS id
testament
OT · NT
limit
25 by default, 1–100
offset
0 by default
/api/v1/scripture?q=%22still+small+voice%22
GET /api/v1/passage/{osis}

A verse, a range, or a whole chapter.

John.3.16 for a verse, John.3.16-18 for a range, John.3 for a chapter — the chapter returns a different shape. A verse comes with its context: speaker, the people and places it names, the themes it develops, and what points at it.

/api/v1/passage/John.3.16-18
GET /api/v1/interlinear/{osis}

The Hebrew, Aramaic or Greek behind the English, word by word.

John.3.16 for one verse or John.3 for a chapter — like /xrefs, no ranges. A reverse interlinear: entries are in English order and each carries the range of English words it produced, so a word in the translation traces back to the word it renders. Every entry has the original, its transliteration, its morphology spelled out and its Strong’s number; lexeme appears only where the bundle holds a document for that number, which is the words behind biblical names and their roots rather than all of Strong’s, and no Greek at all. Answers 503 until npm run build:interlinear has produced the artefact.

/api/v1/interlinear/John.3.16
GET /api/v1/xrefs/{osis}

Cross references, ordered by crowd support.

John.3.16 for one verse or John.3 for a whole chapter — unlike /passage this takes no ranges. The corpus records that two passages are connected but never why, so these carry no relationship type. Nothing below 20 votes is in the bundle at all, so a lower minVotes changes nothing.

minVotes
0 by default, 0–200
limit
100 by default, 1–500
/api/v1/xrefs/John.3.16?minVotes=40
GET /api/v1/connections

The cross-reference corpus in aggregate.

The shape of the whole corpus rather than one passage: hub chapters, book-to-book flows, testament totals. Answers 503 until npm run build:connections has produced the artefact.

book
one book, by OSIS id or slug
osis
one chapter, e.g. Ps.119
limit
20 by default, 1–200
/api/v1/connections?book=John
GET /api/v1/nearby

Places within a radius, nearest first.

Anchored on a place already here, or on a bare coordinate pair for a modern location that has no document. Distances are great-circle, in kilometres.

place
anchor path, or give lat and lon
lat
−90–90
lon
−180–180
radius
km; 50 by default, 1–20000
limit
25 by default, 1–200
/api/v1/nearby?place=places/jerusalem&radius=20
GET /api/v1/changelog

The bundle log, and when the data was generated.

bundleGenerated is when the knowledge base was last ingested — the timestamp to cite or cache on. The generated field on /stats is this server’s index-build clock and changes on every restart.

/api/v1/changelog
GET /api/v1/entity/{path}

Any document, with typed relations both ways.

Every collection, the relation vocabulary included. Relations are stored one-directional, so the inbound half is where a person’s parents live. Add body=true for the prose. A folder root — entity/themes, entity/sources — resolves too, and answers with the collection’s size and a sample instead of a document.

body
false by default
backlinks
100 by default, 0–500
/api/v1/entity/lexemes/h430?body=true
GET /api/v1/family/{path}

A person’s parents, children, siblings and spouses.

The corpus has no child-of edge anywhere — parentage is recorded only on the parent’s document — so a person’s own parent-of relations are their children. This inverts it for you.

generations
1 by default, 1–10 — above 1 adds ancestors and descendants arrays; the four direct lists are always one hop
/api/v1/family/people/moses
GET /api/v1/graph/{path}

A node neighbourhood, with its edges.

Everything within N hops, and the edges between them. Typed edges carry a predicate; plain document links carry only kind, so about a third of the edge list has none. Any depth on a well-connected node runs past limit quickly — themes/faith truncates at depth 2 even at the 600 maximum — and the response sets truncated when it did.

depth
1 by default, 1–3
type
one type only — confines the walk, so hops pass through that type
predicate
keep only one kind of edge
limit
200 by default, 1–600
/api/v1/graph/themes/faith?depth=1
GET /api/v1/vocabulary

The relation predicates and the canon table.

What develops, narrated-in, involves and stated-in mean, their inverses, and how many edges use each — plus all 66 books with their OSIS ids and chapter counts.

/api/v1/vocabulary
GET /api/v1/tags

The tag vocabulary, with counts.

Tags cut across collections. Any of them can be passed to /list or /search.

limit
200 by default, 1–1000
offset
0 by default, 0–100,000
/api/v1/tags
GET /api/v1/openapi.json

OpenAPI 3.1 description of everything above.

For tools that build themselves from a schema. Also served at /openapi.json, and every operation is named to match its MCP tool.

/api/v1/openapi.json

What comes back

Every success carries a licence object alongside its data. A failure is { "error": "…" }, sometimes with an available array naming the legal values — an unknown type or sort on /list tells you what it would have accepted.

400 means a parameter was wrong: a missing q, a testament that is not OT or NT, an unrecognised type. 404 covers everything a reference fails to resolve to, whether it was malformed or merely absent — /passage/NotABook.9.9 and /passage/John.999.1 answer alike, because the reference parser reports both the same way. A number that will not parse — a limit of abc — is treated as absent rather than as an error, so you get the default rather than an empty result.

Every /api/v1 route answers with Access-Control-Allow-Origin: *, so this can be called straight from client-side JavaScript without a proxy in between.

Where it stops

Four places the API gives you less than the whole truth. Most of them do say so — backlinksTruncated, truncated, hasMore, threshold — but you have to know to look for the field, and the last one says nothing at all.

backlinks stops at 100 by default
Both entity and chapter responses cap it, but neither hides it any more: backlinkCount is the true total and backlinksTruncated says whether you are seeing all of it. On /entity you can raise the cap to 500 with ?backlinks=; the chapter shape is fixed at 100. Moses has 225, so the default shows fewer than half — they are ordered to put people, themes and events above the chapters that merely mention him, so the ones that survive the cut are the informative ones. links, the outbound side, is capped at 100 too, and that cap is fixed — no parameter raises it. linkCount is the true total, so compare the two rather than counting: Moses has 76 and returns all of them, while a hub like themes/god returns 100 of far more.
Cross references are the materialised ones
Chapter documents carry references at or above the bundle threshold of 20 votes. The complete corpus — 341,289 rows — ships separately as a dataset.
votes is reconstructed, and saturates
Confidence is stored as votes/200 capped at 1, so the count is recovered by multiplying back rather than recorded directly. It stops rising at 200: a reference with 200 votes and one with 4,000 are indistinguishable here.
limit is a cursor on some endpoints and a cap on the rest
/list, /search, /scripture and /tags page properly: they take an offset and return total alongside the page, so you can walk a whole collection. /xrefs and /graph do not — there limit is still only a cap, raising it to the maximum is the only way to see more, and past that maximum the value is clamped without comment.

What you are getting

Documents
12,751
Connections
90,564
Directed edges
135,433
Verses
31,102
OKF version
0.2

If you build on this

The data is CC BY 4.0 and the scripture text is public domain, so you are free to use it — including commercially. What travels with it is the attribution: every response carries a licence field pointing at the full source list, because the obligation is inherited rather than discharged by us.

Documents under /sa/ are CC BY-SA 4.0 and are kept separate for exactly that reason. There are 451 of them: the 450 narrative events, plus the Theographic source record, which inherits the licence of the dataset it describes while living under /sources/. Filtering on the id prefix /sa/ therefore catches all but that one.

Two fields test it exactly, and which one you get depends on the shape of the response. A response about a single document — /passage, /entity, /xrefs, /family — carries licence.document, naming the licence that actually applies. A listing or a search covers many documents at once and cannot have one licence, so instead every share-alike entry in it is flagged shareAlike: true. Between them nothing viral reaches you unlabelled; the blanket data line is not a substitute for either.

There is also an MCP server if you want an AI assistant to read this directly, and an OpenAPI description if your tooling would rather build itself from a schema.