# Username Generator & Checker (`protocol/username-generator-checker`) Actor

Generate brand-safe, tone-specific usernames with an LLM pipeline, then verify availability across GitHub, X, Twitch, and YouTube in one run.

- **URL**: https://apify.com/protocol/username-generator-checker.md
- **Developed by:** [Protocol](https://apify.com/protocol) (community)
- **Categories:** AI, Social media, Developer tools
- **Stats:** 2 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 ai username generator & checkers

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## AI Username Generator

Generate brand-safe, tone-specific username ideas with an LLM, then verify availability across GitHub, X, Instagram, Threads, TikTok, Twitch, and YouTube in a single run. You get a ranked shortlist with a per-platform `available` / `taken` / `unknown` verdict, confidence scores, and naming-quality signals — so you can pick a handle that fits your brand and is actually free where you need it.

Built for founders naming a startup, creators picking a public handle, and developers who want one consistent identity across platforms — without checking half a dozen sites by hand.

- **Generate + verify + rank in one run** — more than an LLM prompt, more than an availability checker.
- **Fast:** ≈8s for a typical 10 names × 2 platforms run (large 20 × 4 runs take ~17s).
- **Low cost:** ~$0.08 for a typical 10 names × 4 platforms run ($2.00 / 1,000 checks); a max 20 × 7 run costs ~$0.28.
- **No logins, no browser automation:** unauthenticated, point-in-time checks with confidence scores; ambiguous responses are reported honestly as `unknown`.
- **Agent-ready:** flat JSON dataset plus a grouped summary record — wires into MCP, Make, Zapier, and n8n.

---

### Quick start

1. [Create a free Apify account](https://console.apify.com/sign-up) (or sign in).
2. Open this Actor's page and go to the **Input** tab.
3. Add at least one `keywords` entry — everything else has a sensible default.
4. Click **Start** and wait a few seconds.
5. Open the **Storage** tab to view the ranked dataset, or read the `OUTPUT` key-value record for the single best pick.

---

### What this Actor does

1. **Generates** up to 20 candidates from your keywords, brand/personal name, niche, and tone using a cost-optimized LLM pipeline (DeepSeek primary, GPT-4o-mini fallback via OpenRouter).
2. **Filters** them deterministically — profanity, homoglyph/leetspeak lookalikes (`d3vil` is caught by banning `devil`), your custom banned words, and each platform's handle rules (length, allowed characters).
3. **Verifies** availability with unauthenticated HTTP checks over datacenter proxies — no browser automation, no logins, no credentials sent to any platform.
4. **Ranks** the survivors by naming quality, adjusted for real availability, and writes a flat dataset (one row per username × platform) plus a grouped `OUTPUT` summary that names the single best pick.

---

### Why use it

- **Get names, not just availability.** The LLM proposes on-brand candidates in the tone you choose — you are not feeding it your own list.
- **Pick a name that is free _everywhere_.** The grouped summary names the single candidate open across all the platforms you care about.
- **Stop checking handles by hand.** One run replaces opening seven sites in seven tabs for every idea.

---

### Supported platforms

| Platform        | What is checked                            | Reliability                                                                            |
| --------------- | ------------------------------------------- | --------------------------------------------------------------------------------------- |
| **GitHub**      | Profile 404 vs 200                          | High — most reliable check                                                              |
| **Threads**     | WebFinger discovery lookup                  | High                                                                                     |
| **Instagram**   | Profile page `<title>` signal               | Medium-high                                                                              |
| **Twitch**      | Channel existence                           | High                                                                                     |
| **YouTube**     | Handle / channel exists                     | High                                                                                     |
| **TikTok**      | Embedded profile JSON                       | Reliable for `taken`; never reports `available` (see below)                             |
| **X** (formerly Twitter) | Profile reachability               | Variable — 30–50% return `unknown` due to login walls; **optional, not in the default platform list** |

> **Two platforms behave specially, by design.** **X** often returns `unknown` (login wall — opt in via `platforms` and expect a higher `unknown` rate), and **TikTok** never returns `available` (anti-enumeration — it only emits `taken` or `unknown`). Both are deliberate honesty measures, not bugs; the ranking step downweights any `unknown`. Full explanations in [Known limitations](#known-limitations) and the [FAQ](#faq).

---

### Input

Only `keywords` is required. Everything else has a sensible default, so the smallest valid input is a single keyword.

| Field          | Type       | Default        | Description                                                                                                       |
| -------------- | ---------- | -------------- | ----------------------------------------------------------------------------------------------------------------- |
| `keywords`     | `string[]` | — (required)   | Seed words/themes to build names around, e.g. `["vault", "alpha"]`.                                               |
| `brandName`    | `string`   | —              | Existing brand whose fragments to weave in (e.g. `BrandCraft` → `brandcrafthq`).                                  |
| `personalName` | `string`   | —              | Personal name for individual/creator handles (e.g. `Ray` → `raybuilds`).                                          |
| `niche`        | `string`   | —              | Industry/niche (e.g. `fintech`, `indie gaming`) — drives naming conventions.                                      |
| `tone`         | enum       | `Professional` | One of `Professional`, `Minimal`, `Gamer`, `Creative`.                                                            |
| `platforms`    | `string[]` | `["github"]`   | Any of `github`, `threads`, `instagram`, `twitch`, `youtube`, `tiktok`, `x` (lowercase; use `x`, not `twitter`). `x` is optional and not selected by default — see [Supported platforms](#supported-platforms). |
| `maxLength`    | `integer`  | `20`           | Max handle length (hard cap 30). Set ≤ 15 when targeting X; ≤ 24 when targeting TikTok.                           |
| `bannedWords`  | `string[]` | `[]`           | Words to exclude — matched after de-leeting, so `d3vil` is caught by banning `devil`.                             |
| `count`        | `integer`  | `10`           | Candidates to generate (hard cap **20** per run).                                                                 |
| `inputMode`    | enum       | `full`         | `full` = generate **and** verify (billable). `generate_only` = generate without checks (free, for brainstorming). |

#### Example input — founder / startup

```json
{
    "keywords": ["launch", "build"],
    "niche": "developer tools",
    "tone": "Professional",
    "platforms": ["github", "x"],
    "count": 10
}
````

#### Example input — creator / gamer

```json
{
    "keywords": ["stream", "play"],
    "tone": "Gamer",
    "platforms": ["twitch", "youtube"],
    "count": 10
}
```

***

### Output

The Actor writes to two places:

1. **Dataset** — one row per **candidate × selected platform** (10 names × 2 platforms = up to 20 rows). Up to 20 candidates per run; the row count is *not* capped at 20.
2. **Key-value store `OUTPUT`** — a grouped per-name summary with the top pick and a machine-readable `chargeable` / `degraded` verdict. This is the record AI agents should read first.

#### Dataset fields

| Field                                       | Meaning                                                                                      |
| ------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `username`                                  | The generated candidate.                                                                     |
| `normalizedUsername`                        | Lowercased/stripped form used for checks and cache keys.                                     |
| `platform`                                  | `github` / `threads` / `instagram` / `twitch` / `youtube` / `tiktok` / `x`.                   |
| `status`                                    | `available`, `taken`, `invalid`, `unknown`, `rate_limited`, or `unverified` (generate-only). |
| `confidence`                                | 0–100 *verification* certainty (cache hit 100, HTTP-verified 90) — not naming quality.       |
| `reasonCode`                                | Stable machine-readable code to branch on: `PROFILE_NOT_FOUND`, `PROFILE_EXISTS`, `LOGIN_WALL`, `REQUEST_TIMEOUT`, `RATE_LIMITED`, `AMBIGUOUS_SIGNAL`, `CHECK_FAILED`, `RULES_INVALID`, `UNVERIFIED_GENERATE_ONLY`. |
| `reason`                                    | Human-readable, platform-aware explanation of the verdict.                                   |
| `debug`                                     | Nested low-level detail `{ verificationMethod, httpStatus?, timedOut? }` (not a CSV column).  |
| `verification_method`                       | *Deprecated* (kept for back-compat; use `reasonCode` / `debug.verificationMethod`). How the status was found, e.g. `http_404`, `http_200`, `cache_hit`, `none`. |
| `rankingScore`                              | 0–100 naming quality, adjusted for availability.                                             |
| `rankingRationale`                          | Short human-readable reason for the rank.                                                    |
| `statusReason`                              | *Deprecated* (kept for back-compat; use `reason`). Why a status was assigned (e.g. login wall → `unknown`). |
| `brandFit` / `readability` / `memorability` | 0–100 LLM naming scores.                                                                     |
| `isHighConfidence`                          | `true` when `confidence ≥ 80` and status is `available`/`taken`.                             |
| `platformRulesValid`                        | Passed that platform's handle rules.                                                         |
| `runId`                                     | Apify run ID for traceability.                                                               |

#### Example output row

```json
{
    "username": "buildvaulthq",
    "normalizedUsername": "buildvaulthq",
    "platform": "github",
    "status": "available",
    "confidence": 90,
    "reasonCode": "PROFILE_NOT_FOUND",
    "reason": "GitHub profile not found — the handle appears available.",
    "debug": { "verificationMethod": "http_404", "httpStatus": 404 },
    "verification_method": "http_404",
    "rankingScore": 82,
    "rankingRationale": "Strong brand fit, available on GitHub, easy to read",
    "statusReason": "Verified via http_404",
    "brandFit": 88,
    "readability": 80,
    "memorability": 78,
    "isHighConfidence": true,
    "platformRulesValid": true,
    "runId": "abc123"
}
```

***

### How availability checking works

- **Unauthenticated HTTP only.** The Actor requests public profile/channel URLs and maps the HTTP response to a status. No credentials are ever sent to GitHub, Threads, Instagram, Twitch, YouTube, TikTok, or X, and raw external HTML is never stored in your dataset or logs.
- **Datacenter proxies.** Checks run through Apify datacenter proxies with session rotation — no residential proxies, keeping runs cheap and fast.
- **No browser automation.** Ambiguous responses degrade to `unknown` rather than being force-resolved with a headless browser. This is why X (which gates profiles behind a login wall) frequently returns `unknown`.
- **10-second pipeline deadline.** A global deadline flips any still-pending checks to `unknown` and proceeds to scoring, so a slow platform can never blow the run-time budget.
- **Caching.** Confirmed-`taken` handles are cached in a key-value store, so repeat runs over trending names skip redundant proxy calls.

***

### Run modes & pricing

This Actor uses **pay-per-event** pricing at **$2.00 / 1,000 checks**. You choose the work per run with `inputMode`:

| `inputMode`      | Work performed                                              | Billing                                                        |
| ---------------- | ----------------------------------------------------------- | -------------------------------------------------------------- |
| `full` (default) | Generate + Tier-1 filter + availability verification        | Charged per verified result at **$2.00 / 1,000 checks**        |
| `generate_only`  | Generate + Tier-1 filter only (rows come back `unverified`) | **Free — no charge.** Ideal for brainstorming before committing |

Charged per verified result ($0.002/row) when the run succeeds and at least one platform was verified. A typical run (10 names × 4 platforms) costs ~$0.08. Runs that fail before producing output, or that come back **degraded** (zero platforms verifiable — all `unknown`/`rate_limited`), are **not** charged. `generate_only` runs are always free. There is no separate user tier and no reduced name cap — every mode supports up to 20 names.

***

### Usage via API

Run the Actor programmatically with the Apify API client. Supply your [Apify API token](https://console.apify.com/account/integrations).

#### Python

```bash
pip install apify-client
```

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_API_TOKEN>")

run_input = {
    "keywords": ["launch", "build"],
    "tone": "Professional",
    "platforms": ["github", "x"],
    "count": 10,
    "inputMode": "full",
}

## Start the run and wait for it to finish
run = client.actor("protocol/username-generator-checker").call(run_input=run_input)

## Read the per-row dataset
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["username"], item["platform"], item["status"], item["rankingScore"])

## Read the grouped summary (best pick + chargeable/degraded verdict)
summary = client.key_value_store(run["defaultKeyValueStoreId"]).get_record("OUTPUT")
print(summary["value"])
```

#### JavaScript / TypeScript

```bash
npm install apify-client
```

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_API_TOKEN>' });

const run = await client.actor('protocol/username-generator-checker').call({
    keywords: ['stream', 'play'],
    tone: 'Gamer',
    platforms: ['twitch', 'youtube'],
    count: 10,
    inputMode: 'full',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.table(items.map((i) => ({ name: i.username, platform: i.platform, status: i.status })));
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/protocol~username-generator-checker/run-sync-get-dataset-items?token=<YOUR_APIFY_API_TOKEN>" \
  -H 'Content-Type: application/json' \
  -d '{ "keywords": ["vault", "alpha"], "platforms": ["github"], "count": 5 }'
```

***

### Use with AI agents (MCP)

The Actor is built to be driven by AI agents. The `INPUT_SCHEMA.json` exposes typed fields with enum dropdowns and defaults, so an MCP client (e.g. Claude Desktop via the [Apify MCP Server](https://docs.apify.com/platform/integrations/mcp)) can call it with minimal prompting. Agents should read the grouped `OUTPUT` record — it carries the single best candidate and a machine-readable `chargeable`/`degraded` verdict, so the agent does not have to reduce the flat dataset itself. Invalid input returns a structured error code rather than a silent failure (see [Run-level error codes](#run-level-error-codes)).

Connect via the [Apify MCP Server](https://docs.apify.com/platform/integrations/mcp) with a client config like:

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=protocol/username-generator-checker",
                "--header",
                "Authorization: Bearer <YOUR_APIFY_API_TOKEN>"
            ]
        }
    }
}
```

***

### Run-level error codes

Most failures are reported per-row via `reasonCode` (see [Dataset fields](#dataset-fields)). A small set of run-level codes cover failures before any row can be produced — for example, invalid top-level input. The Actor always returns one of these rather than failing silently:

| Code                          | Meaning                                                                  |
| ------------------------------ | ------------------------------------------------------------------------- |
| `ERR_INVALID_INPUT`            | Input failed schema validation (e.g. missing `keywords`, `count` > 20).   |
| `ERR_MISSING_API_KEY`          | The Actor's LLM provider credential is not configured (operator-side).   |
| `ERR_LLM_PIPELINE_FAILED`      | Both the primary and fallback LLM engines failed or timed out.           |
| `ERR_ALL_PLATFORMS_RATE_LIMITED` | Every selected platform returned a rate-limit response — run degraded. |
| `ERR_OUTPUT_SCHEMA_VIOLATION`  | An internal output failed schema validation before being written.        |

***

### FAQ

**Why does X return `unknown` so often, and why isn't it checked by default?**
X gates many profiles behind a login wall. Rather than scrape past it with a headless browser, the Actor honestly reports `unknown` (30–50% of X checks). Because of this, X is **not** included in the default `platforms` list — add `x` explicitly when you need it, and treat `unknown` as "could not confirm," not "taken."

**Why does TikTok never return `available`?**
TikTok intentionally returns the same status code whether a handle was never registered or belongs to a banned account — there is no unauthenticated way to distinguish the two. To avoid false positives, this Actor's TikTok adapter only ever reports `taken` or `unknown`, never `available`. Expect most genuinely free TikTok handles to come back `unknown`; this is a deliberate platform anti-enumeration measure, not a bug, and the ranking step already downweights candidates with any `unknown` result.

**Can a name marked `available` be taken later?**
Yes. Results are a point-in-time snapshot. A handle can be claimed by someone else before you register it, and `available` may reflect a recently-released handle still in a platform grace window. Re-confirm before relying on it.

**Will the same input always return the same names?**
No. LLM generation is non-deterministic — the same input can produce different candidates across runs. The availability verdicts for a given handle are deterministic.

**How many names can I generate per run?**
Up to 20, in any mode. Larger batches require multiple runs.

**How do I brainstorm cheaply before committing?**
Run with `inputMode: "generate_only"`. It generates and filters names without availability checks (status `unverified`) and is free. Switch to `full` when you want verified results.

**Is my input data sent anywhere?**
Keyword and name inputs are sent to a third-party LLM provider (via OpenRouter) to generate candidates. See Data & privacy below.

**Can I connect this to Zapier, Make, n8n, or a webhook?**
Yes. The Actor works with any tool that can call the Apify API — including Make, Zapier, and n8n integrations and [Apify webhooks](https://docs.apify.com/integrations/webhooks) (e.g. notify on run success). See [Usage via API](#usage-via-api) for direct API/SDK calls, or [Use with AI agents (MCP)](#use-with-ai-agents-mcp) for MCP clients.

***

### Known limitations

| Limitation                   | What to expect                                                                                |
| ---------------------------- | --------------------------------------------------------------------------------------------- |
| X `unknown` rate, optional | 30–50% of X checks return `unknown` due to login walls — expected, not a bug. X is **not in the default platform list**; opt in explicitly via `platforms`. |
| TikTok never reports `available` | TikTok collapses "never registered" and "banned" into one ambiguous code — the adapter only ever emits `taken` or `unknown`, so expect a near-100% `unknown` rate on free handles. Not a bug. |
| No browser automation        | Ambiguous responses degrade to `unknown` rather than being force-resolved.                    |
| Point-in-time availability   | A handle marked `available` may be claimed before you register it. Re-confirm before relying. |
| Grace-window handles         | `available` may reflect a recently-released handle still in a platform grace window.          |
| Non-deterministic generation | The same input may produce different candidates on different runs.                            |
| Max 20 names per run         | Hard cap; larger batches require multiple runs.                                               |

***

### Data & privacy

- **Keyword and name inputs are sent to a third-party LLM provider** (via OpenRouter) to generate candidates. **Do not include personal data** you do not want shared with an LLM provider. `personalName`-derived outputs persist in the run's dataset and exports.
- **Availability checks are unauthenticated** — no credentials are sent to any platform, and no raw external HTML is stored in your dataset or logs.
- **Data retention:** datasets are retained per your Apify account settings. **Delete the run to erase its outputs.**

***

### Support

Found a bug or want a platform added? Open an issue on the Actor's **Issues** tab in the Apify Console. Feature requests (preset naming modes, availability monitoring/watchlist, bulk validation) are tracked on the roadmap.

# Actor input Schema

## `keywords` (type: `array`):

Required. Seed words or themes the usernames should be built around (e.g. \["vault", "alpha"]). Provide at least one. Each keyword is woven into generated candidates where natural. Do not include personal data you don't want sent to an LLM provider.

## `brandName` (type: `string`):

Optional. An existing brand name whose fragments should be incorporated into candidates where natural (e.g. "BrandCraft" may yield "brandcrafthq"). Leave empty if naming from scratch.

## `personalName` (type: `string`):

Optional. A personal name to incorporate for individual/creator handles (e.g. "Ray" may yield "raybuilds"). Note: name-derived outputs persist in the run's dataset and exports.

## `niche` (type: `string`):

Optional. Industry, niche, or category the username is for (e.g. "fintech", "indie gaming", "food blogging"). Drives industry naming conventions in generation — fintech favors short/trust-evoking names, gaming favors thematic/clan-style, creator brands favor memorable/personal.

## `tone` (type: `string`):

Optional. Naming tone applied to all candidates. Must be exactly one of: "Professional" (corporate, trust-evoking), "Minimal" (short, clean, lowercase feel), "Gamer" (thematic, edgy, gaming-culture style), "Creative" (playful, unexpected wordplay). Defaults to "Professional".

## `platforms` (type: `array`):

Optional. Platforms to verify availability on. Allowed values: "github", "x", "twitch", "youtube", "threads", "instagram", "tiktok" (lowercase, exactly as listed — e.g. use "x", not "twitter"). Defaults to \["github"], the most reliable check. "x" is optional and not selected by default — it frequently returns status "unknown" due to login walls, so select it deliberately. "tiktok" is also optional and not selected by default — TikTok's anti-enumeration design means it can confirm a handle is taken but cannot confirm one is available, so expect a high "unknown" rate for genuinely free names; this is a platform limitation, not a bug. Each candidate is checked on every selected platform (one output row per candidate-platform pair).

## `maxLength` (type: `integer`):

Optional. Maximum character length for generated usernames. Hard cap 30 (YouTube/Instagram/Threads' limit — the longest supported platforms). Defaults to 20, a safe cross-platform value; note X allows only 15 characters and TikTok only 24, so set the limit accordingly when targeting those platforms. Values above 30 are rejected.

## `bannedWords` (type: `array`):

Optional. Words that must never appear in generated usernames (e.g. competitor names, sensitive terms). Matching is applied after normalization and de-leeting, so "d3vil" is caught by banning "devil". Defaults to \[] (no extra filtering beyond the built-in profanity list).

## `count` (type: `integer`):

Optional. How many username candidates to generate. Hard cap 20 per run — this bounds LLM cost and keeps availability checks within the run-time budget; a higher count is rejected before generation. Defaults to 10.

## `inputMode` (type: `string`):

Optional. "full" (default) generates candidates AND verifies availability on the selected platforms — the complete, billable pipeline. "generate\_only" generates and filters candidates but skips all availability checks: rows come back with status "unverified", it is much cheaper and non-billable, ideal for fast iterative brainstorming before committing to a full verification run. No other value is accepted in V1 ("validate" is planned but not yet available).

## Actor input object example

```json
{
  "keywords": [
    "brand",
    "craft"
  ],
  "tone": "Professional",
  "platforms": [
    "github"
  ],
  "maxLength": 20,
  "bannedWords": [],
  "count": 10,
  "inputMode": "full"
}
```

# Actor output Schema

## `shortlist` (type: `string`):

Ranked username candidates as dataset rows — one row per candidate x selected platform — each carrying availability status, a machine-readable reasonCode, confidence, and ranking scores. In full mode rows are availability-verified; in generate\_only mode rows are status "unverified". Field-level detail is in dataset\_schema.json.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "brand",
        "craft"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("protocol/username-generator-checker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "keywords": [
        "brand",
        "craft",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("protocol/username-generator-checker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "brand",
    "craft"
  ]
}' |
apify call protocol/username-generator-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=protocol/username-generator-checker",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Username Generator & Checker",
        "description": "Generate brand-safe, tone-specific usernames with an LLM pipeline, then verify availability across GitHub, X, Twitch, and YouTube in one run.",
        "version": "0.1",
        "x-build-id": "Isfprz4JEWVu6fjRo"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/protocol~username-generator-checker/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-protocol-username-generator-checker",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/protocol~username-generator-checker/runs": {
            "post": {
                "operationId": "runs-sync-protocol-username-generator-checker",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/protocol~username-generator-checker/run-sync": {
            "post": {
                "operationId": "run-sync-protocol-username-generator-checker",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "keywords"
                ],
                "properties": {
                    "keywords": {
                        "title": "Keywords",
                        "minItems": 1,
                        "type": "array",
                        "description": "Required. Seed words or themes the usernames should be built around (e.g. [\"vault\", \"alpha\"]). Provide at least one. Each keyword is woven into generated candidates where natural. Do not include personal data you don't want sent to an LLM provider.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "brandName": {
                        "title": "Brand name",
                        "type": "string",
                        "description": "Optional. An existing brand name whose fragments should be incorporated into candidates where natural (e.g. \"BrandCraft\" may yield \"brandcrafthq\"). Leave empty if naming from scratch."
                    },
                    "personalName": {
                        "title": "Personal name",
                        "type": "string",
                        "description": "Optional. A personal name to incorporate for individual/creator handles (e.g. \"Ray\" may yield \"raybuilds\"). Note: name-derived outputs persist in the run's dataset and exports."
                    },
                    "niche": {
                        "title": "Industry / niche",
                        "type": "string",
                        "description": "Optional. Industry, niche, or category the username is for (e.g. \"fintech\", \"indie gaming\", \"food blogging\"). Drives industry naming conventions in generation — fintech favors short/trust-evoking names, gaming favors thematic/clan-style, creator brands favor memorable/personal."
                    },
                    "tone": {
                        "title": "Tone / style",
                        "enum": [
                            "Professional",
                            "Minimal",
                            "Gamer",
                            "Creative"
                        ],
                        "type": "string",
                        "description": "Optional. Naming tone applied to all candidates. Must be exactly one of: \"Professional\" (corporate, trust-evoking), \"Minimal\" (short, clean, lowercase feel), \"Gamer\" (thematic, edgy, gaming-culture style), \"Creative\" (playful, unexpected wordplay). Defaults to \"Professional\".",
                        "default": "Professional"
                    },
                    "platforms": {
                        "title": "Target platforms",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Optional. Platforms to verify availability on. Allowed values: \"github\", \"x\", \"twitch\", \"youtube\", \"threads\", \"instagram\", \"tiktok\" (lowercase, exactly as listed — e.g. use \"x\", not \"twitter\"). Defaults to [\"github\"], the most reliable check. \"x\" is optional and not selected by default — it frequently returns status \"unknown\" due to login walls, so select it deliberately. \"tiktok\" is also optional and not selected by default — TikTok's anti-enumeration design means it can confirm a handle is taken but cannot confirm one is available, so expect a high \"unknown\" rate for genuinely free names; this is a platform limitation, not a bug. Each candidate is checked on every selected platform (one output row per candidate-platform pair).",
                        "items": {
                            "type": "string",
                            "enum": [
                                "github",
                                "x",
                                "twitch",
                                "youtube",
                                "threads",
                                "instagram",
                                "tiktok"
                            ],
                            "enumTitles": [
                                "GitHub",
                                "X (Twitter)",
                                "Twitch",
                                "YouTube",
                                "Threads",
                                "Instagram",
                                "TikTok"
                            ]
                        },
                        "default": [
                            "github"
                        ]
                    },
                    "maxLength": {
                        "title": "Maximum username length",
                        "minimum": 1,
                        "maximum": 30,
                        "type": "integer",
                        "description": "Optional. Maximum character length for generated usernames. Hard cap 30 (YouTube/Instagram/Threads' limit — the longest supported platforms). Defaults to 20, a safe cross-platform value; note X allows only 15 characters and TikTok only 24, so set the limit accordingly when targeting those platforms. Values above 30 are rejected.",
                        "default": 20
                    },
                    "bannedWords": {
                        "title": "Banned words",
                        "type": "array",
                        "description": "Optional. Words that must never appear in generated usernames (e.g. competitor names, sensitive terms). Matching is applied after normalization and de-leeting, so \"d3vil\" is caught by banning \"devil\". Defaults to [] (no extra filtering beyond the built-in profanity list).",
                        "items": {
                            "type": "string"
                        },
                        "default": []
                    },
                    "count": {
                        "title": "Number of usernames",
                        "minimum": 1,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Optional. How many username candidates to generate. Hard cap 20 per run — this bounds LLM cost and keeps availability checks within the run-time budget; a higher count is rejected before generation. Defaults to 10.",
                        "default": 10
                    },
                    "inputMode": {
                        "title": "Run mode",
                        "enum": [
                            "full",
                            "generate_only"
                        ],
                        "type": "string",
                        "description": "Optional. \"full\" (default) generates candidates AND verifies availability on the selected platforms — the complete, billable pipeline. \"generate_only\" generates and filters candidates but skips all availability checks: rows come back with status \"unverified\", it is much cheaper and non-billable, ideal for fast iterative brainstorming before committing to a full verification run. No other value is accepted in V1 (\"validate\" is planned but not yet available).",
                        "default": "full"
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
