Getting Started with Content Sync
Content Sync helps an app keep a local copy of public Quran Foundation content fresh. Instead of downloading every Mushaf, translation, word-by-word translation, word-by-word transliteration, tafsir, recitation, chapter recitation, or article again, your app asks:
- What changed since the last time I checked?
- Which small rows can I update directly?
- Which full content copy do I need to download again?
Terms
| Term | Plain meaning |
|---|---|
| Resource | One content item your app tracks, such as translation 19, word-by-word transliteration 60, tafsir 151, ayah recitation 10, chapter recitation 159, or article 123. |
| Resource filter | The list of content your app wants to keep in sync, for example translations:19;tafsirs:151. |
| Change | One server event telling your app what changed. The API field is called type. |
| Full copy | The complete current rows for one resource. The API calls this a snapshot. |
| Snapshot URL | A relative API path returned on some changes. Fetch it to replace the full local copy for that one resource. |
| Sync token | A private checkpoint returned by the API. Store it and send it next time to get only newer changes. |
| Sync sequence | A server sequence number for content changes. Mutations expose this as sequence. |
| Sync until | The upper sequence bound for one sync run. The API field is called sync_until_sequence. |
| Cursor | A temporary page link. Use it only while finishing the current sync request. |
When next_page_url or snapshot_url is present and non-null, we return a relative /api/v4/... path, such as /api/v4/resources/sync?cursor=... or /api/v4/resources/snapshots/translations/19. You should prefix that path with https://apis.quran.foundation/content.
Content Available for Offline Sync
Content Sync currently supports these resource groups: mushafs, translations, word_by_word_translations, word_by_word_transliterations, tafsirs, recitations, chapter_recitations, and articles.
| Resource group | What the full copy contains | Changes included |
|---|---|---|
mushafs | Mushaf metadata, page mappings, and positioned words. Font files and images are not included. | Mushaf, page, and word changes. |
translations | Translation rows for the resource. Footnotes are nested inside each translation row. | Translation row changes and footnote-driven translation row updates. |
word_by_word_translations | Word translation rows for an eligible one-word translation resource. | Word translation row changes. |
word_by_word_transliterations | Word transliteration rows for an eligible one-word transliteration resource. | Word transliteration row changes. |
tafsirs | Tafsir rows for the resource. | Tafsir row changes. |
recitations | Ayah audio files and compatible chapter audio files for the legacy ayah recitation. | Ayah audio file and compatible chapter audio file changes. |
chapter_recitations | Chapter audio file rows for one chapter recitation. | Chapter audio file changes. |
articles | Visible article localizations. | Article localization changes and resource-level refresh, delete, or restore events. |
If a resource group is not listed above, it is not accepted by this sync endpoint. Continue using that resource's regular API endpoint when available.
word_by_word_translations and word_by_word_transliterations are separate
groups. The translation group remains translation-only (word_translation
records). The transliteration group contains approved, shareable one-word
transliteration resources (word_transliteration records), such as production
resource 60.
Audio Resource Identities
The recitations group is the legacy ayah-by-ayah resource, keyed by the
Recitation.id used by the regular recitation endpoints. The
chapter_recitations group is keyed by the corresponding
Audio::Recitation.id. These IDs and groups are distinct even when both
resources use the same underlying resource content.
Chapter-recitation snapshots contain only chapter_audio_file records. For
backward compatibility, chapter-file mutations for the selected approved
chapter recitation may also be emitted through the linked legacy recitations
resource. New clients that want an independently addressable chapter audio
resource should use chapter_recitations.
Mushaf Fonts and Images
Mushaf Content Sync provides Mushaf metadata, page mappings, and positioned words. It does not include font files or images. For rendering, see Mushaf Fonts and Images and the Font Rendering guide.
First Sync
On first sync, ask for the content you want and set bootstrap=true. The API returns pages of RESOURCE_CREATE changes. Each one includes a snapshot_url, which points to the full copy for that resource.
curl "https://apis.quran.foundation/content/api/v4/resources/sync?bootstrap=true&resources=chapter_recitations:159;mushafs:1;translations:19;tafsirs:151;word_by_word_transliterations:60&per_page=100" \
-H "x-auth-token: $ACCESS_TOKEN" \
-H "x-client-id: $CLIENT_ID"
If has_more is true, call next_page_url until it becomes false. Store next_sync_token only from the final page.
Fetch and apply any returned snapshot_url values before you mark the sync as complete locally.
Fetching a Full Copy
A full copy returns all current rows for one resource. For example, a Mushaf full copy returns its metadata, page mappings, and positioned words. Font files and images are not included.
curl "https://apis.quran.foundation/content/api/v4/resources/snapshots/mushafs/1" \
-H "x-auth-token: $ACCESS_TOKEN" \
-H "x-client-id: $CLIENT_ID"
Client rule: replace all local rows for mushafs:1 with the records array from the response.
For a chapter recitation, use the Audio::Recitation.id from the
chapter_recitations filter:
curl "https://apis.quran.foundation/content/api/v4/resources/snapshots/chapter_recitations/159" \
-H "x-auth-token: $ACCESS_TOKEN" \
-H "x-client-id: $CLIENT_ID"
The records array contains the complete current chapter-file list for that
recitation. A chapter audio record has this shape:
{
"record_type": "chapter_audio_file",
"id": 2001,
"audio_recitation_id": 159,
"chapter_id": 1,
"audio_url": "https://example.com/audio/001.mp3"
}
For word-by-word transliteration resource 60, fetch the same snapshot URL
returned by bootstrap:
curl "https://apis.quran.foundation/content/api/v4/resources/snapshots/word_by_word_transliterations/60" \
-H "x-auth-token: $ACCESS_TOKEN" \
-H "x-client-id: $CLIENT_ID"
Production resource 60 currently contains 77,431 records. The snapshot
envelope's records are ordered by word_id, then id. Each
word_transliteration record has exactly these fields:
{
"id": 1,
"resource_content_id": 60,
"resource_id": 60,
"word_id": 60,
"language_id": 38,
"language_name": "english",
"text": "bis'mi",
"updated_at": "2026-08-19T02:43:00Z"
}
Here resource_id == resource_content_id == ResourceContent.id identifies the
transliteration resource, while word_id identifies the Word owner. Replace all local rows for
word_by_word_transliterations:60 with the returned records before storing
the final sync token.
How Snapshots Relate to Tokens
The sync_token is not sent to the snapshot endpoint. The token belongs to GET /resources/sync only.
During bootstrap, the link between sync and snapshots is the RESOURCE_CREATE mutation:
GET /resources/sync?bootstrap=true...returnsRESOURCE_CREATEchanges.- Each
RESOURCE_CREATEincludes asnapshot_url. - Your app fetches each
snapshot_urland replaces local rows for that resource. - Your app stores the final
next_sync_tokenafter all returned changes have been applied. - Later, your app sends that token back to
GET /resources/syncwith the same canonicalresourcesfilter.
Snapshots are current when fetched. They are not pinned to the exact sequence of the change that asked you to fetch them.
Next Sync
After first sync, use the stored sync_token with the same resources filter. The API returns only newer changes.
curl "https://apis.quran.foundation/content/api/v4/resources/sync?sync_token=$SYNC_TOKEN&resources=chapter_recitations:159;mushafs:1;translations:19;tafsirs:151;word_by_word_transliterations:60&per_page=100" \
-H "x-auth-token: $ACCESS_TOKEN" \
-H "x-client-id: $CLIENT_ID"
Sequence Fields
sequence on a mutation is the server's increasing content-change number for that mutation. Apply mutations in ascending sequence order.
sync_until_sequence on a sync response is the upper bound fixed for that sync run. If the response has more pages, every next_page_url continues the same run with the same upper bound. This keeps pagination stable even if newer content changes are committed while your app is paging.
sync_sequence on a snapshot response is the current server sequence when the snapshot was fetched. Do not use snapshot sync_sequence as your next checkpoint. Only store next_sync_token from the final sync page.
Mutation Types
| Type | Client action |
|---|---|
RESOURCE_CREATE | Fetch snapshot_url, then replace all local rows for that resource. |
RESOURCE_INVALIDATE | Fetch snapshot_url, then replace all local rows for that resource. |
RESOURCE_DELETE | Remove or hide the full local resource. |
RESOURCE_UPDATE | Keep existing rows. Treat it as a resource-level freshness marker. |
ROW_CREATE | Upsert one local row using resource_group, resource_id, record_type, and record_key. |
ROW_UPDATE | Upsert one local row using resource_group, resource_id, record_type, and record_key. |
ROW_DELETE | Delete one local row using resource_group, resource_id, record_type, and record_key. |
Only RESOURCE_CREATE and RESOURCE_INVALIDATE include snapshot_url.
Testing Sync Behavior
You can test bootstrap and snapshot fetches with real public resources. To test pagination, use a resources filter that returns more items than per_page. To observe a no-change incremental sync, call sync again with the final next_sync_token when no matching content changes have occurred.
Mutation scenarios such as row updates, deletes, resource deletes, restores, and invalidations require actual content changes on tracked resources. The public API does not expose a way to force those events on demand.