# Username Scanning Module Function Signature in user-scanner

> Discover the username scanning module function signature in user-scanner. Learn how to implement a validate_<sitename> function to check username availability and status.

- Repository: [Kaif/user-scanner](https://github.com/kaifcodec/user-scanner)
- Tags: api-reference
- Published: 2026-09-02

---

**A username scanning module must expose exactly one validator function named `validate_<sitename>` that accepts a username string and returns a `Result` object indicating availability, taken status with metadata, or an error.**

The **kaifcodec/user-scanner** repository enforces a strict contract for all username scanning modules. This guide walks through the expected function signature, return type requirements, and implementation patterns derived directly from the source code.

## The Required Function Signature

Every username scanning module must implement a single validator function following this exact pattern:

```python
def validate_<sitename>(user: str) -> Result:
    ...

```

The naming convention is mandatory: prefix `validate_` followed by the lowercase site identifier. For example, `validate_github`, `validate_twitter`, or `validate_linkedin`.

### Parameter and Return Specifications

| Element | Specification |
|---------|-------------|
| Parameter `user` | Raw username string supplied by the caller |
| Return type | Instance of `user_scanner.core.result.Result` |
| Exception handling | **No exceptions allowed** — all errors must return via `Result.error()` |

## The Result Object: Three Factory Methods

The `Result` class in [`user_scanner/core/result.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/core/result.py) provides three static factory methods for constructing return values:

1. **`Result.available()`** — The username does not exist on the platform
2. **`Result.taken(extra={...}, media={...})`** — The username exists; optional `extra` dict holds structured metadata (fullname, location, bio), and `media` dict contains image URLs
3. **`Result.error("message")`** — Any unexpected condition: network failures, rate limits, or parsing errors

### Error Handling Rule

Validators **must not raise exceptions**. The following pattern is forbidden:

```python

# INCORRECT — do not use

raise ValueError("unexpected response")
return Result.error("unexpected response")  # CORRECT

```

## Implementation Patterns from core/orchestrator.py

Most modules leverage helper utilities to maintain concise, DRY code. The two primary helpers are defined in [`user_scanner/core/orchestrator.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/core/orchestrator.py):

- **`generic_validate`** — Standard HTTP validation with configurable request parameters
- **`impersonate_validate`** — For bot-protected sites requiring browser impersonation

### Minimal Validator Example (GitHub)

```python
from user_scanner.core.orchestrator import generic_validate
from user_scanner.core.result import Result
import re

def validate_github(user: str) -> Result:
    """Check whether a GitHub username exists."""
    url = f"https://github.com/{user}"
    show_url = f"https://github.com/{user}"

    def process(resp):
        # Username available: 404 or explicit "Not Found"

        if resp.status_code == 404 or "Not Found" in resp.text:
            return Result.available()

        # Username taken: profile header present

        if resp.status_code == 200 and 'class="vcard-fullname"' in resp.text:
            extra = {}
            m = re.search(r'class="vcard-fullname">([^<]+)</', resp.text)
            if m:
                extra["fullname"] = m.group(1).strip()
            return Result.taken(
                extra=extra,
                media={"avatar": f"https://github.com/{user}.png"}
            )

        # Unexpected state

        return Result.error(f"Unexpected status {resp.status_code}")

    return generic_validate(url, process, show_url=show_url, follow_redirects=True)

```

### Bot-Protected Site Pattern (Snapchat)

For platforms protected by Cloudflare or similar services, use `impersonate_validate` from [`user_scanner/core/impersonate.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/core/impersonate.py):

```python
from user_scanner.core.impersonate import impersonate_validate
from user_scanner.core.result import Result

def validate_snapchat(user: str) -> Result:
    """Validate Snapchat usernames behind Cloudflare protection."""
    url = f"https://www.snapchat.com/add/{user}"

    def process(resp):
        if resp.status_code == 404 and "Sorry, we couldn't find" in resp.text:
            return Result.available()
        if resp.status_code == 200 and "profile-card" in resp.text:
            return Result.taken()
        return Result.error(f"Unexpected status {resp.status_code}")

    return impersonate_validate(
        url,
        process,
        warmup_url="https://www.snapchat.com/",
        impersonate="chrome",
        show_url=url,
    )

```

## Critical Implementation Requirements

According to the contributing guidelines in [`CONTRIBUTING.md`](https://github.com/kaifcodec/user-scanner/blob/main/CONTRIBUTING.md), every username scanning module must satisfy these five rules:

1. **Exact naming** — Function name must be `validate_<sitename>` with matching lowercase filename
2. **Explicit verification** — Never rely solely on HTTP 200; validators must identify unique markers confirming both taken and available states
3. **Metadata extraction** — Include rich structured data in `Result.taken()` when profiles are found
4. **Error containment** — Return `Result.error()` for all failure paths; exceptions break the orchestration
5. **Safe URL construction** — Use `params` arguments rather than f-strings for user-controlled data in request parameters

## Key Source Files

| Purpose | Path |
|---------|------|
| Contribution guidelines — signature definition | [`CONTRIBUTING.md`](https://github.com/kaifcodec/user-scanner/blob/main/CONTRIBUTING.md) |
| `Result` class implementation | [`user_scanner/core/result.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/core/result.py) |
| Validation helpers (`generic_validate`) | [`user_scanner/core/orchestrator.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/core/orchestrator.py) |
| Browser impersonation helper | [`user_scanner/core/impersonate.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/core/impersonate.py) |
| Reference implementation (Reddit) | [`user_scanner/user_scan/social/reddit.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/user_scan/social/reddit.py) |
| Async pattern reference (Mastodon email) | [`user_scanner/email_scan/social/mastodon.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/email_scan/social/mastodon.py) |

## Summary

- **Function signature**: `def validate_<sitename>(user: str) -> Result`
- **Return exclusively** via `Result.available()`, `Result.taken()`, or `Result.error()`
- **Never raise exceptions** — express all errors through `Result.error()`
- **Use helpers** from [`core/orchestrator.py`](https://github.com/kaifcodec/user-scanner/blob/main/core/orchestrator.py) and [`core/impersonate.py`](https://github.com/kaifcodec/user-scanner/blob/main/core/impersonate.py) for clean implementations
- **Follow naming conventions** exactly: filename matches `validate_` prefix target

## Frequently Asked Questions

### What happens if a validator raises an exception instead of returning Result.error()?

The orchestration system in [`user_scanner/core/orchestrator.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/core/orchestrator.py) expects all validators to return `Result` objects. An unhandled exception will propagate uncaught, potentially crashing the scanning batch or leaving the caller without proper status information. Always wrap error conditions in `Result.error()`.

### Can I use async/await in a username scanning module?

Yes. The repository includes async patterns as demonstrated in [`user_scanner/email_scan/social/mastodon.py`](https://github.com/kaifcodec/user-scanner/blob/main/user_scanner/email_scan/social/mastodon.py). For async validators, use `async def` and ensure the orchestrator calling the function handles await properly. The same `Result` return type contract applies.

### How do I choose between generic_validate and impersonate_validate?

Use **`generic_validate`** for standard sites without aggressive bot protection. Use **`impersonate_validate`** when the target site employs Cloudflare, DataDome, or similar anti-bot measures that reject programmatic HTTP clients. The impersonate helper uses browser fingerprinting to bypass these protections.

### What metadata should I include in Result.taken()?

Include any publicly visible profile information that enriches the output: `fullname`, `location`, `bio`, `joined_date` in `extra`, and avatar/profile image URLs in `media`. The exact fields depend on what the target platform exposes in its HTML or API response.