Continue → Overview

REST API Reference

Four endpoints, one response shape. Everything below is live against your account.

https://namegender.com/api Sign up free

Quick start

Create a key in your dashboard, then send your first request. No SDK needed.

curl "https://namegender.com/api?name=Ay%C5%9Fe&country=TR" \
  -H "Authorization: Bearer YOUR_API_KEY"
<?php

$query = http_build_query(['name' => 'Ayşe', 'country' => 'TR']);

$ch = curl_init('https://namegender.com/api?' . $query);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer YOUR_API_KEY'],
]);

$result = json_decode(curl_exec($ch), true);

echo $result['gender'];       // female
echo $result['probability'];  // 100
const params = new URLSearchParams({ name: 'Ayşe', country: 'TR' });

const response = await fetch(`https://namegender.com/api?${params}`, {
  headers: { Authorization: 'Bearer YOUR_API_KEY' },
});

const result = await response.json();

console.log(result.gender);       // "female"
console.log(result.probability);  // 100
import requests

response = requests.get(
    "https://namegender.com/api",
    params={"name": "Ayşe", "country": "TR"},
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)

result = response.json()
print(result["gender"])       # female
print(result["probability"])  # 100
require "net/http"
require "json"

uri = URI("https://namegender.com/api")
uri.query = URI.encode_www_form(name: "Ayşe", country: "TR")

request = Net::HTTP::Get.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)["gender"]   # female
req, _ := http.NewRequest("GET", "https://namegender.com/api?name=Ay%C5%9Fe&country=TR", nil)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")

resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

var result struct {
    Gender      string `json:"gender"`
    Probability int    `json:"probability"`
}
json.NewDecoder(resp.Body).Decode(&result)

fmt.Println(result.Gender)   // female

Every endpoint returns the same shape, so you can switch inputs without changing your parsing code.

Authentication

Pass your API key in any of three ways. The Authorization header is recommended: query strings end up in server logs and browser history.

Authorization header (recommended)
Authorization: Bearer ng_live_xxxxxxxxxxxx
Custom header
X-Api-Key: ng_live_xxxxxxxxxxxx
Query parameter
https://namegender.com/api?name=Ayşe&key=ng_live_xxxxxxxxxxxx
Never expose your key in client-side code. Calls from a browser or mobile app should go through your own backend.

You can restrict a key to specific IP addresses from the dashboard.

Client libraries

One API, four input types, and bulk tools that handle files other services choke on.

View all
GET · POST https://namegender.com/api

Gender from name

Accepts a first name or a full name. Titles, middle names and surnames are stripped before lookup, so "Dr. Ayşe Yılmaz" and "Ayşe" give the same answer.

Parameter Type Description
name
required
string The name to classify. First name or full name.
country
optional
string ISO 3166-1 alpha-2 country code. Improves accuracy for names whose gender differs by region, such as Andrea (male in Italy, female in Germany).
askToAI
optional
boolean Fall back to a language model when the name is not in the database. Costs one extra credit.
forceToGenderize
optional
boolean Return the most likely gender even when confidence is below the threshold. Off by default, because a coin-flip answer presented as certain is worse than no answer.
curl "https://namegender.com/api?name=Jordan" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "status": true,
  "used_credits": 1,
  "remaining_credits": 49999,
  "expires": null,
  "q": "Jordan",
  "name": "Jordan",
  "gender": "male",
  "country": null,
  "total_names": 62240,
  "probability": 84,
  "confidence": "high",
  "duration": "1ms",
  "source": "db",
  "matched_as": null
}
GET · POST https://namegender.com/api/email

Gender from email

Extracts the person from the local part of the address, then classifies that. "ayse.yilmaz84@example.com" resolves to Ayşe.

Parameter Type Description
email
required
string The email address. Only the part before @ is used.
country
optional
string ISO 3166-1 alpha-2 country code. Improves accuracy for names whose gender differs by region, such as Andrea (male in Italy, female in Germany).
askToAI
optional
boolean Fall back to a language model when the name is not in the database. Costs one extra credit.

forceToGenderize is not available here: the name is extracted internally, so forcing a result on an uncertain extraction compounds two guesses.

curl "https://namegender.com/api/email?email=ayse.yilmaz84%40example.com" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "status": true,
  "q": "ayse.yilmaz84@example.com",
  "name": "Ayse",
  "gender": "female",
  "probability": 95,
  "confidence": "unverified",
  "total_names": 655,
  "duration": "1ms",
  "source": "db"
}
GET · POST https://namegender.com/api/username

Gender from username

Handles camelCase, snake_case, trailing digits and leading @ signs. "AyseYilmaz84" resolves to Ayşe.

Parameter Type Description
username
required
string The username or handle. A leading @ is ignored.
country
optional
string ISO 3166-1 alpha-2 country code. Improves accuracy for names whose gender differs by region, such as Andrea (male in Italy, female in Germany).
askToAI
optional
boolean Fall back to a language model when the name is not in the database. Costs one extra credit.
forceToGenderize
optional
boolean Return the most likely gender even when confidence is below the threshold. Off by default, because a coin-flip answer presented as certain is worse than no answer.
curl "https://namegender.com/api/username?username=AyseYilmaz84" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "status": true,
  "q": "AyseYilmaz84",
  "name": "Ayse",
  "gender": "female",
  "probability": 95,
  "confidence": "unverified",
  "source": "db"
}
POST https://namegender.com/api/bulk

Bulk request

Send up to 100 names in one request. Use this instead of looping: one bulk call for 100 names is a single round trip and a single deduplicated lookup.

Parameter Type Description
names
required
string[] Array of names. Maximum 100 items.
type
optional
string What the items are: name, email or username. Defaults to name.
country
optional
string Applied to every item in the request.
curl -X POST "https://namegender.com/api/bulk" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"names": ["Ayşe", "Mehmet", "Priya", "Wei"]}'
<?php

$ch = curl_init('https://namegender.com/api/bulk');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer YOUR_API_KEY',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'names' => ['Ayşe', 'Mehmet', 'Priya', 'Wei'],
    ]),
]);

$data = json_decode(curl_exec($ch), true);

foreach ($data['results'] as $row) {
    printf("%-8s %-7s %d%%\n", $row['q'], $row['gender'] ?? '?', $row['probability']);
}
const response = await fetch('https://namegender.com/api/bulk', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ names: ['Ayşe', 'Mehmet', 'Priya', 'Wei'] }),
});

const { results, summary } = await response.json();

console.log(summary.match_rate);   // 100
results.forEach(r => console.log(r.q, r.gender, r.probability));
import requests

response = requests.post(
    "https://namegender.com/api/bulk",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"names": ["Ayşe", "Mehmet", "Priya", "Wei"]},
)

data = response.json()
print(data["summary"]["match_rate"])   # 100

for row in data["results"]:
    print(row["q"], row["gender"], row["probability"])
{
  "status": true,
  "used_credits": 4,
  "remaining_credits": 49995,
  "duration": "2ms",
  "summary": {
    "total": 4,
    "identified": 4,
    "unknown": 0,
    "match_rate": 100
  },
  "results": [
    { "q": "Ayşe",   "gender": "female", "probability": 95, "total_names": 0, "confidence": "unverified", "source": "db" },
    { "q": "Mehmet", "gender": "male",   "probability": 95, "total_names": 0, "confidence": "unverified", "source": "db" },
    { "q": "Priya",  "gender": "female", "probability": 95, "total_names": 0, "confidence": "unverified", "source": "db" },
    { "q": "Wei",    "gender": "male",   "probability": 85, "total_names": 0, "confidence": "unverified", "source": "db" }
  ]
}

Results come back in the same order you sent them. Credits are charged per name, and the whole request is rejected before any work if your balance is short, so you never get a half-processed batch.

The summary block tells you the match rate at a glance, so you can decide whether the input needs cleaning before you process the rest.

GET https://namegender.com/api/me

Account & quota

Check your remaining balance without spending a credit.

curl "https://namegender.com/api/me" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "status": true,
  "email": "you@example.com",
  "remaining_credits": 50099,
  "purchased_credits": 50000,
  "free_today": 99,
  "free_daily_limit": 100,
  "lifetime_requests": 12480,
  "expires": null
}

Non-Latin scripts

Send a name in its own script and we bridge it to the reference data. No extra parameter: we detect the script and pick the right strategy for it.

Supported
Arabic script

Arabic writing omits short vowels, so محمد transliterates to "mhmd" while our data holds "muhammed". We match on the consonant pattern instead, and keep the feminine ة marker so خالد (Khalid) and خالدة (Khalida) stay apart. Also covers Persian and Urdu names written in Arabic script.

Chinese characters

Converted to Pinyin. The surname comes first in Chinese, so 李明 is surnamed 李 (Li) with the given name 明 (Ming) — we strip the surname before looking up. A single character is a complete given name and is handled as one.

Korean

Same surname-first handling as Chinese, using the common Korean surnames.

Japanese kana

Hiragana and katakana are read correctly (ひろし → Hiroshi).

Cyrillic

Russian, Ukrainian, Bulgarian and Serbian names transliterate directly.

Devanagari

Hindi, Marathi and Nepali names. The inherent trailing vowel is handled (राहुल → Rahul, not "Rahula").

Not supported
Japanese kanji

Japanese kanji and Chinese characters occupy the same Unicode range, so we cannot tell them apart from the characters alone. Han input is read as Chinese: a kanji name we do not already hold verbatim gets its Mandarin reading, which for a Japanese name is usually wrong (健太 reads as "jian tai" when the name is Kenta). Names we do hold match exactly and are correct. Send Japanese names in kana or Latin to be sure.

Thai

Thai omits vowels much like Arabic and needs its own matching layer. Not built yet, so these return null.

# Arap yazısı — ünsüz iskeleti üzerinden eşleşir
curl "https://namegender.com/api?name=%D9%85%D8%AD%D9%85%D8%AF" -H "Authorization: Bearer KEY"
# -> { "gender": "male", "probability": 100, "source": "script", "matched_as": "mahamad" }

# Çince — soyadı ayıklanır, verilen ad sorgulanır
curl "https://namegender.com/api?name=%E6%9D%8E%E6%98%8E" -H "Authorization: Bearer KEY"
# -> { "gender": "male", "probability": 92, "source": "db" }

When we cannot bridge a script at all, the response is gender: null with source: none. Check the source field: script means we transliterated, db means we matched the characters you sent directly. The two carry different confidence, and we show you which one you got.

Response fields

Identical across all endpoints.

Field Type Description
status boolean false when the request failed. Check this first.
used_credits integer Credits this request consumed.
remaining_credits integer Credits left after this request.
expires null Always null. Purchased credits do not expire.
q string Your input, echoed back unchanged.
name string The name we actually looked up after stripping titles and surnames.
gender string male, female, or null when we are not confident enough to say.
country string The country the statistics came from, or null for the global aggregate.
total_names integer How many observations this answer is based on. 0 means the source supplied no auditable count.
probability integer Observed dominant-gender ratio for counted data. Uncounted data is capped below 100; 0 when gender is null.
duration string Server-side processing time.

Evidence fields other services do not give you

confidence distinguishes counted evidence from records without a sample size. source tells you where the answer came from, and matched_as names the entry a fuzzy match landed on.

Field Type Description
source string db, fuzzy, llm, or none.
confidence string high, medium, low, unverified, or unknown, based on sample evidence.
matched_as string For a fuzzy match, the database entry that matched. Otherwise null.

Error codes

Errors return a JSON body with status: false and a machine-readable error string. Match on error, not on the message: messages are translated and may change.

Code HTTP Meaning
missing_key 401 API key is missing. Pass it as the "key" parameter or an Authorization: Bearer header.
invalid_key 401 This API key is not valid.
revoked_key 401 This API key has been revoked.
blocked 403 This account has been suspended. Contact support.
email_not_verified 403 This account's email address has not been confirmed yet. Open the confirmation link we emailed you, or request a new one from your dashboard.
ip_not_allowed 403 Requests from this IP address are not allowed for this key.
forbidden 403 You are not allowed to perform this action.
no_credits 402 You are out of credits. Buy more or wait for your daily free quota to reset.
missing_input 400 The "name" parameter is required.
invalid_input 422 The "name" parameter is not valid.
too_many_items 422 A maximum of 100 items can be sent in one request.
unknown_endpoint 404 There is no endpoint at this path. Check the URL against the API documentation.
method_not_allowed 405 This endpoint does not accept DELETE requests.
payload_too_large 413 That request body is too large.
rate_limited 429 Too many requests. Slow down and try again shortly.
not_ready 503 The name database is being rebuilt. Try again in a moment.
server_error 500 Something went wrong on our side. We have been notified.
{
  "status": false,
  "error": "no_credits",
  "message": "You are out of credits. Buy more or wait for your daily free quota to reset.",
  "request_id": "req_9c1f5b2a7e0d4a13",
  "docs": "https://namegender.com/docs#error-no_credits"
}

Rate limits

Normal usage does not hit a limit. The cap exists to stop a leaked key from being abused, and it counts per API key rather than per IP so that several customers on one server do not consume each other's allowance.

Current limit: 1,200 requests per minute per key. Need more? Ask and we will raise it on your account.

Every response carries the standard X-RateLimit-Limit and X-RateLimit-Remaining headers.

Migrating from another provider

Our response fields and parameter names match the common convention, so switching usually means changing one line: the base URL.

- https://api.other-provider.io/api?name=Ayşe&key=KEY
+ https://namegender.com/api?name=Ayşe&key=KEY

askToAI and forceToGenderize keep their original spelling for exactly this reason. If a field you rely on is missing, tell us and we will add it.

Encode your input

Names contain spaces and non-ASCII characters. URL-encode the value before putting it in a query string, or use POST with a JSON body.

Ayşe Yılmaz  ->  Ay%C5%9Fe%20Y%C4%B1lmaz
محمد          ->  %D9%85%D8%AD%D9%85%D8%AF
中村           ->  %E4%B8%AD%E6%9D%91