Skip to main content

Resources API

from quran_foundation import ChapterRecitationSnapshotRecord, QuranClient

client = QuranClient(client_id="YOUR_CLIENT_ID", access_token="APP_ACCESS_TOKEN")

translations = client.list_translations()
translation_info = client.get_translation_info(131)
recitations = client.list_recitations()
snapshot = client.get_resource_snapshot("translations", 131)
mushaf_snapshot = client.get_mushaf_snapshot(1)
core_snapshot = client.get_quran_core_snapshot()
chapter_snapshot = client.get_chapter_recitation_snapshot(159)
chapter_records: list[ChapterRecitationSnapshotRecord] = chapter_snapshot["records"]
for record in chapter_records:
if record["record_type"] == "audio_segment":
print(record["verse_key"], record["segments"])
transliteration_snapshot = client.get_word_by_word_transliteration_snapshot(60)

Supported helpers:

  • list_recitations(params=None)
  • get_recitation_info(recitation_id, params=None)
  • list_translations(params=None)
  • get_translation_info(translation_id, params=None)
  • list_tafsirs(params=None)
  • get_tafsir_info(tafsir_id, params=None)
  • list_languages(params=None)
  • list_chapter_infos(params=None)
  • list_chapter_reciters(params=None)
  • list_recitation_styles(params=None)
  • list_verse_media(params=None)
  • sync_resources(params=None)
  • get_resource_snapshot(resource_group, resource_id, params=None)
  • get_chapter_recitation_snapshot(audio_recitation_id, params=None)
  • get_word_by_word_translation_snapshot(word_by_word_translation_id, params=None)
  • get_word_by_word_transliteration_snapshot(word_by_word_transliteration_id, params=None)
  • get_mushaf_snapshot(mushaf_id, params=None)
  • get_quran_core_snapshot(params=None) (singleton quran_core:1)

Use mushafs:<id> in sync_resources(...), then call get_mushaf_snapshot(...) to download metadata, page mappings, positioned words, and layout data. Font files and Mushaf images are not included. See the Font Rendering guide for implementation details and the Developer Terms for caching and bundling conditions.

Use chapter_recitations:<id> to sync chapter audio, where <id> is an Audio::Recitation.id, then call get_chapter_recitation_snapshot(id). The returned records are chapter_audio_file rows and associated audio_segment verse and word timings. The SDK exports ChapterAudioFileSnapshotRecord, AudioSegmentSnapshotRecord, and the ChapterRecitationSnapshotRecord union. This is distinct from the legacy recitations:<id> group, which is keyed by an ayah-recitation ID and may include compatible chapter audio and timing rows for existing clients. Segment timestamps and tuple boundaries are milliseconds from the start of the chapter file; each tuple is [one_based_word_index, start_ms, end_ms]. Use duration_ms for exact arithmetic; duration is the legacy whole-seconds compatibility value.

Bootstrap returns RESOURCE_CREATE entries with snapshot_url; it does not return timing rows inline. Fetch and apply every referenced snapshot, including the audio_segment rows, before storing the final sync token. For incremental audio_segment ROW_CREATE, ROW_UPDATE, and ROW_DELETE mutations, use (resource_group, resource_id, record_type, record_key) as the stable local key. Do not depend on source_record_id or data, because either may be None for a delete. When RESOURCE_INVALIDATE is returned, replace the complete chapter-recitation snapshot.

Canonical Quran Core​

quran_core:1 provides canonical Uthmani verse text, word-level IndoPak and Uthmani Tajweed text, Surah metadata, and Juz/Hizb/Rub-el-Hizb boundaries. The SDK exports QuranCoreSnapshotRecord and typed chapter, verse, word, and boundary variants, including QuranCoreWordSnapshotRecord. Its resource_content_id is None; local rows use (record_type, id) as their key. Mushaf-specific page/line/glyph data remains in mushafs:<id>.

changes = client.sync_resources(params={
"bootstrap": True,
"resources": "mushafs:1;quran_core:1",
})
core = client.get_quran_core_snapshot()
for record in core["records"]:
if record["record_type"] == "verse":
print(record["verse_key"], record["text_uthmani"])
if record["record_type"] == "word":
print(
record["location"],
record.get("text_indopak_nastaleeq"),
record["text_uthmani_tajweed"],
)

Use text_indopak_nastaleeq for IndoPak text and render it with the compatible IndoPak Nastaleeq font; text_indopak is deprecated and retained for compatibility. Existing local copies receive the new field after the resource is re-snapshotted or backfilled, so read it with .get() until then.

Use the same combined filter for incremental sync, and replace the complete resource on RESOURCE_INVALIDATE.

Word-by-Word Transliterations​

Use the resource content ID with word_by_word_transliterations. This group is separate from the translation-only word_by_word_translations group and contains approved, shareable, one-word transliteration resources.

changes = client.sync_resources(
params={
"bootstrap": True,
"resources": "word_by_word_transliterations:60",
}
)
snapshot = client.get_word_by_word_transliteration_snapshot(60)
for record in snapshot["records"]:
print(record["word_id"], record["text"])

Each word_transliteration snapshot record has exactly these fields: id, resource_content_id, resource_id, word_id, language_id, language_name, text, and updated_at. Snapshots are ordered by word_id, then id. resource_id == resource_content_id == ResourceContent.id identifies the owning resource, while word_id identifies the normalized Word owner. Production resource 60 currently contains 77,429 records; row id: 1 has normalized word_id: 1 and text "bis'mi". Incremental row mutations use record_type="word_transliteration".

Read next: