Skip to main content

Python

A typed Python client for BibleQL. Requires Python 3.10 or newer.

Install​

pip install bibleql

Configure​

import bibleql

client = bibleql.Client(
api_key="bql_live_...", # or omit it and set BIBLEQL_API_KEY
default_translation="eng-web",
timeout=30, # seconds
)

If api_key is omitted, the client reads the BIBLEQL_API_KEY environment variable, and raises bibleql.ConfigurationError if neither is set:

client = bibleql.Client()

Unlike the Ruby and Node packages, api_url already defaults to https://bibleql.org/graphql, so there is nothing to pin.

The client holds an HTTP connection pool. Use it as a context manager, or call client.close() when you are done:

with bibleql.Client() as client:
print(client.passage("John 3:16").text)

The library never reads .env files itself. If you keep the key in one, load it first — for example with dotenv.load_dotenv() from python-dotenv.

For where to initialize the client in an application, see Configuring the SDKs.

The examples below all assume a client built as above.

Reading passages​

passage = client.passage("John 3:16")

passage.reference # "John 3:16"
passage.text # "For God so loved the world..."
passage.translation_id # "eng-web"
passage.verses # [Verse(book_id='JHN', book_name='John', chapter=3, verse=16, text='...')]

# Per-call translation
client.passage("Juan 3:16", translation="spa-rv1909")

Verses and chapters​

verse = client.verse("MAT", 5, 3)
verse.book_name # "Matthew"
verse.chapter # 5
verse.verse # 3
verse.text

verses = client.chapter("MAT", 5) # list[Verse]
for verse in client.search("love", limit=5):
print(f"{verse.book_name} {verse.chapter}:{verse.verse}")
print(verse.text)

Substring matching, not stemming — see Searching the Bible.

for result in client.semantic_search("fe y esperanza", limit=5):
print(f"{result.similarity:.2f}", result.verse.text)

Unlike every other method, semantic_search does not fall back to default_translation when translation is omitted: it lets the API pick its default, spa-rv1909, which is the only translation with embeddings. See Semantic Search.

Random verse​

client.random_verse()
client.random_verse(testament="NT") # "OT" or "NT"
client.random_verse(books=["PSA", "PRO"]) # or books="PSA,PRO"

Passing books makes testament be ignored — see API Behavior.

Verse of the day​

import datetime

client.verse_of_the_day()
client.verse_of_the_day(date=datetime.date(2026, 1, 1)) # or date="2026-01-01"

Returns a Passage, which may hold more than one verse.

Concordance​

The Python client is currently the only SDK that covers the concordance queries. See Concordance for what the fields mean.

page = client.concordance("misericordia", translation="spa-rv1909", first=10)

page.total_count # occurrences matching the filters
page.entry.surface_forms # ["misericordia", "misericordias", ...]
page.entry.occurrences_by_testament # TestamentCount(old=327, new=71)

for occurrence in page.occurrences:
print(occurrence.verse.book_name, occurrence.context) # context has <mark> tags

The GraphQL connection (edges / node / pageInfo) is flattened into occurrences, has_next_page and end_cursor. Fetch the next page with the cursor, or let the client walk every page lazily:

if page.has_next_page:
page = client.concordance("misericordia", translation="spa-rv1909", after=page.end_cursor)

for occurrence in client.iter_concordance("misericordia", translation="spa-rv1909"):
...

Filter by book (localized names work) or testament:

client.concordance("misericordia", translation="spa-rv1909", book="Salmos")
client.concordance("misericordia", translation="spa-rv1909", testament=bibleql.Testament.NEW)

And the alphabetical word-frequency index:

client.concordance_index(translation="spa-rv1909", prefix="mis", min_occurrences=5)
Sanitize context

context contains <mark> HTML around the match. Sanitize it before rendering it as HTML.

Discovery​

client.translations() # list[Translation]
client.translation("eng-web") # with .books
client.languages() # .code, .translation_count, .translations
client.books() # the 66 canonical books
client.bible_index() # .chapters -> [Chapter(number=1, verse_count=31, ...)]

Models​

Every result is an immutable dataclass with snake_case attributes. To get a dict:

import dataclasses

dataclasses.asdict(client.verse("JHN", 3, 16))

Error handling​

try:
client.passage("Nonexistent 1:1")
except bibleql.NotFoundError as e:
print(f"Not found: {e}")
except bibleql.AuthenticationError:
print("Check BIBLEQL_API_KEY")
except bibleql.RateLimitError:
print("Rate limited — back off")
except bibleql.NetworkError as e: # includes RequestTimeoutError
print(f"Transient network problem: {e}")
except bibleql.QueryError as e:
print(f"GraphQL error: {e}")
print(e.errors) # the raw error list

The hierarchy:

BibleQLError
├── ConfigurationError no API key
├── NetworkError no response (DNS, connection refused, ...)
│ └── RequestTimeoutError
├── APIError (.status, .body) non-2xx HTTP response
│ ├── AuthenticationError 401
│ ├── RateLimitError 429
│ └── ServerError 5xx
└── QueryError (.errors) GraphQL errors in the response
└── NotFoundError

See Errors for what each maps to on the wire.

Not covered by the package​

Offline packages (offlineDownloadable, offlinePackage) have no methods. Use the GraphQL document from Offline Packages with any HTTP client.