The text API
Any site may keep its copy of the English text in step with this one, or fetch a passage or a paper from it by reference. The text is that of the Uversa Press edition, with its numerous corrections, and it is in the public domain; the edition as a whole, with its index, is licensed under CC BY-NC-SA 4.0. Its HTML marks the words of Jesus, which this site shows in red; the printed book does not.
It is at version 8, made on 2026-10-13: 4 parts, 197 papers, 1,626 sections and 14,586 paragraphs.
In short
GET https://ubsite.sigmaai.au/api/v1/text?since=0 the whole text, and its version
GET https://ubsite.sigmaai.au/api/v1/text?since=N what has changed since version N
GET https://ubsite.sigmaai.au/api/v1/paragraphs/101:4.2-7 a passage, by reference
GET https://ubsite.sigmaai.au/api/v1/papers/101 a paper
Keep the version you are sent, and ask with it next time. A version changes only when a record does: a correction, an italic, the red letter, a reference. When nothing has, the reply is empty, so asking once a day costs nothing. The whole text is about 3 MB compressed, and a reply is compressed whenever your client accepts gzip, as every common one does.
A reply
This is the reply to a site holding version 7, as it would come today. The lists hold whole records, shown below; here they are only counted.
{
"api": 1,
"version": 8,
"since": 7,
"full": false,
"made": "2026-10-13",
"source": {
"press": "5f95c312e679c1fc6c4d3d0f7a0b75535b8a641d"
},
"licence": {
"text": "public domain",
"credit": "requested, not required",
"credit_line": "Text: the Uversa Press edition of The Urantia Book, https://ubsite.sigmaai.au/"
},
"parts": [],
"papers": [],
"sections": [],
"paragraphs": [ 14,586 records ],
"removed": {"parts": [], "papers": [], "sections": [], "paragraphs": []}
}
Applying a reply
- For each record in
parts,papers,sectionsandparagraphs, replace yours with the same key, or add it. The key isreffor a section or a paragraph, and the number for a part or a paper. - Delete each key listed under
removed. - If
fullistrue, the reply is the whole text, so delete anything it does not contain. A reply is full when you ask from 0, or from a version this site has not made. - Keep
version, and send it assincenext time.
The rest of a reply: since is the version it is relative to, 0 when it is
full; made is the date of version; source is the
commit of the Uversa Press text it was made from; and licence says what you
may do with it.
The records
A paragraph:
{
"ref": "147:4.4",
"uf_ref": "147:4.4",
"page_1955": "1650.5",
"paper": 147,
"section": 4,
"paragraph": 4,
"separator_before": null,
"group_before": true,
"kind": "prose",
"setoff": false,
"readings_1955": [],
"html": "<span class=\"jesus\"><span class=\"mark\">“1.</span> <em>The level of the flesh.</em> Such a purely selfish and lustful interpretation would be well exemplified by the supposition of your question.</span>",
"hash": "e552cd1f7dc75085"
}
And one where the 1955 printing reads otherwise: it set only Vertical in italics.
{
"ref": "112:1.7",
"uf_ref": "112:1.7",
"page_1955": "1226.11",
"paper": 112,
"section": 1,
"paragraph": 7,
"separator_before": null,
"group_before": true,
"kind": "prose",
"setoff": false,
"readings_1955": [
"<em>Vertical</em> depth"
],
"html": "<span class=\"mark\">2.</span> <em>Vertical depth</em><span class=\"v1955\"></span> embraces the organismal drives and attitudes, the varying levels of self-realization and the general phenomenon of reaction to environment.",
"hash": "4bb13f7934e75925"
}
ref- Paper, section and paragraph, as this site numbers them:
/147:4.4opens it here. The Foreword is paper 0, and each paper's introduction is section 0. uf_ref- The same paragraph as the Urantia Foundation numbers it: see below.
page_1955- Where the paragraph begins in the 1955 first printing, whose pages the Foundation's
printings keep: the page, then the paragraph on that page,
"1109.3". The Foundation prints none beside 0:12.10, the Foreword's closing ACKNOWLEDGMENT; its"16.7"is where the 1955 printing has it, between 0:12.9 (16.6) and 0:12.11 (16.8). paper,section,paragraph- The three numbers of
ref. separator_before- The separator the book prints before the paragraph, as its stars:
"* * *", or"* * * * *"before the closing notes of Parts I, II and III;nullwhere there is none. A separator is not a paragraph, and has no number of its own. group_beforetruewhere the paragraph begins a new group, as the 1955 printing sets one off; this site marks it ¶.falseotherwise.kind"prose"; or"verse", a line of verse, which the book sets one to a line and does not fill: the psalm in 48:6, the song in 122:9, and the prayers of 144:3 and 144:5; or"heading", the one heading within the text, the Foreword's ACKNOWLEDGMENT, 0:12.10, which the book sets apart. The book indents each block of verse as one; a block begins with a line of verse that follows prose, or that has a separator before it.setofftruefor a line of verse the book sets in further than the lines around it: the second line of each couplet of the Lord's Prayer, 144:3.4, 3.6, 3.8 and 3.10.falseotherwise.readings_1955- What the 1955 first printing reads where today's text departs from it, as the Uversa
Press gives it, in HTML: one for each
<span class="v1955"></span>inhtml, in the same order, and[]for most paragraphs. A marker stands just after the words that changed. Which of today's words a reading replaces is not recorded, and a long one is shortened with “…”, so show a reading beside the text, as this site does in a card at a small ring, and never in place of it. The SRT Committee's reasons for each change, which this site shows with it, are not part of the text and are not sent. html- The text, in Unicode (curly quotes, em dashes), with its markup and nothing else:
<em>for italics,<sup>for an exponent,<span class="jesus">for the words of Jesus,<span class="mark">around a list's number, an empty<span class="leader"></span>where the book prints a dot leader, and an empty<span class="v1955"></span>where the 1955 printing reads otherwise (seereadings_1955). For plain text, strip the tags: what is left is the text exactly. The only entities there can be are&,<and>, and the text holds none of those characters today, but unescape them all the same:# Python text = html.unescape(re.sub(r"<[^>]+>", "", record["html"])) // JavaScript const text = record.html.replace(/<[^>]+>/g, "") .replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); hash- For checking your copy: see below.
A section. Section 0, a paper's introduction, has no title.
{
"ref": "139:9",
"uf_ref": "139:10",
"paper": 139,
"section": 9,
"title": "James and Judas Alpheus",
"combined_with": 10,
"hash": "73b934817e1e9cde"
}
A paper has paper, part (null for the Foreword),
title and author. A part has part, label,
title, sponsor and papers, its first and last.
Two numberings
This site numbers paragraphs as the Uversa Press edition does. 106 of them have other
references in the Urantia Foundation's numbering, which every translation follows. If you
join this text to other data by reference, join on uf_ref, not
ref.
- The Foundation gives the * * * separator in 134:6, and each of the six between
the prayers of 144:5, a number of its own, though it prints none beside them, so its
numbers there run ahead of ours. The Uversa Press numbers no separator: each is sent
as
separator_beforeon the paragraph after it. - It files the one heading over sections 9 and 10 of Paper 139 under 10: our 139:9.1–11 are its 139:10.1–11.
{
"ref": "144:5.11",
"uf_ref": "144:5.12",
"page_1955": "1622.10",
"paper": 144,
"section": 5,
"paragraph": 11,
"separator_before": "* * *",
"group_before": false,
"kind": "verse",
"setoff": false,
"readings_1955": [],
"html": "Our creative Parent, who is in the center of the universe,",
"hash": "b2f5e94908f95c78"
}
Both number the Foreword's closing ACKNOWLEDGMENT 0:12.10, though the Foundation prints no number beside it.
A passage, or a paper
To quote a paragraph, or to show a passage or a paper, ask for it by reference. Write each reference whole, as this site numbers it, with commas between them:
GET https://ubsite.sigmaai.au/api/v1/paragraphs/101:4.2 a paragraph
GET https://ubsite.sigmaai.au/api/v1/paragraphs/101:4 a section
GET https://ubsite.sigmaai.au/api/v1/paragraphs/101:4.2-7 paragraphs 2 to 7 of 101:4
GET https://ubsite.sigmaai.au/api/v1/paragraphs/101:4.7-5.2 from 101:4.7 to 101:5.2
GET https://ubsite.sigmaai.au/api/v1/paragraphs/101:4-6,5:1.3,61:4 several, from anywhere in the book
GET https://ubsite.sigmaai.au/api/v1/papers/101 a paper
GET https://ubsite.sigmaai.au/api/v1/papers/0,101-103 several; the Foreword is Paper 0
A range runs in the order of the book from its first reference to its last, across
sections and papers if need be, 101:4.7-102:1.3. Its end is written like its
start, with as much as changes: 101:4.2-7, 101:4.7-5.2,
101:4-6, 101:4-102:1. An en dash will do for the hyphen. At most
100 references are taken at a time.
The reply holds the records /text sends, the same to the byte, hashes and
all. The paragraphs come in the order of the book, each once, however the references
overlap. With them come the section, the paper and the part each stands in, so that a
quotation can be headed without a second request. This is the reply to
/paragraphs/101:4.7-5.2, its records counted:
{
"api": 1,
"version": 8,
"made": "2026-10-13",
"source": {
"press": "5f95c312e679c1fc6c4d3d0f7a0b75535b8a641d"
},
"licence": {
"text": "public domain",
"credit": "requested, not required",
"credit_line": "Text: the Uversa Press edition of The Urantia Book, https://ubsite.sigmaai.au/"
},
"parts": [ 1 record ],
"papers": [ 1 record ],
"sections": [ 2 records ],
"paragraphs": [ 6 records ]
}
It has no since, full or removed: it is not a step
in keeping a copy, and its version is only the version its records are from.
Never keep it as the version of a copy.
The index writes 61:4.3,4 for two paragraphs of one section. Here that
would mean 61:4.3 and the whole of Paper 4, so /paragraphs/ refuses a bare
number with a 400 that says so. Write 61:4.3,61:4.4 or 61:4.3-4,
and ask /papers/4 for the paper.
To ask by the Foundation's numbering, add ?numbering=uf: /paragraphs/144:5.108?numbering=uf
is this site's 144:5.102, and /paragraphs/139:10?numbering=uf is its 139:9.
The records are the same either way, and each carries both references. The numbers the
Foundation gives the separators of 134:6 and 144:5 name no paragraph. Asking for one is a
404, as is any reference not in the text, and the reply lists every such reference under
missing. Papers are numbered alike, so /papers/ takes the
switch and changes nothing.
From a page on any site:
const r = await fetch("https://ubsite.sigmaai.au/api/v1/paragraphs/101:4.2");
const {papers, sections, paragraphs} = await r.json();
// papers[0].title, sections[0].title, paragraphs[0].html
A client in Python
With nothing but the standard library. It keeps the text in one JSON file.
import gzip, json, pathlib, urllib.request
BASE = "https://ubsite.sigmaai.au/api/v1"
AGENT = "MySite/1.0 (+https://example.org)" # your site's name, please
STORE = pathlib.Path("urantia-book-en.json")
KEY = {"parts": "part", "papers": "paper", "sections": "ref", "paragraphs": "ref"}
have = json.loads(STORE.read_text()) if STORE.exists() else {"version": 0, "records": {}}
req = urllib.request.Request(f"{BASE}/text?since={have['version']}",
headers={"User-Agent": AGENT, "Accept-Encoding": "gzip"})
with urllib.request.urlopen(req) as r:
body = r.read()
if r.headers.get("Content-Encoding") == "gzip":
body = gzip.decompress(body)
reply = json.loads(body)
if reply["full"]:
have["records"] = {}
for kind, key in KEY.items():
for rec in reply[kind]:
have["records"][f"{kind}/{rec[key]}"] = rec
for gone in reply["removed"][kind]:
have["records"].pop(f"{kind}/{gone}", None)
have["version"] = reply["version"]
STORE.write_text(json.dumps(have, ensure_ascii=False))
print(f"version {reply['version']}: {sum(len(reply[k]) for k in KEY)} records")
Checking your copy
A record's hash is the first 16 hex digits of the SHA-256 of the record
without its hash, as JSON with its keys sorted and no spaces, in UTF-8. Recompute it over
what you store, and a record that has gone wrong in your copy shows itself:
# Python
rec = dict(record); del rec["hash"]
hashlib.sha256(json.dumps(rec, ensure_ascii=False, sort_keys=True,
separators=(",", ":")).encode()).hexdigest()[:16]
// JavaScript
const {hash, ...rec} = record;
const json = JSON.stringify(Object.fromEntries(Object.entries(rec).sort(([a], [b]) => a < b ? -1 : 1)));
const hex = [...new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(json)))]
.map(b => b.toString(16).padStart(2, "0")).join("").slice(0, 16);
Please
- Send a User-Agent that names your site, as above. It is how this site sees who keeps a copy, and whether it is current.
- Once a day is plenty: the text changes rarely. Send back the
ETagasIf-None-Matchand an unchanged reply is a bodiless 304. An address that asks many times a minute is refused for a while with a 429. - A passage changes only when the version does. Keep what you are sent, rather than asking again each time a page of yours is viewed.
- Keep the address in your settings. The site will move to urantiabook.org.
- Credit is welcome, not required: “Text: the Uversa Press edition of The Urantia Book, https://ubsite.sigmaai.au/”.
Questions, or to say that your site uses the API: tigran.aivazian@urantiabook.org.
Other endpoints
GET /api/v1- The current version, the counts, the Uversa Press commit this database was built from, and these links.
GET /api/v1/versions- Every version: when it was made, from which commit, and how many records it changed.
Errors come back as JSON with a status, and a message in error. A 400 is
for a since that is not a version number, or a reference that cannot be read.
A 404 is for an address that is not an endpoint, or for references not in the text, listed
under missing. Every reply may be read from
a page on another site (Access-Control-Allow-Origin: *).
Versions
The counts are of records: parts, papers, sections and paragraphs. Version 1 is the whole text, so it counts every one of them.
| Version | Made | Text commit | New or changed | Removed |
|---|---|---|---|---|
| 8 | 2026-10-13 | 5f95c31 |
14,586 | 0 |
| 7 | 2026-10-12 | 806c7ac |
14,586 | 0 |
| 6 | 2026-10-12 | 806c7ac |
14,586 | 0 |
| 5 | 2026-10-12 | 806c7ac |
14,586 | 0 |
| 4 | 2026-10-12 | 806c7ac |
1 | 0 |
| 3 | 2026-10-11 | 806c7ac |
14,586 | 0 |
| 2 | 2026-10-11 | 806c7ac |
14,586 | 0 |
| 1 | 2026-10-11 | 806c7ac |
16,413 | 0 |