Metadata-Version: 2.4
Name: faceapi-client
Version: 1.0.1
Summary: Official Python SDK for the FaceAPI — privacy-first face recognition
Author-email: zeinvob <minhajuli958@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://face-recognition-api-om7k.onrender.com/portal/
Project-URL: Documentation, https://face-recognition-api-om7k.onrender.com/docs/
Project-URL: Repository, https://github.com/zeinvob/face-recognition-api
Keywords: face-recognition,biometrics,api,sdk
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28

# faceapi-client

Official Python SDK for the **FaceAPI** — a privacy-first face recognition service.

- No images ever stored — only AES-256 encrypted face descriptors
- Anti-replay protection built in (nonces handled automatically)
- Multi-tenant collections — one key, many isolated user pools
- GDPR purge endpoint included

**Live API:** https://face-recognition-api-om7k.onrender.com  
**Get your free API key:** https://face-recognition-api-om7k.onrender.com/portal/  
**Interactive docs:** https://face-recognition-api-om7k.onrender.com/docs/

---

## Installation

```bash
pip install faceapi-client
```

You also need a face recognition library to extract descriptors from images. The most common:

```bash
pip install face-recognition
```

> `face-recognition` requires `cmake` and `dlib`. On Mac: `brew install cmake`. On Ubuntu: `sudo apt install cmake`.

---

## Get an API Key

1. Go to https://face-recognition-api-om7k.onrender.com/portal/register/
2. Create a free account
3. Click **New Key** on the dashboard
4. Copy your key — it starts with `fk_live_` and is shown only once

---

## Quick Start

```python
import face_recognition
from faceapi import FaceAPIClient

client = FaceAPIClient(api_key="fk_live_your_key_here")

# 1. Extract descriptor from a photo (runs locally — no photo sent to API)
image      = face_recognition.load_image_file("photo.jpg")
descriptor = face_recognition.face_encodings(image)[0].tolist()

# 2. Enroll a face
result = client.enroll(descriptor=descriptor, label="john_doe")
face_id = result.face_id  # save this in your database

# 3. Verify — is this the same person?
result = client.verify(descriptor=descriptor, face_id=face_id)
print(result.verified)    # True / False
print(result.confidence)  # 0.93

# 4. Identify — who is this person?
result = client.identify(descriptor=descriptor)
print(result.label)       # "john_doe"
print(result.confidence)  # 0.91
```

---

## Full API Reference

### `FaceAPIClient(api_key, base_url, timeout)`

| Parameter  | Type | Default | Description |
|---|---|---|---|
| `api_key`  | str | required | Your API key (`fk_live_...`) |
| `base_url` | str | production URL | Override for self-hosted instances |
| `timeout`  | int | 30 | Request timeout in seconds |

```python
client = FaceAPIClient(api_key="fk_live_...")
```

---

### `client.enroll(descriptor, label, collection_id)`

Enroll a face. Call this once per person and store the returned `face_id`.

| Parameter      | Type | Required | Description |
|---|---|---|---|
| `descriptor`   | list[float] | Yes | 128-float list from your face model |
| `label`        | str | No | Human-readable ID (e.g. `"emp_001"`) |
| `collection_id`| str | No | Namespace for tenant isolation |

```python
result = client.enroll(
    descriptor=descriptor,
    label="alice",
    collection_id="my_company",
)

print(result.face_id)   # "3f2a1b..." — store this
print(result.enrolled)  # True = new face, False = existing label updated
```

---

### `client.verify(descriptor, face_id, collection_id)`

1:1 match — check if a face matches a specific enrolled face.

```python
result = client.verify(
    descriptor=descriptor,
    face_id="3f2a1b...",
)

if result.verified:
    print(f"Identity confirmed! Confidence: {result.confidence:.0%}")
else:
    print("Face does not match")
```

| Field          | Type | Description |
|---|---|---|
| `verified`     | bool | True if faces match |
| `confidence`   | float | Match score 0.0–1.0 |
| `threshold`    | float | The threshold used for this key |
| `processing_ms`| int | Server-side processing time |

---

### `client.identify(descriptor, collection_id)`

1:N search — find who this person is among all enrolled faces.

```python
result = client.identify(
    descriptor=descriptor,
    collection_id="my_company",  # search only this namespace
)

if result.identified:
    print(f"Hello, {result.label}! Confidence: {result.confidence:.0%}")
else:
    print("Unknown person — access denied")
```

| Field          | Type | Description |
|---|---|---|
| `identified`   | bool | True if a match was found |
| `label`        | str | The matched person's label |
| `face_id`      | str | The matched face_id |
| `confidence`   | float | Match score 0.0–1.0 |

---

### `client.list_faces(collection_id)`

List all enrolled faces for your API key.

```python
result = client.list_faces(collection_id="my_company")

print(f"Total enrolled: {result.count}")
for face in result.faces:
    print(face.face_id, face.label, face.enrolled_at)
```

---

### `client.delete_face(face_id)`

Delete a single enrolled face permanently.

```python
client.delete_face(face_id="3f2a1b...")
```

---

### `client.purge(collection_id)`

Permanently delete all faces — useful for GDPR right-to-erasure.

```python
# Delete one user's data only
result = client.purge(collection_id="user_123")
print(f"Deleted {result.deleted_count} faces")

# Delete everything for your API key
result = client.purge()
```

---

### `client.usage()`

Check your API key usage and quota.

```python
stats = client.usage()
print(f"Plan: {stats.tier}")
print(f"Used today: {stats.used_today} / {stats.daily_limit or 'unlimited'}")
print(f"Total requests: {stats.total_requests}")
print(f"Confidence threshold: {stats.confidence_threshold}")
```

---

## Collections — Multi-Tenant Isolation

If your app serves multiple users or companies, use `collection_id` to keep their faces completely isolated from each other.

```python
# Enroll faces under different tenants
client.enroll(descriptor=d1, label="alice", collection_id="company_a")
client.enroll(descriptor=d2, label="bob",   collection_id="company_b")

# Identify only searches within the given collection
client.identify(descriptor=d1, collection_id="company_a")  # finds alice
client.identify(descriptor=d1, collection_id="company_b")  # finds nothing
```

---

## Error Handling

```python
from faceapi import (
    FaceAPIClient,
    AuthenticationError,   # Invalid or revoked API key
    RateLimitError,        # Daily limit reached or IP locked
    NotFoundError,         # face_id not found
    InvalidDescriptorError,# Descriptor must be 128 floats
    NonceError,            # Nonce expired or already used
    FaceAPIError,          # Base class for all errors
)

try:
    result = client.verify(descriptor=descriptor, face_id=face_id)
except AuthenticationError:
    print("Check your API key")
except RateLimitError as e:
    print(f"Slow down: {e}")
except NotFoundError:
    print("face_id not found — was it deleted?")
except FaceAPIError as e:
    print(f"API error [{e.code}]: {e}")
```

---

## Real-World Examples

### Attendance System

```python
import face_recognition
from faceapi import FaceAPIClient

client = FaceAPIClient(api_key="fk_live_...")

def enroll_employee(photo_path: str, employee_id: str) -> str:
    image      = face_recognition.load_image_file(photo_path)
    descriptor = face_recognition.face_encodings(image)[0].tolist()
    result     = client.enroll(descriptor=descriptor, label=employee_id, collection_id="staff")
    return result.face_id

def clock_in(photo_path: str):
    image      = face_recognition.load_image_file(photo_path)
    descriptor = face_recognition.face_encodings(image)[0].tolist()
    result     = client.identify(descriptor=descriptor, collection_id="staff")

    if result.identified:
        print(f"Welcome, {result.label}! Clocked in.")
    else:
        print("Face not recognized. Access denied.")

enroll_employee("john.jpg", "EMP001")
clock_in("camera.jpg")
```

---

### Door Access Control

```python
def check_access(camera_frame_path: str, allowed_face_ids: list) -> bool:
    image      = face_recognition.load_image_file(camera_frame_path)
    descriptor = face_recognition.face_encodings(image)[0].tolist()

    for face_id in allowed_face_ids:
        result = client.verify(descriptor=descriptor, face_id=face_id)
        if result.verified:
            print(f"Access granted (confidence: {result.confidence:.0%})")
            return True

    print("Access denied")
    return False
```

---

### Delete a User's Data (GDPR)

```python
def delete_user(user_id: str):
    result = client.purge(collection_id=user_id)
    print(f"Deleted {result.deleted_count} face records for user {user_id}")
```

---

## Pricing

| Tier | Requests/day | Price |
|---|---|---|
| Free | 100 | $0 forever |
| Pro | 10,000 | $29/month |
| Enterprise | Unlimited | Contact us |

Get started free: https://face-recognition-api-om7k.onrender.com/portal/

---

## Support

- **Docs:** https://face-recognition-api-om7k.onrender.com/docs/
- **Issues:** https://github.com/zeinvob/face-recognition-api/issues
- **Email:** minhajuli958@gmail.com
