Metadata-Version: 2.4
Name: tldrapi
Version: 0.1.6
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)
- [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 — chapters of the same book, articles in a series, 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** arranged as a 1D spectrum across the underlying 3D
quality space (LLM x retention x strategy). The 5 bolded rows are the
main anchors; each also accepts a short alias equal to its LLM tier
name (`quick` / `standard` / `deep` / `premium` / `ultra`).

| #  | Preset                | What it delivers                                                                                       |
|---:|-----------------------|--------------------------------------------------------------------------------------------------------|
|  1 | `minimal-quick`       | Cheapest and fastest. Headline-length blurb from a small chunk. Title-level takeaway.                  |
|  2 | **`brief-quick`**     | 3-sentence recap with the fastest LLM. Previews and low-latency feed cards.                            |
|  3 | `minimal-standard`    | Headline blurb with the mid-tier LLM's fluency; still very cheap.                                      |
|  4 | `balanced-quick`      | 3-5 sentences from the fast LLM; slightly deeper than `brief-quick`.                                   |
|  5 | `brief-standard`      | 3-sentence recap with smoother phrasing than `brief-quick`.                                            |
|  6 | **`balanced-standard`** | Balanced coverage without run-ons. The general default for most articles.                            |
|  7 | `thorough-quick`      | Paragraph-length from the fast LLM; retains the top 2-3 supporting facts.                              |
|  8 | `minimal-deep`        | Headline output with the deeper LLM's coherence; frugal way to buy fluency without length.             |
|  9 | `brief-deep`          | 3-sentence recap with deeper-model reasoning.                                                          |
| 10 | **`thorough-deep`**   | Preserves specific dates, names, secondary facts. Research papers, meeting transcripts, long articles. |
| 11 | `detailed-quick`      | Longer paragraph from the fast LLM; more supporting facts, still light on nuance.                      |
| 12 | `thorough-standard`   | Retains dates and names on Standard-class content; great for meeting-transcript recaps.                |
| 13 | `complete-quick`      | Maximum retention the Quick LLM can produce; nearing Standard breadth but Quick tone.                  |
| 14 | `balanced-deep`       | 4-6 sentences with deep-model narrative flow.                                                          |
| 15 | `detailed-standard`   | Full-paragraph, entity-preserving; approaches Deep on retention.                                       |
| 16 | `complete-standard`   | Maximum Standard retention; substantial output length.                                                 |
| 17 | `detailed-deep`       | Heavy retention with deep-model reasoning; picks up minor arguments.                                   |
| 18 | `complete-deep`       | Maximum Deep retention; edging into Premium coverage.                                                  |
| 19 | `minimal-premium`     | Very short output with premium-model tone; premium quality at bargain length.                          |
| 20 | `brief-premium`       | 3-sentence recap with high-fidelity entity handling.                                                   |
| 21 | **`detailed-premium`** | Entity preservation, edge cases, atmospheric detail. Substantial documents and long-form reports.     |
| 22 | `balanced-premium`    | Moderate-length premium coverage; smoother than Deep, more concise than `detailed-premium`.            |
| 23 | `thorough-premium`    | Heavy retention with premium reasoning.                                                                |
| 24 | `minimal-ultra`       | Single-shot on the full document, minimum output length. Ultra fidelity, tiny output.                  |
| 25 | `brief-ultra`         | Full-context single-shot, 3-sentence output. Ideal for research-grade preview blurbs.                  |
| 26 | **`complete-ultra`**  | Single-shot on the full document, maximum retention, no chunking artifacts. Book-length manuscripts, long-form technical documentation. |
| 27 | `complete-premium`    | Maximum Premium retention; almost every noteworthy fact.                                               |
| 28 | `balanced-ultra`      | Full-context single-shot, balanced-length output.                                                      |
| 29 | `thorough-ultra`      | Full-context, retains most secondary facts.                                                            |
| 30 | `detailed-ultra`      | Full-context, near-maximum retention; the top rung of the spectrum.                                    |


**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)
```

## 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.
