Python
A typed Python client for BibleQL. Requires Python 3.10 or newer.
- Package:
bibleqlon PyPI - Source: github.com/lporras/bibleql-python
- License: MIT
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]
Search
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.
Semantic search
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)
contextcontext 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.