Advanced usage
How to read the raw static /v1/ data directly from your own code — in any language, with no library. Every file is plain JSON at a predictable path, served CORS-open, so you fetch what you need and assemble it yourself. If you just want the Bible working in a browser page, BibleEngine.lookup() is the simpler path.
/v1/, not /api/.
The data lives under /v1/ — static JSON, Access-Control-Allow-Origin: *, readable from a backend, a build step, or browser JS on any origin. The /api/ page is only a browser console for trying queries by hand; it renders in the page and sends no CORS header, so a script hitting /api/?…&format=json gets HTML, not data. Script against /v1/.
Verse ids
Every verse has one packed integer id. Pack and unpack it with plain arithmetic:
id = book*1000000 + chapter*1000 + verse // John 3:16 -> 43003016 book = Math.floor(id / 1000000) // 43 chapter = Math.floor(id / 1000) % 1000 // 3 verse = id % 1000 // 16
Book is 1–66 in canon order (Genesis 1 … Revelation 66). These same ids are what the search index returns and what address the chapter files.
Text markers
Three editorial markers can appear in the verse text. They are real characters in the string — strip or style them however you like.
[ ]supplied word — added by translators for sense. Example: darkness [was] upon the deep.
‹ ›words of Christ — single guillemets U+2039 / U+203A (not ASCII angle brackets < >). Example: ‹Blessed are the meek›.
¶paragraph mark (pilcrow) that begins some verses.
Which tree carries them differs. /v1/kjv (plain KJV) keeps all three. /v1/kjvstrongs stores stripped text in t — no markers, since that separate plain /v1/kjv tree holds the KJV markers. /v1/akjvstrongs is the only AKJV text, so its t keeps the supplied [ ] and words-of-Christ ‹ › markers — but not the ¶ pilcrow. (If you need the pilcrow on AKJV today, fetch both trees for the verse: a KJV verse beginning with ¶ marks exactly where the AKJV pilcrow belongs, since it always sits at the very start of the verse.)
The data files
All files are static JSON under /v1/. Chapter files live at /v1/<tree>/BB/CCC.json — BB = book number 01–66, CCC = chapter zero-padded to 3. John 3 is /v1/kjv/43/003.json.
| Path | Shape |
|---|---|
/v1/kjv/BB/CCC.json | { "16": "‹For God so loved…›", … } — verse number → plain text string (markers kept). |
/v1/kjvstrongs/BB/CCC.json/v1/akjvstrongs/BB/CCC.json | { "16": { "t": "For God so loved…", "w": [ ["For",["G1063"]], ["God",["G2316"]], ["so",["G3779"]], ["loved",["G25"]], … ] } }t = verse text, w = tokens in order, each [token, [Strong’s…]] (the list may be empty). In kjvstrongs the t is marker-free (the plain /v1/kjv tree holds the KJV markers); in akjvstrongs the t keeps the [ ] and ‹ › markers (there is no plain /v1/akjv tree). Superscription verses also carry sup and supw — AKJV only. |
/v1/index//v1/index-akjv/ | Search index — see The search index. |
/v1/lexicon/{H|G}/N.json | Strong’s entries — see The lexicon. |
/v1/kjv/manifest.json | { books: [ { id, name, chapters:[verseCounts…] }, … ] } — the source of truth for book names and chapter/verse bounds. |
There is no plain /v1/akjv/ tree — the AKJV text is the t field of /v1/akjvstrongs/.
The search index
The index maps a word or a Strong’s number to the list of verse ids that contain it, split into small bucket files so you fetch only what a query needs. It lives at /v1/index/ (KJV) and /v1/index-akjv/ (AKJV).
| Path | Shape |
|---|---|
meta.json | translation, id encoding, bucket lists, the stop-word list, and counts. |
words/<x>.json | { "loved": [43003016, …], … } — word → verse ids, bucketed by first letter (anything outside a–z → _). love is in words/l.json. |
strongs/<H|G>/<n>.json | { "G25": [40005043, …], … } — Strong’s number → verse ids, bucketed by floor(n/1000): G25 → strongs/G/0.json, H7225 → strongs/H/7.json. |
Working with the lists yourself:
- Exact word forms. Keys are the exact word as written —
lovematches love but not loved or loveth. - Wildcard (
love*): union every key in the same bucket that starts with the prefix. - Several words (AND): intersect their verse-id lists.
- Phrase: narrow by the words it contains, fetch those verses, then substring-match the literal phrase — strip the
[ ] ‹ › ¶markers from the text first so a phrase crossing a marker still matches. - Stop-words are too common to index and are omitted; a search for one finds nothing. The 54: the, and, of, to, a, in, that, is, was, he, for, it, with, his, they, be, not, shall, unto, them, him, but, all, which, thou, thee, thy, ye, as, from, on, an, at, by, or, up, my, me, we, you, this, these, i, o, so, if, out, into, then, their, have, had, were, are, will.
The lexicon
/v1/lexicon/<H|G>/<n>.json, keyed H7225 / G25 (uppercase, no zero-padding), bucketed by floor(n/100): G25 → lexicon/G/0.json, H7225 → lexicon/H/72.json. Each entry:
- o
- original-language lemma (Hebrew/Greek).
- tr
- transliteration.
- ph
- phonetic pronunciation.
- pos
- part of speech.
- et
- etymology / derivation.
- ref
- dictionary cross-reference (TDNT volume:page,entry for Greek).
- str
- Strong’s concise definition.
- ipd
- expanded definition as small HTML (OL/UL/LI).
Every lexicon field is optional — some entries are sparse and a few legacy rows are malformed, so treat each one as may-be-absent.
Headers, CORS, caching, versioning
/v1/*—Access-Control-Allow-Origin: *, methodsGET, OPTIONS. Open for cross-origin fetch.- Cache:
public, max-age=86400, stale-while-revalidate=604800— fresh for a day, then served stale up to a week while revalidating. Not immutable: paths aren’t content-hashed, so a rebuild changes the bytes at the same URL. The Cloudflare edge cache is purgeable. /js/*— cached a day, CORS-enabled. You may import the engine modules cross-origin; the consumer page needs<script type="module">andbibleengine.orgin itsconnect-srcCSP.- Site-wide security headers (nosniff, X-Frame-Options DENY, HSTS, strict CSP). No cookies on the data routes.
- Versioning: breaking schema changes go to
/v2/;/v1/paths stay stable.
Reading it from your own code
Direct verse fetch — when you already know the reference (John 3:16 = book 43, chapter 3):
const chapter = await fetch('https://bibleengine.org/v1/kjv/43/003.json')
.then(response => response.json());
const verse = chapter['16']; // verse text
// Strong's-tagged: /v1/kjvstrongs/43/003.json -> chapter['16'].t and chapter['16'].w
A word search, end to end. A visitor to myBibleSite.org searches for love. This returns {ref, text} for every match, talking only to /v1/, so it runs from any origin with no server:
// A visitor typed a word into the search box on myBibleSite.org.
// We answer it entirely from BibleEngine's static /v1/ files. Those files
// send "Access-Control-Allow-Origin: *", so this cross-origin fetch works
// straight from the browser — no server, no API key, nothing to install.
const BASE = 'https://bibleengine.org/v1';
// Pad a number with leading zeros, e.g. padZeros(7, 3) returns "007".
const padZeros = (number, width) => String(number).padStart(width, '0');
// Each verse has one packed id: book*1000000 + chapter*1000 + verse.
// unpackId splits it back into parts (id 1027004 -> Genesis 27:4).
const unpackId = id => ({
book: Math.floor(id / 1000000),
chapter: Math.floor(id / 1000) % 1000,
verse: id % 1000
});
// Fetch a JSON file, remembering it so the same file is never fetched twice.
const fileCache = new Map();
async function fetchJson(url) {
if (fileCache.has(url)) return fileCache.get(url);
const response = await fetch(url);
const data = response.ok ? await response.json() : null;
fileCache.set(url, data);
return data;
}
async function searchForWord(input) {
const word = input.toLowerCase().trim();
if (!word) return [];
// STEP 1 — Find which verses contain the word, using the search index.
// The index is split into one file per first letter, so we download only
// the bucket we need: "love" lives in /v1/index/words/l.json.
// First, meta.json lists the very common words (the, and, of, ...) that were
// left out of the index. If the search word is one of them, there is nothing
// to look up, so stop here.
const meta = await fetchJson(`${BASE}/index/meta.json`);
if (meta.stopwords.includes(word)) return [];
// The bucket maps each word to the list of verse ids that contain it.
// This is an exact match on the word as written: "love" finds "love",
// but not "loved" or "loveth".
const bucket = await fetchJson(`${BASE}/index/words/${word[0]}.json`);
const verseIds = (bucket && bucket[word]) || [];
if (!verseIds.length) return [];
// STEP 2 — Work out which chapter files we need.
// Verse text lives in one file per chapter at /v1/kjv/BB/CCC.json
// (BB = book number 01-66, CCC = chapter 001-150). Several of our verse
// ids usually fall in the same chapter, so collect the DISTINCT chapter
// files — that way we fetch each chapter once, not once per verse.
const chapterFiles = new Set(verseIds.map(id => {
const { book, chapter } = unpackId(id);
return `${padZeros(book, 2)}/${padZeros(chapter, 3)}`; // e.g. "01/027"
}));
// STEP 3 — Download everything we need at once (in parallel):
// - the manifest, which maps a book number to its name (1 -> "Genesis")
// - each chapter file, stored in `chapters` keyed by "BB/CCC"
const chapters = {};
const [manifest] = await Promise.all([
fetchJson(`${BASE}/kjv/manifest.json`),
...[...chapterFiles].map(async path => {
chapters[path] = await fetchJson(`${BASE}/kjv/${path}.json`) || {};
})
]);
const bookName = Object.fromEntries(manifest.books.map(book => [book.id, book.name]));
// STEP 4 — Build the result list, in Bible order.
// A chapter file maps verse number to its text, so the text for a verse is
// chapters["BB/CCC"][verseNumber].
return verseIds.sort((a, b) => a - b).map(id => {
const { book, chapter, verse } = unpackId(id);
const path = `${padZeros(book, 2)}/${padZeros(chapter, 3)}`;
return {
ref: `${bookName[book]} ${chapter}:${verse}`, // e.g. "Genesis 27:4"
text: chapters[path][verse] || ''
};
});
}
// --- Wire it to the visitor's search ---
const results = await searchForWord('love');
console.log(results.length + ' verses'); // 280
results.slice(0, 20).forEach(verse => { // first page of hits
console.log(verse.ref + ' — ' + verse.text);
});
Notes. For a wildcard (love*), union every key in the bucket that starts with the prefix. For several words, intersect their id lists. A common word touches many chapter files (love is 280 verses across 162 chapters); they are CDN- and browser-cached, but page the output. text keeps the [ ] ‹ › ¶ markers — strip or style them as you like. For AKJV, swap /v1/index → /v1/index-akjv and read text from /v1/akjvstrongs/BB/CCC.json as chapter[verse].t (which keeps the [ ] and ‹ › markers, though not the ¶).
Rather not wire the fetches yourself? The engine module exports its pieces — import { runQuery, parseReference, searchWord, fetchVerses } from 'https://bibleengine.org/js/BibleEngine.js' — so you can reuse them without the BibleEngine facade (load the manifest and index meta.json yourself; search.html shows the pattern). Or just call BibleEngine.lookup() and skip all of this.
Using this data
The Bible texts here — the King James Version, the American King James Version, and Strong’s numbering and lexicon — are in the public domain. Free for anyone to use, copy, and build on, with no permission needed.
The BibleEngine software, JSON data format, and search index are released under CC0 (a public-domain dedication): use them for any purpose — personal, ministry, or commercial — with no attribution, license terms, or asking.
Attribution is not required, but it is appreciated and helps others find the service. A sample credit:
Bible data and search by BibleEngine — https://BibleEngine.org
Plain text: Powered by BibleEngine — https://BibleEngine.org
HTML: <a href="https://BibleEngine.org">Bible data by BibleEngine</a>
← API (BibleEngine.lookup) · BibleEngine home · interactive search