Metadata-Version: 2.4
Name: quran-foundation-api
Version: 0.1.3
Summary: Official Python SDK for Quran Foundation APIs.
Project-URL: Homepage, https://api-docs.quran.foundation/docs/sdk/python/
Project-URL: Documentation, https://api-docs.quran.foundation/docs/sdk/python/
Project-URL: Support, https://api-docs.quran.foundation/request-access/
Author-email: Quran Foundation <developers@quran.foundation>
License-Expression: MIT
License-File: LICENSE
Keywords: api,oauth2,quran,quran-foundation,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.27
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Requires-Dist: twine>=6.0; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.115; extra == 'fastapi'
Requires-Dist: uvicorn>=0.30; extra == 'fastapi'
Description-Content-Type: text/markdown

# quran-foundation-api

Official Python SDK for Quran Foundation APIs.

```bash
pip install quran-foundation-api
```

```python
from quran_foundation import QuranClient

client = QuranClient(client_id="YOUR_CLIENT_ID", access_token="ACCESS_TOKEN")
chapters = client.list_chapters()
print(chapters)
```

## Content and search

```python
from quran_foundation import QuranClient

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

chapter = client.get_chapter(1)
verses = client.get_verses_by_range("1:1", "1:7", translations="131")
translations = client.list_translations()
results = client.search("mercy", mode="advanced", params={"size": 10})
```

### Content resource sync

Mushafs and word-by-word translations can be included in content sync and fetched
as resource snapshots:

```python
changes = client.sync_resources(
    params={
        "bootstrap": True,
        "resources": "mushafs:1;word_by_word_translations:85",
    }
)
snapshot = client.get_word_by_word_translation_snapshot(85)
mushaf_snapshot = client.get_mushaf_snapshot(1)
```

Mushaf snapshots contain the layout metadata, pages, publicly distributable font
assets, and words needed for offline use. The API also exposes approved, shareable
word-translation resources; transliteration resources are excluded.

For endpoints that do not yet have a dedicated helper, use the service request helpers:

```python
client.content_request("/verses/by_page/1", params={"translations": "131"})
client.search_request("/api/v1/search", params={"query": "mercy", "mode": "quick"})
client.user_request("/bookmarks", params={"page": 1})
```

## Analytics Events

Analytics submission is server-side and requires a client-credentials access
token with the `analytics.events.write` scope. Keep the client secret and token
in server-side environment variables.

```python
import os
from datetime import datetime, timezone

from quran_foundation import AnalyticsEvent, QuranClient
from quran_foundation.oauth import client_credentials

token = client_credentials(
    client_id=os.environ["QURAN_CLIENT_ID"],
    client_secret=os.environ["QURAN_CLIENT_SECRET"],
    scope="analytics.events.write",
)
client = QuranClient(
    client_id=os.environ["QURAN_CLIENT_ID"],
    access_token=token["access_token"],
)

result = client.submit_analytics_events(
    [
        AnalyticsEvent(
            event_id="stable-event-id-1",
            name="quran.reader.verse_viewed",
            version=1,
            occurred_at=datetime.now(timezone.utc),
            user_id="QURAN_FOUNDATION_USER_ID",
            session_id="session-123",
            properties={"verse_key": "2:255", "surface": "reader"},
        ),
        AnalyticsEvent(
            event_id="stable-event-id-2",
            name="quran.app.started",
            version=1,
            occurred_at=datetime.now(timezone.utc),
            anonymous_id="anonymous-123",
        ),
    ]
)
```

A successful response accepts the complete batch. Retry a failed batch with
the same event IDs so downstream processing can identify duplicates. Use a
Quran Foundation OAuth user ID for `user_id`; omit it for guests or unknown
users.

## User APIs

Use signed-in User API helpers with a user access token. Keep the token server-side.

```python
profile = client.get_profile()
bookmarks = client.list_bookmarks()
client.create_bookmark({"verse_key": "2:255", "mushaf_id": 1})
client.update_preference({"key": "theme", "value": "dark"})
```

## OAuth2 helpers

Use OAuth helpers on the server side. Never expose `client_secret`, access tokens, or refresh tokens to browser code.

```python
from quran_foundation.oauth import build_authorization_url, create_pkce_pair, exchange_code

verifier, challenge = create_pkce_pair()
authorize_url = build_authorization_url(
    client_id="YOUR_CLIENT_ID",
    redirect_uri="https://your-app.com/callback",
    scope="openid offline_access user bookmark",
    state="random-state",
    code_challenge=challenge,
)

tokens = exchange_code(
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    code="CODE_FROM_CALLBACK",
    code_verifier=verifier,
    redirect_uri="https://your-app.com/callback",
)
```

## Development

```bash
python -m pip install -e ".[dev]"
ruff check .
pytest
python -m build
twine check dist/*
```

## Contributing and releases

Public API changes require tests, documentation, a changelog entry, and a
version bump. Merging to `main` runs CI but does not publish the package. See
[`CONTRIBUTING.md`](CONTRIBUTING.md) for the versioning and release process.
