# GenderAPI.io V2: Username and nickname lookup

> Send type: username and value to GenderAPI.io V2. Use forceToGenderize for nickname inference and understand unknown results and credits.

Canonical HTML: https://www.genderapi.io/docs/v2/gender-from-username

Last reviewed: 2026-09-27

## Before sending the request

Send GenderAPI.io V2 prediction requests to POST https://api.genderapi.io/api/v2/gender with type and value in the JSON body. Use your existing GenderAPI.io key in Authorization: Bearer YOUR_API_KEY and Content-Type: application/json. Read the result from data and the access and billing information from meta. Every request is processed independently.

Choose your language in the code example. Set GENDERAPI_API_KEY in the process environment before running it. Missing or unrecognized keys can use the shared IP trial, so confirm meta.access.mode is api_key when integrating an account.

- [Authentication and trial access](https://www.genderapi.io/docs/v2/authentication)
- [All request parameters](https://www.genderapi.io/docs/v2/request-parameters)

## Gender from a username or nickname

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `type` | `string` | Yes | `—` | Input category: name, email or username. |
| `value` | `string` | Yes | `—` | Required. 1–254 characters; nonblank, without control characters. Email values must be valid email syntax. |
| `country` | `string` | No | `—` | Optional uppercase ISO 3166-1 alpha-2 code, for example TR or US. Country-specific lookup can fall back to the global dataset. |
| `forceToGenderize` | `boolean` | No | `false` | Try the dataset first, then nickname-aware AI if unresolved. Supported for name, email and username. |
| `options` | `object` | No | `—` | Container for per-input AI options. |
| `options.ai_mode` | `string` | No | `fallback` | Allowed values: off, fallback, always. With forceToGenderize: true, omit this field or use fallback. |
| `id` | `string` | No | `—` | Optional 1–64 character identifier in POST JSON. Returned beside each batch result; unique within a batch. Accepted but not echoed for single predictions. Not supported in GET queries. |

Send type: username and the handle or nickname in value to POST https://api.genderapi.io/api/v2/gender. Set forceToGenderize to true when you want an attempt based on nickname semantics, such as prenses, even if no real name can be extracted. GenderAPI.io V2 still checks the dataset first. A nickname prediction may return a non-null data.gender with data.name: null.

Run these examples on your server. Set GENDERAPI_API_KEY in the process environment to your existing API key. Confirm meta.access.mode is api_key: an unrecognized key can fall back to the IP trial.

Every prediction request is a new billable operation, including retries. These examples do not automatically retry. Check billing status before sending another request.

HTTP 4xx and 5xx JSON responses preserve the error body and return a nonzero exit status. Check code, action and meta.usage.billing_status before retrying.

**cURL**

cURL 7.76+ in a POSIX shell. Run in your terminal.

```bash
curl --silent --show-error --fail-with-body --max-time 30 \
  --request POST 'https://api.genderapi.io/api/v2/gender' \
  --header "Authorization: Bearer ${GENDERAPI_API_KEY:?Set GENDERAPI_API_KEY}" \
  --header 'Content-Type: application/json' \
  --data '{
  "type": "username",
  "value": "prenses",
  "country": "TR",
  "forceToGenderize": true
}'
```

**JavaScript / Node.js**

Node.js 22+; built-in fetch. Save as example.mjs and run node example.mjs.

```javascript
// Server-side Node.js. Save as example.mjs.
const apiKey = process.env.GENDERAPI_API_KEY;
if (!apiKey) throw new Error("Set GENDERAPI_API_KEY");
const body = {
  "type": "username",
  "value": "prenses",
  "country": "TR",
  "forceToGenderize": true
};

const response = await fetch("https://api.genderapi.io/api/v2/gender", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify(body),
  signal: AbortSignal.timeout(30_000),
  redirect: "error",
});
const raw = await response.text();
if (!(response.headers.get("content-type") ?? "").includes("json")) {
  throw new Error(`HTTP ${response.status}: expected JSON; request ID ${response.headers.get("x-request-id")}`);
}
const result = JSON.parse(raw); // Also accepts application/problem+json.
if (!response.ok) {
  console.error(`HTTP ${response.status}`, result);
  process.exitCode = 1; // A retry is a new operation; inspect billing first.
} else {
  console.log(JSON.stringify(result, null, 2));
  // For batches, inspect every item: HTTP 200 may contain item errors.
}
```

**Python**

Python 3.10+; standard library. Save as example.py and run python3 example.py.

```python
import json
import os
import sys
import urllib.error
import urllib.request

api_key = os.environ.get("GENDERAPI_API_KEY")
if not api_key:
    raise RuntimeError("Set GENDERAPI_API_KEY")
body = json.loads("{\"type\":\"username\",\"value\":\"prenses\",\"country\":\"TR\",\"forceToGenderize\":true}")

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None

request = urllib.request.Request(
    "https://api.genderapi.io/api/v2/gender",
    method="POST",
    data=json.dumps(body).encode("utf-8"),
    headers={
        "Authorization": "Bearer " + api_key,
        "Content-Type": "application/json",
    },
)
opener = urllib.request.build_opener(NoRedirect())
try:
    response = opener.open(request, timeout=30)
except urllib.error.HTTPError as error:
    response = error  # Keep the Problem Details body on non-2xx responses.
with response:
    status = response.status
    raw = response.read().decode("utf-8")
    if "json" not in response.headers.get("Content-Type", ""):
        raise RuntimeError(f"HTTP {status}: expected a JSON response")
    result = json.loads(raw)
print(json.dumps(result, indent=2), file=sys.stderr if status >= 300 else sys.stdout)
if not 200 <= status < 300:
    sys.exit(1)  # Inspect code, action and billing before retrying.
# For batches, inspect every item even when HTTP status is 200.
```

**PHP**

PHP 8+ with the cURL extension. Save as example.php and run php example.php.

```php
<?php
$apiKey = getenv('GENDERAPI_API_KEY');
if (!$apiKey) {
    throw new RuntimeException('Set GENDERAPI_API_KEY');
}
$body = json_decode('{"type":"username","value":"prenses","country":"TR","forceToGenderize":true}', true, 512, JSON_THROW_ON_ERROR);

$ch = curl_init('https://api.genderapi.io/api/v2/gender');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode($body, JSON_THROW_ON_ERROR),
]);
$raw = curl_exec($ch);
if ($raw === false) {
    throw new RuntimeException(curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?: '';
curl_close($ch);
if (strpos($contentType, 'json') === false) {
    throw new RuntimeException("HTTP $status: expected a JSON response");
}
$result = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
$output = json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR) . PHP_EOL;
if ($status < 200 || $status >= 300) {
    fwrite(STDERR, $output); // Preserve the Problem Details body.
    exit(1);
}
echo $output;
// For batches, inspect every item; HTTP 200 can contain item errors.
```

**Java**

Java 17+; standard HTTP client. Save as GenderApiExample.java and run java GenderApiExample.java.

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;

public class GenderApiExample {
    private static String requiredEnv(String name) {
        String value = System.getenv(name);
        if (value == null || value.isBlank()) throw new IllegalStateException("Set " + name);
        return value;
    }

    public static void main(String[] args) throws Exception {
        String apiKey = requiredEnv("GENDERAPI_API_KEY");
        String body = "{\"type\":\"username\",\"value\":\"prenses\",\"country\":\"TR\",\"forceToGenderize\":true}";
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .followRedirects(HttpClient.Redirect.NEVER)
            .build();
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.genderapi.io/api/v2/gender"))
            .timeout(Duration.ofSeconds(30))
            .header("Authorization", "Bearer " + apiKey)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
            .build();
        HttpResponse<String> response = client.send(request,
            HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
        if (!response.headers().firstValue("content-type").orElse("").contains("json")) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": expected JSON");
        }
        // JSON text; parse with your application's JSON library when integrating.
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            System.err.println(response.body()); // Includes Problem Details.
            System.exit(1);
        }
        System.out.println(response.body());
        // For batches, inspect each item's data or error, including on HTTP 200.
    }
}
```

**C# / .NET**

.NET 8+ console application. Use as Program.cs in a console project, then run dotnet run.

```csharp
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;

string RequiredEnv(string name) =>
    !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(name))
        ? Environment.GetEnvironmentVariable(name)!
        : throw new InvalidOperationException($"Set {name}");

var apiKey = RequiredEnv("GENDERAPI_API_KEY");
using var handler = new HttpClientHandler { AllowAutoRedirect = false };
using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.genderapi.io/api/v2/gender");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
request.Content = new StringContent("{\"type\":\"username\",\"value\":\"prenses\",\"country\":\"TR\",\"forceToGenderize\":true}", Encoding.UTF8, "application/json");

using var response = await client.SendAsync(request);
var raw = await response.Content.ReadAsStringAsync();
if (!(response.Content.Headers.ContentType?.MediaType?.Contains("json") ?? false))
    throw new InvalidOperationException($"HTTP {(int)response.StatusCode}: expected JSON");
using var result = JsonDocument.Parse(raw);
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(result.RootElement); // Preserve Problem Details.
    Environment.ExitCode = 1;
}
else
{
    Console.WriteLine(result.RootElement);
    // For batches, inspect every item even on HTTP 200.
}
```

**Go**

Go 1.22+; standard library. Save as main.go and run go run main.go.

```go
package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"
    "time"
)

func requiredEnv(name string) string {
    value := os.Getenv(name)
    if value == "" { panic("Set " + name) }
    return value
}

func run() error {
    apiKey := requiredEnv("GENDERAPI_API_KEY")
    body := "{\"type\":\"username\",\"value\":\"prenses\",\"country\":\"TR\",\"forceToGenderize\":true}"
    request, err := http.NewRequest("POST", "https://api.genderapi.io/api/v2/gender", strings.NewReader(body))
    if err != nil { return err }
    request.Header.Set("Authorization", "Bearer " + apiKey)
    request.Header.Set("Content-Type", "application/json")
    client := &http.Client{
        Timeout: 30 * time.Second,
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            return http.ErrUseLastResponse
        },
    }
    response, err := client.Do(request)
    if err != nil { return err }
    defer response.Body.Close()
    raw, err := io.ReadAll(response.Body)
    if err != nil { return err }
    if !strings.Contains(response.Header.Get("Content-Type"), "json") || !json.Valid(raw) {
        return fmt.Errorf("HTTP %d: expected a JSON response", response.StatusCode)
    }
    if response.StatusCode < 200 || response.StatusCode >= 300 {
        return fmt.Errorf("HTTP %d: %s", response.StatusCode, raw)
    }
    fmt.Println(string(raw))
    // For batches, inspect every item even on HTTP 200.
    return nil
}

func main() {
    if err := run(); err != nil {
        fmt.Fprintln(os.Stderr, err) // Keep Problem Details for error handling.
        os.Exit(1)
    }
}
```

## Example response

This is a real response captured on 1 October 2026 with an API key; only the account balance is replaced. It is not a measure of accuracy, and a live request can return a different result. API-key requests report meta.access.mode: api_key and null trial-only quota fields; IP-trial requests report ip_trial. Read data for the inference and meta.usage for the operation's billing outcome.

```json
{
  "data": {
    "input": {
      "type": "username",
      "value": "prenses",
      "country": "TR",
      "forceToGenderize": true
    },
    "name": "prenses",
    "gender": "female",
    "country": "TR",
    "confidence": 0.9456521739130435,
    "confidence_kind": "observed_frequency",
    "sample_count": 184,
    "source": "dataset",
    "result_status": "identified",
    "reason": null,
    "country_source": "dataset",
    "match": {
      "name": "prenses",
      "method": "normalized",
      "scope": "country",
      "country": "TR"
    }
  },
  "meta": {
    "request_id": "a799faa4-bc1a-4537-90b3-2c1b60205499",
    "duration_ms": 15,
    "access": {
      "mode": "api_key",
      "reason": null
    },
    "usage": {
      "charged_credits": 1,
      "remaining_credits": 49999,
      "billing_status": "confirmed",
      "resets_at": null,
      "limit": null,
      "period_seconds": null
    }
  }
}
```

- [Response fields and confidence](https://www.genderapi.io/docs/v2/responses)

## Prediction response fields

| Field | Type | Nullable | Example | Description |
| --- | --- | --- | --- | --- |
| `data.input` | `object` | No | `{"type":"name","value":"James","country":"US"}` | Input type, value and country. forceToGenderize is included when true. |
| `data.name` | `string` | Yes | `"james"` | Matched or extracted given name; null when none is available. |
| `data.gender` | `string` | Yes | `"male"` | male, female or JSON null. An inference, not verified personal identity. |
| `data.result_status` | `string` | No | `"identified"` | identified when a gender is returned; unknown otherwise. |
| `data.reason` | `string` | Yes | `null` | Null for identified results. Unknown reasons: not_found, no_name_candidate, ambiguous or insufficient_evidence. |
| `data.confidence` | `number` | Yes | `0.95` | 0–1 score or null. Null gender has null confidence. |
| `data.confidence_kind` | `string` | Yes | `"observed_frequency"` | observed_frequency: dominant dataset count / total. model_reported: AI score, not a calibrated probability. Null when unavailable. |
| `data.sample_count` | `integer` | Yes | `1200` | Dataset sample size or null. AI does not invent a sample count. |
| `data.source` | `string` | No | `"dataset"` | dataset, ai or none. |
| `data.country` | `string` | Yes | `"US"` | Country association from dataset or ai_association, or null. Does not establish nationality, residence or ethnicity. |
| `data.country_source` | `string` | Yes | `"dataset"` | Origin of the country association: dataset, ai_association or null. |
| `data.match` | `object` | No | `{"name":"james","method":"normalized","scope":"country","country":"US"}` | Matched dataset name, method (normalized, token, substring or model_inference), country/global scope and matched country. Unavailable evidence is null. |

## Access, billing and request metadata

| Field | Type | Nullable | Example | Description |
| --- | --- | --- | --- | --- |
| `meta.request_id` | `string` | No | `"550e8400-e29b-41d4-a716-446655440000"` | Unique identifier for this HTTP attempt; also sent in X-Request-ID. |
| `meta.duration_ms` | `integer` | No | `42` | Request processing time in milliseconds. |
| `meta.access.mode` | `string` | No | `"api_key"` | api_key, ip_trial or unauthenticated. Verify api_key when using a paid account. |
| `meta.access.reason` | `string` | Yes | `null` | Trial fallback reason: api_key_missing, api_key_invalid or api_key_not_found; null otherwise. |
| `meta.usage.charged_credits` | `integer` | Yes | `1` | Net credits charged. Zero before charging or after a full confirmed refund; null when billing is unconfirmed. |
| `meta.usage.remaining_credits` | `integer` | Yes | `99` | Balance when the operation completes. May be negative; null when unavailable or billing is unconfirmed. |
| `meta.usage.billing_status` | `string` | No | `"confirmed"` | not_charged, confirmed or unconfirmed. Check this before retrying a failed prediction. |
| `meta.usage.resets_at` | `string` | Yes | `"2026-09-27T12:00:00.000Z"` | IP trial reset time in UTC (ISO 8601); null for API-key accounts or when unknown. |
| `meta.usage.limit` | `integer` | Yes | `10` | IP trial allowance; null for API-key accounts. |
| `meta.usage.period_seconds` | `integer` | Yes | `86400` | IP trial window in seconds; null for API-key accounts. |

## Read a nickname prediction

The example uses forceToGenderize: true. A resolved dataset lookup costs 1 credit. If unresolved, the API asks AI to interpret the alias and charges 2 credits total for a successful operation, including an unknown result. Omit ai_mode or set it to fallback; off and always conflict with forceToGenderize and return 422.

An inferred gender with name: null is allowed in nickname mode. This means no real given name was extracted; it is not a parser error. AI results have sample_count: null and confidence_kind: model_reported when a gender is returned. The score is not a calibrated probability or verified identity.

Without forceToGenderize, a single username lookup still defaults to ordinary AI fallback for 1 credit total, but it requires a usable real given name. Use options.ai_mode: off for dataset-only processing.

## Related guides

- [AI modes and forceToGenderize](https://www.genderapi.io/docs/v2/ai-options)
- [Batch predictions](https://www.genderapi.io/docs/v2/batch)
- [Credits and usage](https://www.genderapi.io/docs/v2/credits-and-usage)
- [Errors and retries](https://www.genderapi.io/docs/v2/errors-and-retries)
