Metadata-Version: 2.4
Name: tldrapi
Version: 0.1.4
Summary: Official Python SDK for the TLDRapi summarization API. Summarize text at 5 quality tiers with 20+ built-in voice styles.
Author-email: Ehren Biglari <unitycubedapps@gmail.com>
License: MIT
Project-URL: Homepage, https://unitycubed.com/tldrapi
Project-URL: Documentation, https://unitycubed.com/tldrapi/docs
Project-URL: Repository, https://github.com/unitycubedapps/tldrapi-python
Project-URL: Issues, https://github.com/unitycubedapps/tldrapi-python/issues
Keywords: tldrapi,summarize,summarizer,summary,tldr,abstract,llm,ai,nlp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
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: Topic :: Text Processing :: Linguistic
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1.0,>=0.24
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: respx>=0.20; extra == "dev"
Dynamic: license-file

# tldrapi — Python SDK for TLDRapi

Official Python client for [TLDRapi](https://tldrapi.com) — turn any
content into a clean summary in one API call.

- **Free tier** — 100 credits per month, no card, no trial expiry
- **20+ input formats** — text, HTML, Markdown, PDF (with OCR), .docx,
  .doc, .odt, .rtf, .epub, JSON, YAML, CSV, transcripts
- **5 quality tiers** — pick latency vs. depth per call
- **Custom voice styles** — 20+ built-in voices; paid tiers can define
  their own with plain-English instructions
- **Multi-provider routing** — automatic failover across Anthropic,
  OpenAI, Groq, Gemini, and OpenRouter
- **Refunds you don't have to ask for** — every summary is judge-scored
  and mis-summaries are auto-refunded
- **Sync + async clients** — one thin runtime dep (`httpx`), typed
  exceptions per error class

```bash
pip install tldrapi
# or, on systems with both Python 2 and 3 installed:
pip3 install tldrapi
```

Python 3.8+. `pip` and `pip3` both work — on modern systems they point
at the same interpreter, but `pip3` is the safe choice if you have a
legacy Python 2 install on `PATH`.

## Table of contents

- [Getting your free key](#getting-your-free-key)
- [Hello world](#hello-world)
- [Async](#async)
- [Examples gallery](#examples-gallery)
  - [Summarize an article by URL](#summarize-an-article-by-url)
  - [Summarize a PDF (with OCR)](#summarize-a-pdf-with-ocr)
  - [Summarize a Word / RTF / EPUB file](#summarize-a-word--rtf--epub-file)
  - [Summarize a long document asynchronously](#summarize-a-long-document-asynchronously)
  - [Pin a session across many summaries](#pin-a-session-across-many-summaries)
  - [Batch summarize in parallel (async)](#batch-summarize-in-parallel-async)
  - [Convert-only: extract text without summarizing](#convert-only-extract-text-without-summarizing)
  - [PDF → LaTeX](#pdf--latex)
  - [Custom voice: teach the model your tone](#custom-voice-teach-the-model-your-tone)
  - [Handle a rate-limit with backoff](#handle-a-rate-limit-with-backoff)
  - [Show live credit balance to your user](#show-live-credit-balance-to-your-user)
  - [Advanced quality controls — 3 axes, 30 named presets](#advanced-quality-controls)
- [Quality tiers](#quality-tiers)
- [Error handling](#error-handling)
- [Configuration + retries](#configuration--retries)
- [Rates + usage endpoints](#rates--usage-endpoints)
- [Development](#development)
- [License](#license)

## Getting your free key

1. Sign in at [rapidapi.com](https://rapidapi.com)
2. Subscribe to the [TLDRapi Summarizer](https://rapidapi.com/thunderAPIs256/api/tldrapi-summarizer)
   listing — choose **BASIC (Free)**
3. Open the listing → **Console** → **Applications** → **Add App**
4. In the App → **Authorizations** tab → copy the Authorization Key

Pass that to the SDK constructor as `rapidapi_key`. Everything on the
free tier works exactly like the paid tiers — same endpoints, same
response shape, same SDK — just with a 100-credit monthly cap.

## Hello world

```python
from tldrapi import TLDRapi

client = TLDRapi(rapidapi_key="YOUR_RAPIDAPI_KEY")

result = client.summarize("Some long article body here...")
print(result.summary)
```

`client.summarize()` defaults to `tier="standard"` and works on the
free tier. The full response also carries `result.request_id` (share
this with support when reporting an issue), `result.session_id` (see
[session pinning](#pin-a-session-across-many-summaries)), and
`result.credits` (a snapshot of what this call consumed and what you
have left).

## Async

```python
import asyncio
from tldrapi import AsyncTLDRapi

async def main():
    async with AsyncTLDRapi(rapidapi_key="YOUR_RAPIDAPI_KEY") as client:
        r = await client.summarize("Long text here...", tier="deep")
        print(r.summary)

asyncio.run(main())
```

Every method on `TLDRapi` has an `async def` twin on `AsyncTLDRapi`
with the same signature.

## Examples gallery

### Summarize an article by URL

TLDRapi accepts URLs directly — the server fetches, extracts main
content, strips nav/ads, and summarizes.

```python
result = client.summarize(
    "https://arxiv.org/abs/1706.03762",
    tier="deep",
)
print(result.summary)
```

Works with HTML pages, news sites, GitHub READMEs, blog posts, and
academic PDFs served over HTTP.

### Summarize a PDF (with OCR)

```python
# PDF with selectable text — instant path
with open("report.pdf", "rb") as f:
    text = client.convert_pdf_to_latex(f, filename="report.pdf")

# Scanned PDF (no selectable text) — automatic OCR fallback
with open("scanned.pdf", "rb") as f:
    text = client.convert_pdf_to_latex(f, filename="scanned.pdf",
                                       backend="modal")
```

`convert_pdf_to_latex` returns LaTeX for downstream typesetting, or
plain markdown-style text if you don't need LaTeX; pipe it back into
`summarize()` if all you want is a summary.

### Summarize a Word / RTF / EPUB file

```python
with open("chapter.docx", "rb") as f:
    doc = client.convert_docx_to_text(f, filename="chapter.docx")

r = client.summarize(doc.output, tier="premium")
print(r.summary)
```

Same pattern for `.doc`, `.odt`, `.rtf`, `.epub`, `.html`, `.md`,
`.json`, `.yaml`, `.csv`.

### Summarize a long document asynchronously

For inputs that may take longer than the sync HTTP timeout, submit
async and poll:

```python
request_id = client.submit_async(giant_document, tier="ultra")

# blocks + polls in the background; 5-min cap by default
result = client.wait_for_result(request_id)
print(result.summary)
```

Or poll manually:

```python
request_id = client.submit_async(giant_document, tier="ultra")

while True:
    r = client.get_result(request_id)
    if r is not None:
        print(r.summary); break
    time.sleep(5)
```

Credits are deducted at submit time and refunded on failure, same as
sync.

### Pin a session across many summaries

Session pinning keeps the same underlying model — and, in the future,
the same in-memory context — for a batch of related documents:

```python
r1 = client.summarize("Doc 1")
r2 = client.summarize("Doc 2", session_id=r1.session_id)
r3 = client.summarize("Doc 3", session_id=r1.session_id)
```

Useful when you want consistent voice across a run — legal briefs in
the same case file, chapters of the same book, tickets in the same
support thread.

### Batch summarize in parallel (async)

```python
import asyncio
from tldrapi import AsyncTLDRapi

async def summarize_many(texts):
    async with AsyncTLDRapi(rapidapi_key="YOUR_KEY") as client:
        return await asyncio.gather(*(
            client.summarize(t, tier="quick") for t in texts
        ))

summaries = asyncio.run(summarize_many([...list of 50 docs...]))
```

The client is fully concurrent-safe. Free-tier is rate-limited so
throttle to ~3 rps; paid tiers are much higher.

### Convert-only: extract text without summarizing

Sometimes you just want the text — pull the words out of a doc without
paying for a summary:

```python
r = client.convert_html_to_text("<h1>Hi</h1><p>Content...</p>")
print(r.output)
```

Available: `convert_json_to_text`, `convert_html_to_text`,
`convert_md_to_text`, `convert_doc_to_text`, `convert_docx_to_text`.

### PDF → LaTeX

Round-trip a PDF through TLDRapi's PDF pipeline and get LaTeX back —
handy when the downstream is a document generator:

```python
with open("paper.pdf", "rb") as f:
    r = client.convert_pdf_to_latex(f, filename="paper.pdf")

if r.job_id:                          # server chose async path
    r = client.wait_pdf(r.job_id)     # or client.pdf_status(job_id)
print(r.output)                       # LaTeX source
```

### Custom voice: teach the model your tone

Paid tiers can register a natural-language voice instruction and reuse
it as a per-call `voice_name` on future summaries:

```python
sub = client.custom_prompt_submit(
    voice_name="brand-tone",
    instruction=(
        "Write in the second person, active voice. Prefer verbs over "
        "nouns. Keep sentences under 20 words. Avoid corporate jargon "
        "('leverage', 'synergy'). Aim for the reading level of a "
        "well-written newspaper."
    ),
)
# sub.id is your prompt id; sub.status transitions from 'pending' → 'approved'/'rejected'
```

Once approved:

```python
r = client.summarize(text, voice_name="brand-tone")
```

Approval is automatic — the server runs the instruction against a
judge that checks for policy compliance. Rejections come back with
`sub.rejection_reason`.

### Handle a rate-limit with backoff

```python
import time
from tldrapi import RateLimitError

for attempt in range(3):
    try:
        r = client.summarize(text, tier="deep")
        break
    except RateLimitError as e:
        time.sleep(e.retry_after_seconds or 60)
else:
    raise RuntimeError("gave up after 3 rate-limit retries")
```

The SDK exposes the server's `Retry-After` header on
`RateLimitError.retry_after_seconds`.

### Show live credit balance to your user

```python
u = client.usage()
print(f"You have {u.credits_remaining} credits left ({u.plan})")

r = client.summarize(text)
print(f"That call cost {r.credits.get('charged')} credits.")
print(f"Remaining: {r.credits.get('remaining')}")
```

Every summarize response carries a `credits` snapshot so you don't need
a separate `usage()` round-trip on every call.

### Advanced quality controls

Every summarize call has three orthogonal knobs. You can send zero of
them (defaults are fine), or a named preset, or set 1-3 optional axes,
or combine — axes override the preset and the server returns
`X-Quality-Warning`.

**30 named presets.** `tier` can be one of five short names —
`quick`, `standard`, `deep`, `premium`, `ultra` — or one of 25
compound names like `thorough-standard` or `complete-quick`.

**Three optional axis overrides.** Any subset:

- `optional_quality` — LLM tier: `quick | standard | deep | premium | ultra`
- `optional_extractive_lvl` — retention level: `minimal | brief | balanced | thorough | detailed | complete`
- `optional_strategy` — inference strategy: `contextual-compression | premium-single-shot | hierarchical-merge`

```python
# named preset
r = client.summarize(text, tier="thorough-quick")

# preset + one axis override — axes win, warning header returned
r = client.summarize(text, tier="premium",
                    optional_extractive_lvl="brief")

# all three axes, no preset
r = client.summarize(text,
                    optional_quality="ultra",
                    optional_extractive_lvl="complete",
                    optional_strategy="premium-single-shot")

# opt into permissive downgrade on paid-tier
r = client.summarize(text, tier="premium", allow_downgrade=True)
```

## Quality tiers

Each tier is a canonical bundle of (LLM class, chunk size, extractive
retention, inference strategy) tuned for a use case:

| Tier      | Reads at once   | Best for                          |
|-----------|----------------:|-----------------------------------|
| quick     |     4K tokens   | Short texts, previews             |
| standard  |    16K tokens   | Default — most articles           |
| deep      |    32K tokens   | Longer content, deeper reasoning  |
| premium   |    64K tokens   | Substantial documents             |
| ultra     |   100K tokens   | Long-form / research-grade        |

Live rates and per-tier detail available at
[/rates](https://tldrapi.com/rates) or `client.rates()`.

### Paid-tier quality guarantees

Paid tiers WAIT for a specific canonical model rather than silently
mixing peer models. Opt into permissive fallback with
`allow_downgrade=True` — the worker walks DOWN the ladder (premium →
deep → standard → quick) and returns whichever tier's primary is
available. Response carries `X-Quality-Actual` and `X-Original-Tier`
when a downgrade happened, and the credit-cost delta is automatically
refunded.

## Error handling

Every SDK exception inherits from `TLDRapiError`. Catch broadly for a
safety net or narrowly to branch on failure mode:

```python
from tldrapi import (
    TLDRapiError, InsufficientCreditsError, RateLimitError,
    LanguageNotSupportedError, QualitySelectionRequiresPaidPlanError,
    ServerError, TimeoutError, AuthenticationError,
)

try:
    r = client.summarize(user_text, tier="deep")
except InsufficientCreditsError as e:
    top_up_url = e.response_body.get("options", {}).get("top_up", {}).get("url")
    # …prompt the user to top up…
except RateLimitError as e:
    time.sleep(e.retry_after_seconds or 60)
    # …then retry…
except LanguageNotSupportedError:
    # English-only at launch; cross-lingual coming Month 2-3
    ...
except QualitySelectionRequiresPaidPlanError:
    # Free plan can't pick tier; retry without tier=
    r = client.summarize(user_text)
except AuthenticationError:
    # Bad key
    ...
except TimeoutError:
    r = client.summarize(user_text, tier="deep", timeout=120)
except ServerError:
    # 5xx after the SDK's 3 retries
    ...
except TLDRapiError as e:
    print(f"TLDRapi error {e.status_code} (req {e.request_id}): {e}")
```

Every error carries `.status_code`, `.request_id` (X-Request-ID —
attach when reporting bugs), and `.response_body` (parsed JSON error
body).

## Configuration + retries

```python
client = TLDRapi(
    rapidapi_key="YOUR_KEY",
    rapidapi_host="tldrapi-summarizer.p.rapidapi.com",  # staging override
    base_url=None,                       # default = https://{rapidapi_host}
    timeout=60.0,                        # per-request seconds
    retries=3,                           # 5xx + network only
)
```

Automatic retries on 5xx and transient network failures with
exponential backoff + jitter (3 attempts default). 4xx and 429 are
**not** retried — the SDK exposes `RateLimitError.retry_after_seconds`
so you can honor the server's window.

## Rates + usage endpoints

```python
r = client.rates()                # live per-tier credits + base costs
u = client.usage()                # this month's usage + plan info
h = client.rates_history()        # audit log of pricing changes
rng = client.usage_range("2026-09-01", "2026-09-15")   # date-range usage
```

## Development

```bash
pip install -e '.[dev]'
pytest -q
```

## License

Released under the MIT License — see [LICENSE](LICENSE).

Copyright (c) 2026 Ehren Biglari / Unity Cubed.
