Metadata-Version: 2.4
Name: emailkind
Version: 0.1.0
Summary: Python SDK for the EmailKind API — classify emails by provider and type
Author-email: EmailKind <support@emailkind.com>
License: MIT
Project-URL: Homepage, https://emailkind.com
Project-URL: Documentation, https://emailkind.com/docs/sdks
Project-URL: Repository, https://github.com/emailkind/emailkind-python
Keywords: email,classification,provider,api
Classifier: Development Status :: 4 - Beta
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: Topic :: Communications :: Email
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.20

# EmailKind Python SDK

Official Python SDK for the [EmailKind](https://emailkind.com) API. Classify email addresses by provider (Gmail, Outlook, Google Workspace, etc.) and type (business, personal, disposable, education).

## Installation

```bash
pip install emailkind
```

## Quick Start

```python
from emailkind import EmailKind

client = EmailKind("sk_live_your_api_key")

result = client.classify(email="jane@company.com")
print(result.provider.name)              # "Google Workspace"
print(result.classification.is_business) # True
print(result.confidence)                 # 0.98
```

## Authentication

Pass your API key directly or set the `EMAILKIND_API_KEY` environment variable:

```python
# Explicit
client = EmailKind("sk_live_xxx")

# From environment
import os
os.environ["EMAILKIND_API_KEY"] = "sk_live_xxx"
client = EmailKind()

# Custom base URL
client = EmailKind("sk_live_xxx", base_url="https://custom.endpoint.com")

# Custom timeout (default: 30s)
client = EmailKind("sk_live_xxx", timeout=10)
```

## Classify

Classify a single email address or domain:

```python
# By email
result = client.classify(email="user@gmail.com")

# By domain
result = client.classify(domain="gmail.com")

# With company enrichment
result = client.classify(email="ceo@startup.io", enrich=True)
print(result.company.name)   # "Startup Inc."
print(result.company.source) # "website"
```

### ClassifyResult fields

| Field            | Type             | Description                              |
|------------------|------------------|------------------------------------------|
| `success`        | `bool`           | Whether the request succeeded            |
| `request_id`     | `str`            | Unique request identifier                |
| `email`          | `str`            | Input email (if provided)                |
| `domain`         | `str`            | Domain that was classified               |
| `provider`       | `Provider`       | Provider info (id, name, type)           |
| `classification` | `Classification` | Flags (is_business, is_free, etc.)       |
| `mx`             | `list[str]`      | MX records found                         |
| `confidence`     | `float`          | Confidence score (0.0 to 1.0)           |
| `cached`         | `bool`           | Whether the result was served from cache |
| `company`        | `Company | None` | Enrichment data (only if enrich=True)    |

## Batch Classify

Classify multiple emails and/or domains in one request:

```python
batch = client.classify_batch(
    emails=["user@gmail.com", "ceo@company.com"],
    enrich=True,
)

print(batch.count)  # 2
for item in batch.results:
    print(item.input, item.provider.name, item.classification.is_business)
```

You can also pass domains:

```python
batch = client.classify_batch(domains=["gmail.com", "outlook.com"])
```

## Custom Rules

Manage custom classification rules that override default provider detection:

```python
# List rules
rules = client.list_rules()

# Create a rule
rule = client.create_rule(
    match_type="domain",
    match_value="internal.company.com",
    provider_name="Internal Mail",
    provider_type="business",
)
print(rule.id)

# Delete a rule
client.delete_rule(rule.id)
```

## Bulk Processing

Upload a CSV file for asynchronous bulk classification:

```python
# Upload a file (accepts file path or file object)
job = client.bulk_upload("contacts.csv", enrich=True)
print(job.id)      # "job_xxx"
print(job.status)  # "pending"

# Check status
job = client.bulk_status(job.id)
print(job.status)     # "completed"
print(job.processed)  # 1500

# Download results as CSV bytes
csv_data = client.bulk_results(job.id)
with open("results.csv", "wb") as f:
    f.write(csv_data)

# List all bulk jobs
jobs = client.bulk_list()
for j in jobs:
    print(j.id, j.status, j.processed, "/", j.total)
```

You can also pass a file object:

```python
with open("contacts.csv", "rb") as f:
    job = client.bulk_upload(f)
```

## Error Handling

The SDK raises typed exceptions for different error conditions:

```python
from emailkind import (
    EmailKind,
    EmailKindError,
    AuthenticationError,
    RateLimitError,
    ValidationError,
    ForbiddenError,
    NotFoundError,
)

client = EmailKind("sk_live_xxx")

try:
    result = client.classify(email="test@example.com")
except AuthenticationError as e:
    # Invalid or missing API key (401)
    print("Auth failed:", e.message)
except RateLimitError as e:
    # Too many requests (429)
    print("Rate limited, retry after:", e.retry_after, "seconds")
except ValidationError as e:
    # Bad request parameters (400)
    print("Invalid input:", e.message)
except ForbiddenError as e:
    # Insufficient permissions (403)
    print("Forbidden:", e.message)
except NotFoundError as e:
    # Resource not found (404)
    print("Not found:", e.message)
except EmailKindError as e:
    # Any other API error
    print("Error:", e.code, e.message, e.request_id)
```

All exceptions inherit from `EmailKindError` and include:

| Attribute     | Type         | Description                  |
|---------------|--------------|------------------------------|
| `message`     | `str`        | Human-readable error message |
| `code`        | `str | None` | Error code (e.g. `RATE_LIMIT_EXCEEDED`) |
| `request_id`  | `str | None` | Request ID for support       |
| `status_code` | `int | None` | HTTP status code             |

`RateLimitError` also includes `retry_after` (seconds to wait before retrying).

## Requirements

- Python 3.8+
- `requests` >= 2.20

## License

MIT
