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: Bearer ng_live_xxxxxxxxxxxx
X-Api-Key: ng_live_xxxxxxxxxxxx
https://namegender.com/api?name=Ayşe&key=ng_live_xxxxxxxxxxxx
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 allhttps://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
}
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"
}
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"
}
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.
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.
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.
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.
Same surname-first handling as Chinese, using the common Korean surnames.
Hiragana and katakana are read correctly (ひろし → Hiroshi).
Russian, Ukrainian, Bulgarian and Serbian names transliterate directly.
Hindi, Marathi and Nepali names. The inherent trailing vowel is handled (राहुल → Rahul, not "Rahula").
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 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. |
ai_consent_required |
422 | askToAI sends the name to a third-party AI provider, which this account has not agreed to. See the "ai" field for who that provider is, then enable AI lookups in your dashboard settings. |
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