Metadata-Version: 2.4
Name: whatsapp-data-sdk
Version: 1.0.0
Summary: Typed Python client for the WhatsApp Data API (checkleaked.cc / RapidAPI): profile info, phone breaches, carrier lookup, Telegram, OSINT, bulk checks and a money-saving cache-first lookup.
Project-URL: Homepage, https://whatsapp.checkleaked.cc
Project-URL: Repository, https://github.com/eduair94/whatsapp-data-sdk-python
Project-URL: Issues, https://github.com/eduair94/whatsapp-data-sdk-python/issues
Project-URL: Changelog, https://github.com/eduair94/whatsapp-data-sdk-python/blob/main/CHANGELOG.md
Author-email: Eduardo Airaudo <airaudoeduardo@gmail.com>
License: MIT
License-File: LICENSE
Keywords: breach,carrier,checkleaked,leak,lookup,osint,phone,rapidapi,sdk,telegram,whatsapp
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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: Typing :: Typed
Requires-Python: >=3.8
Requires-Dist: requests>=2.25
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Requires-Dist: types-requests; extra == 'dev'
Description-Content-Type: text/markdown

# whatsapp-data-sdk (Python)

[![PyPI version](https://img.shields.io/pypi/v/whatsapp-data-sdk.svg)](https://pypi.org/project/whatsapp-data-sdk/)
[![Python versions](https://img.shields.io/pypi/pyversions/whatsapp-data-sdk.svg)](https://pypi.org/project/whatsapp-data-sdk/)
[![CI](https://github.com/eduair94/whatsapp-data-sdk-python/actions/workflows/ci.yml/badge.svg)](https://github.com/eduair94/whatsapp-data-sdk-python/actions/workflows/ci.yml)
[![license: MIT](https://img.shields.io/pypi/l/whatsapp-data-sdk.svg)](./LICENSE)
[![website](https://img.shields.io/badge/website-whatsapp.checkleaked.cc-2ea44f)](https://whatsapp.checkleaked.cc)

Typed Python client for the **WhatsApp Data API** — official SDK for **[whatsapp.checkleaked.cc](https://whatsapp.checkleaked.cc)** (also on RapidAPI). The Python port of [`whatsapp-data-sdk`](https://www.npmjs.com/package/whatsapp-data-sdk) (npm), same endpoints and behavior.

WhatsApp profile lookups, phone breaches, carrier data, Telegram checks, OSINT, geo/business search, bulk verification — plus a **cache-first lookup** that reads the cheap DB endpoint before spending on a live check.

- 🐍 Typed (`TypedDict` responses, `py.typed`), one dependency (`requests`).
- 💸 Built-in cache-first strategy to minimize cost.
- 🔁 Timeouts, retries with backoff (Retry-After aware), and a clean error taxonomy.

## Install

```bash
pip install whatsapp-data-sdk
```

## Quick start

```python
from whatsapp_data_sdk import WhatsAppDataClient

client = WhatsAppDataClient(api_key="YOUR_KEY")  # sent as x-rapidapi-key

profile = client.get_profile("59898297150")
print(profile.get("exists"), profile.get("about"), profile.get("profilePic"))
```

## Authentication & hosts

Your key is always sent in `x-rapidapi-key`. Choose where requests go with `transport`:

| `transport`        | Live host                       | Cache / DB-only host              |
| ------------------ | ------------------------------- | --------------------------------- |
| `"proxy"` (default)| `whatsapp-proxy.checkleaked.cc` | same host, `/number_cache` path   |
| `"rapidapi"`       | `wp-data.p.rapidapi.com`        | `wp-data-db-only.p.rapidapi.com`  |

```python
client = WhatsAppDataClient(api_key="RAPIDAPI_KEY", transport="rapidapi")
```

Override URLs/hosts via `base_url`, `cache_base_url`, `rapidapi_host`, `rapidapi_cache_host`.

## 💸 Cache-first lookups (save money)

`check_cached()` reads the cheap cache / DB-only endpoint first and only falls back to a paid live check on a true miss.

```python
r1 = client.check_cached("59898297150")                  # cacheFirst (default)
print(r1["_source"])                                     # "cache" | "live"
r2 = client.check_cached("59898297150", mode="cacheOnly")  # never a live call
r3 = client.check_cached("59898297150", mode="live")       # always fresh
```

An authoritative cached "not on WhatsApp" record counts as a hit (no wasteful live re-check); only a true `NOT_IN_DATABASE` miss falls through to live.

## API

Every method maps 1:1 to a marketplace endpoint.

```python
# Profile & pictures
client.get_profile(number, telegram=True, include_carrier=True)  # Get Profile Information
client.get_profile_no_picture(number)                            # (No profile pic)
pic = client.get_last_picture(number)                            # JPEG bytes -> pic.content, pic.to_data_uri()
client.get_device_count(number)                                  # Device count
client.get_osint_info(number)                                    # OSINT Info

# Breaches
client.get_phone_breaches(number, limit=50)                      # built-in (no external key)
client.get_phone_breaches_external(number, api_key="...")        # your checkleaked.cc key

# Search & database
client.search(country_code="UY", is_business=True, limit=20)     # Search
client.search_google_maps(latitude=-34.9, longitude=-56.16, radius=2000)  # Search in Google Maps
client.get_leaked_numbers_info()                                 # Leaked Numbers (500M) -> Drive link (MEGA tier)
client.download_entire_database()                                # Download the Entire DB -> Drive link (MEGA tier)

# Bulk
client.bulk_check(numbers)                                       # live (max 50)
client.bulk_check_db_basic(numbers)                              # DB basic (max 1000)
client.bulk_check_db_full(numbers)                               # DB full (max 1000)

# Telegram / carrier
client.get_telegram_profile("59898297150")                      # experimental
client.get_telegram_profile(["num1", "num2"])
client.carrier_lookup(number)                                    # Carrier Lookup
client.carrier_lookup_alt(number, page=2)                        # Carrier Lookup Alternative
```

## "Not found" resolves — it does not raise

A number **not on WhatsApp** is a normal result, so these resolve (never raise):

- `get_profile` / `get_profile_no_picture` / `check_cached` → a dict with `exists=False` (and `code="NUMBER_NOT_FOUND"`).
- `search` with zero matches → an empty page (`success=False`, `data["docs"] == []`).
- `get_device_count` for a non-WhatsApp number → `success=False`.

What **does** raise: invalid number (400), auth (401/403), rate limit (429), timeout, network, 5xx.

## Per-request options

Every method accepts `timeout` (seconds) and `retries`:

```python
client.get_profile("59898297150", timeout=5, retries=0)
```

## Errors

All failures raise a subclass of `WhatsAppDataError`:

| Class | When |
| --- | --- |
| `ValidationError` | Bad arguments (raised before any request; no `.status`). |
| `AuthError` | 401 / 403 — missing/invalid/unsubscribed key. |
| `RateLimitError` | 429 — carries `.retry_after_ms` when the server sent `Retry-After`. |
| `HttpError` | Any other non-2xx (e.g. 400, 5xx); has `.status`, `.body`. |
| `TimeoutError` | Request exceeded the timeout. |
| `NetworkError` | Transport failure. |
| `WhatsAppDataError` | Base class. |

```python
from whatsapp_data_sdk import AuthError, RateLimitError, WhatsAppDataError

try:
    client.get_profile("59898297150")
except AuthError:
    ...  # 401/403
except RateLimitError as e:
    print(e.retry_after_ms)
except WhatsAppDataError as e:
    print(e.status, e.body)
```

> POSTs (`bulk_check`, `bulk_check_db_*`) are **not** auto-retried, to avoid double-spending quota.

## Phone number formats

Any format works — the SDK normalizes; the helpers are exported:

```python
from whatsapp_data_sdk import to_digits, to_e164
to_digits("+598 98 297 150")  # "59898297150"  (path & live endpoints)
to_e164("59898297150")        # "+59898297150" (bulk-DB endpoints require it)
```

## Notes

Enrichments run entirely server-side — you never supply Gemini, LeakCheck, or any other keys. Face analysis, breach lookups, etc. are handled by the API. The only user-provided key is your own checkleaked.cc key for `get_phone_breaches_external`.

## License

MIT © Eduardo Airaudo
