# Determine gender from Arabic names

> Try Arabic-script names with GenderAPI V2. Understand diacritics, full-name matching, country context, AI fallback and unknown results before integrating.

Canonical HTML: https://www.genderapi.io/determine-gender-from-arabic-names

Last reviewed: 2026-09-26

## Keep the Arabic spelling and check the matched name

Submit the name in its original Unicode spelling. The input below uses right-to-left Arabic text while the surrounding English guide and JSON remain left-to-right. The API can receive that text without a client-side transliteration step; accepting it is not a promise of dataset coverage.

The dataset path applies Unicode NFKC normalization and token splitting. It does not implement a general Arabic diacritic-removal or spelling-equivalence rule. Vowel marks and other spelling differences can therefore affect which stored key is found. Preserve the original value instead of silently treating every variation as the same person.

A full name can produce several candidate tokens. The available record with the largest stored total wins, so the first word is not guaranteed to be the returned given name. Review data.name and data.match.name before using a result to enrich a record.

| Example input | What it represents | What to check |
| --- | --- | --- |
| `ليلى الخطيب` | Full-name example | Multiple tokens can be considered. Inspect the selected match; this example does not assert a live outcome. |
| `ليلى / لَيْلَى` | Unmarked and marked spellings | Submit the actual spelling. These strings are not guaranteed to resolve to an identical stored record. |
| `Layla / Leila` | Latin spellings | These are separate input values, not an automatic transliteration equivalence promised by V2. |
| `country: SA` | Saudi Arabia demo context | This context is not a default for every Arabic-speaking community. Omit country when it is unknown. |

## Choose SA context deliberately

The live demo sends country: SA (Saudi Arabia). The API does not choose this country from the name's script. In your integration, use known, relevant context or omit country; do not turn this sample value into a default for every record.

For each candidate, the dataset path prefers an existing country-specific record and otherwise considers the global record. It then selects the available candidate with the largest stored total. Inspect data.match.scope and data.match.country to see the actual match scope. An ambiguous country-specific record is not automatically replaced by a global result; fallback may use AI instead.

data.input.country describes the request context. data.country and data.country_source describe a returned association. They are different fields and do not establish citizenship, ethnicity or residence.

- [Country context and matching reference](https://www.genderapi.io/docs/v2/gender-from-name#country-evidence-workflow)

## Send a Arabic name with V2

Run the request on your server with GENDERAPI_API_KEY set to your existing key. The example explicitly enables ordinary AI fallback. It illustrates a request, not a measured prediction or coverage result. Use options.ai_mode: off for dataset-only processing.

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": "name",
  "value": "ليلى الخطيب",
  "country": "SA",
  "options": {
    "ai_mode": "fallback"
  }
}'
```

**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": "name",
  "value": "ليلى الخطيب",
  "country": "SA",
  "options": {
    "ai_mode": "fallback"
  }
};

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\":\"name\",\"value\":\"ليلى الخطيب\",\"country\":\"SA\",\"options\":{\"ai_mode\":\"fallback\"}}")

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":"name","value":"ليلى الخطيب","country":"SA","options":{"ai_mode":"fallback"}}', 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\":\"name\",\"value\":\"ليلى الخطيب\",\"country\":\"SA\",\"options\":{\"ai_mode\":\"fallback\"}}";
        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\":\"name\",\"value\":\"ليلى الخطيب\",\"country\":\"SA\",\"options\":{\"ai_mode\":\"fallback\"}}", 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\":\"name\",\"value\":\"ليلى الخطيب\",\"country\":\"SA\",\"options\":{\"ai_mode\":\"fallback\"}}"
    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)
    }
}
```

- [Name request parameters](https://www.genderapi.io/docs/v2/gender-from-name)
- [Set up authentication](https://www.genderapi.io/docs/v2/authentication)

## Keep the result and its evidence together

An inferred association is not a person's self-declared gender. Keep the original input, the returned evidence and any information provided by the person separate. An unknown result is a valid outcome; it should not become a guessed category in your application.

| Field | How to use it |
| --- | --- |
| `data.gender / data.result_status` | Use male or female only when identified. Preserve native JSON null when the result is unknown. |
| `data.name / data.match` | Inspect the returned name and selected candidate. A substring match does not prove that the input belongs to someone with that given name. |
| `data.confidence / data.confidence_kind` | Confidence is on a 0–1 scale or null. observed_frequency comes from stored counts; model_reported is an AI score. Evaluate thresholds separately for each kind. |
| `data.source / data.sample_count` | Distinguish dataset, ai and none. AI results have no stored sample count. A sample count is not a measured accuracy score. |
| `meta.access.mode` | Confirm api_key for an account integration. Missing or unrecognized keys can instead use the shared IP trial. |
| `meta.usage` | Read charged_credits and billing_status. A successful unknown is billable. A lost response does not establish that the request was free. |

- [Response fields and unknown outcomes](https://www.genderapi.io/docs/v2/responses)
- [Accuracy and confidence explained](https://www.genderapi.io/accuracy-methodology)
- [Data sources and the dated database profile](https://www.genderapi.io/data-provenance)

## Choose when to use AI

forceToGenderize is optional for names, email addresses and usernames. With it enabled, omit ai_mode or use fallback; off and always conflict with it and return 422. A positive starting balance is enough to begin a request even if its final charge takes the balance below zero.

Nickname mode can return a gender with name: null. It can also return an unknown result. Neither ordinary fallback nor nickname inference guarantees a correct or non-null answer.

| Request option | Behavior | Credits for a successful lookup |
| --- | --- | --- |
| options.ai_mode: off | Use the dataset only. | 1, including an unknown result |
| options.ai_mode: fallback | Try the dataset, then ordinary AI when no gender is returned. This is the single-request default. | 1 total, including AI fallback |
| options.ai_mode: always | Ask AI directly. | 2 |
| forceToGenderize: true | Try the dataset first, then allow AI to interpret a personal nickname or alias even without a real given name. | 1 for a resolved dataset result; 2 total if AI is used |

- [AI options and nickname inference](https://www.genderapi.io/docs/v2/ai-options)
- [Credits and usage](https://www.genderapi.io/docs/v2/credits-and-usage)

## Process a name list without losing its context

Use POST /api/v2/gender/batch for up to 50 items with registered API-key access or 10 items in the IP trial. Assign unique item IDs and retain the original value for each row. Each item can have its own country and AI policy; omit a country when it is unknown.

Batch items default to dataset-only mode, unlike the single-request fallback default. Set options.ai_mode: fallback on each item that needs AI. Check every result, even after HTTP 200, preserve unknown outcomes and review item errors and billing before another submission. Each successful ordinary item uses one credit; one batch HTTP request is not one prediction credit.

- [Batch examples and partial failures](https://www.genderapi.io/docs/v2/batch#batch-application-workflow)
- [Handle unknowns in your application](https://www.genderapi.io/docs/v2/responses#application-decisions)

## Understand what you send

The live demo sends the entered value to GenderAPI only when you submit it. If AI is invoked, the submitted type, value and country context are sent to the configured inference service. For dataset-only prediction, use options.ai_mode: off without forceToGenderize in your integration.

The Privacy Policy, processing agreement and subprocessor register describe the published handling terms. A result is an inferred association; let information supplied by the person take precedence. Do not use it as the basis for consequential decisions about an individual.

- [Privacy Policy](https://www.genderapi.io/privacy-policy)
- [Data Processing Agreement](https://www.genderapi.io/data-processing-agreement)
- [Subprocessor register](https://www.genderapi.io/subprocessors)

## Does V2 accept Arabic text with diacritics?

It accepts Unicode name input, including Arabic text. Dataset normalization does not generally remove Arabic vowel marks, so marked and unmarked spellings may produce different matches or an unknown result.

## Does this tool identify an Arabic-speaking person's nationality?

No. SA is the explicit country context for this demonstration. Script, a name association and a returned country field do not establish nationality or residence.

## Is accuracy measured for every Arabic name?

No such measurement is published here. Input acceptance, stored counts and a confidence value are not an independent accuracy benchmark. Review the methodology page for the available evidence and the measurements that remain unpublished.

## Does an unknown result use credits?

A successful unknown is billable. Ordinary dataset or fallback mode costs one credit total. With forceToGenderize enabled, a resolved dataset result costs one credit and using AI costs two total, including an unknown AI result. Errors have separate billing rules; inspect meta.usage.
