sahifa
Python client for Sahifa, the HTML-to-PDF and screenshot API that gets Arabic and right-to-left text right and processes documents in Saudi Arabia.
No arabic_reshaper, no python-bidi, no fonts to install: write HTML, get a PDF with joined Arabic letters, correct word order and mixed Arabic and English, rendered by Chromium in Jeddah.
pip install sahifa
Quick start
Get a free API key at sahifa.dev: 100 renders a month, no card needed.
from sahifa import Sahifa, arabic_document
sahifa = Sahifa() # reads SAHIFA_API_KEY
pdf = sahifa.pdf(
arabic_document('<h1>فاتورة ضريبية مبسطة</h1><p>الإجمالي: <bdi dir="ltr">1,150.00 SAR</bdi></p>'),
format="A4",
margin="15mm",
footer={"source": '<div style="width:100%;text-align:center;font-size:9px">صفحة {{page}} من {{total}}</div>'},
)
with open("invoice.pdf", "wb") as f:
f.write(pdf)
sahifa.pdf(source, **options) calls POST /v3/convert/pdf (PDFShift v3 compatible) and returns bytes. source is HTML or a URL.
Options: format, landscape, margin, header={"source": ...}, footer={"source": ...}, css, javascript, delay (milliseconds), wait_for, pages, zoom. See the PDF reference.
Django
from django.http import HttpResponse
from django.template.loader import render_to_string
from sahifa import Sahifa
sahifa = Sahifa()
def invoice_pdf(request, pk):
html = render_to_string("invoices/detail.html", {"invoice": Invoice.objects.get(pk=pk)})
return HttpResponse(sahifa.pdf(html, format="A4"), content_type="application/pdf")
FastAPI
from fastapi import FastAPI, Response
from sahifa import Sahifa
app, sahifa = FastAPI(), Sahifa()
@app.get("/invoices/{number}.pdf")
def invoice(number: str):
html = templates.get_template("invoice.html").render(number=number)
return Response(sahifa.pdf(html), media_type="application/pdf")
Screenshots
png = sahifa.screenshot(url="https://example.com", full_page=True, block_cookie_banners=True)
jpg = sahifa.screenshot(html="<h1 dir='rtl'>مرحبا</h1>", format="jpeg")
Pass exactly one of url= or html=. Options: format (png, jpeg, webp, pdf), full_page, viewport_width, viewport_height, device_scale_factor, selector, dark_mode, delay (seconds), block_ads, block_cookie_banners. See the screenshot reference.
Errors and retries
Failures raise SahifaError with .status, .code and the API's message. Busy and rate-limit responses (429, 502, 503, 504) and network errors are retried up to max_retries times (default 3), waiting for Retry-After.
from sahifa import SahifaError
try:
sahifa.pdf(html)
except SahifaError as e:
if e.status == 402:
print("Monthly allowance used up")
else:
raise
Options
Sahifa(api_key="sk_live_...", base_url="https://api.sahifa.dev", max_retries=3, timeout=120)
The client can be used as a context manager (with Sahifa() as s:), which closes the HTTP connection pool.
Arabic tips
arabic_document(fragment)wraps HTML in a full document withdir="rtl", UTF-8 and Arabic fonts.- Wrap left-to-right parts (IBANs, VAT numbers, amounts, e-mails) in
<bdi dir="ltr">…</bdi>. - Use
text-align: start/endrather thanleft/right.
Links
Documentation · Examples in five languages · Pricing · Data residency · Support: support@sahifa.dev
MIT License. Sahifa is a service of Yimello LLC.
Metadata
Release files for sahifa 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sahifa-1.0.0.tar.gz | 6.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sahifa-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 12.9 kB
Release files / sahifa-1.0.0.tar.gz
| Download URL | sahifa-1.0.0.tar.gz |
|---|---|
| Size | 6.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5e025186a196f3ba187e97899122f5d33fb5b4b178718914a32e1838502e9e81
|
|
BLAKE2b-256 checksum How to use checksums |
7470b55d4617e6e096522d4a44934c10356ea735f5719d9bb2a582946972fde1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logRelease files / sahifa-1.0.0-py3-none-any.whl
| Download URL | sahifa-1.0.0-py3-none-any.whl |
|---|---|
| Size | 6.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
27ea0a6014f547a79049a3c609e120596ca0c75ee0779cce58c802ef4dd9de5d
|
|
BLAKE2b-256 checksum How to use checksums |
571a0fc856384fad0a076c1f51e84a3eae1bb85f1501f0867d2de005a05f89a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency log