Demografix Python SDK
Predict gender, age, and nationality from names. The Python SDK covers all three Demografix APIs — genderize.io (gender), agify.io (age), and nationalize.io (nationality) — with single-name lookups and batches of up to 100 names per request. Synchronous and asynchronous clients share the same methods and models.
Install
pip install demografix
The SDK requires Python 3.10 or newer. The asynchronous client uses httpx.
Quickstart
Construct a client, run a list of names through a batch call, read the predictions, and read the quota.
from collections import Counter
from demografix import Demografix
client = Demografix(api_key="YOUR_API_KEY")
names = ["michael", "matthew", "jane", "sofia", "lars"]
batch = client.genderize_batch(names)
split = Counter(r.gender or "unknown" for r in batch.results)
print(split) # Counter({'male': 3, 'female': 2})
print(batch.quota.remaining) # 24987
Async client
AsyncDemografix mirrors the synchronous API with native async HTTP. Use one client across requests so
its connection pool can be reused, and close it with async with or aclose().
import asyncio
from collections import Counter
from demografix import AsyncDemografix
async def main():
names = ["michael", "matthew", "jane", "sofia", "lars"]
async with AsyncDemografix(api_key="YOUR_API_KEY") as client:
genders, ages, nationalities = await asyncio.gather(
client.genderize_batch(names),
client.agify_batch(names),
client.nationalize_batch(names),
)
split = Counter(r.gender or "unknown" for r in genders.results)
print(split)
print(nationalities.quota.remaining)
asyncio.run(main())
Task cancellation propagates normally. Network failures and timeouts raise TransportError, matching
the synchronous client.
genderize
Predict gender from names. A single call returns the prediction fields plus a quota.
result = client.genderize("peter")
result.gender # "male", "female", or None
result.probability # 1.0
result.count # 1352696
result.quota.remaining # 24987
The batch form reduces a list to a gender split.
batch = client.genderize_batch(["michael", "matthew", "jane"])
gender_mix = Counter(r.gender or "unknown" for r in batch.results)
gender is None when no match is found, with probability 0.0 and count 0. That is a
successful response, not an error.
agify
Predict age from names. Aggregate a batch into an age distribution.
result = client.agify("michael")
result.age # 57 or None
result.count # 311558
batch = client.agify_batch(["michael", "matthew", "jane"])
ages = [r.age for r in batch.results if r.age is not None]
average_age = sum(ages) / len(ages)
age is an integer or None.
nationalize
Predict nationality from names. Each prediction carries up to five candidate countries in descending probability.
result = client.nationalize("nguyen")
result.country[0].country_id # "VN"
result.country[0].probability # 0.891132
batch = client.nationalize_batch(["nguyen", "schmidt", "rossi"])
top_countries = Counter(
r.country[0].country_id for r in batch.results if r.country
)
country is an empty list when no match is found.
Batch limit
Each batch accepts at most 100 names. A batch of more than 100 raises ValidationError before any
request goes out. Chunk a longer list and aggregate across the chunks.
def chunked(items, size=100):
for i in range(0, len(items), size):
yield items[i : i + size]
split = Counter()
for chunk in chunked(roster):
batch = client.genderize_batch(chunk)
split.update(r.gender or "unknown" for r in batch.results)
country_id
genderize and agify accept an optional country_id (ISO 3166-1 alpha-2) to scope the prediction
to one country. Input is case-insensitive; the response echoes it uppercase on every prediction.
nationalize has no such parameter.
result = client.genderize("kim", country_id="us")
result.country_id # "US"
# Scope a whole list, then aggregate.
batch = client.agify_batch(["kim", "andrea", "jan"], country_id="us")
ages = [r.age for r in batch.results if r.age is not None]
batch.results[0].country_id # "US", echoed uppercase on each prediction
Scoping changes the prediction: andrea reads mostly female in the United States and mostly male in
Italy. When the request sends no country_id, the field is None.
Quota
Every result and every raised error carries a quota read from the response headers. Quota is never
cached on the client; read it from the returned value.
| Field | Meaning |
|---|---|
limit |
names allowed in the current window |
remaining |
names left in the current window |
reset |
seconds until the window resets |
batch = client.genderize_batch(["michael", "matthew"])
batch.quota.remaining
Errors
Non-2xx responses raise a typed exception. Transport failures raise TransportError. Every exception
subclasses DemografixError and carries status, message, and quota (when the response included
headers).
| Exception | Status | Cause |
|---|---|---|
AuthError |
401 | invalid or missing API key |
SubscriptionError |
402 | expired freebie or inactive subscription |
ValidationError |
422 | bad parameter, or a batch over 100 names (raised client-side) |
RateLimitError |
429 | window exhausted; quota is always populated |
DemografixError |
other non-2xx | base class for the hierarchy |
TransportError |
none | network error, timeout, or non-JSON body |
A RateLimitError carries quota, so reset tells you how long to wait before retrying.
import time
from demografix import Demografix, RateLimitError
client = Demografix(api_key="YOUR_API_KEY")
names = ["michael", "matthew", "jane"]
while True:
try:
batch = client.genderize_batch(names)
break
except RateLimitError as exc:
time.sleep(exc.quota.reset)
Methods
| Method | Returns | country_id |
|---|---|---|
genderize(name, country_id=None) |
GenderizeResult |
yes |
genderize_batch(names, country_id=None) |
Batch of GenderizePrediction |
yes |
agify(name, country_id=None) |
AgifyResult |
yes |
agify_batch(names, country_id=None) |
Batch of AgifyPrediction |
yes |
nationalize(name) |
NationalizeResult |
no |
nationalize_batch(names) |
Batch of NationalizePrediction |
no |
A *Result exposes the prediction fields directly plus a quota. A Batch exposes results plus
one quota for the whole response. Both Demografix and AsyncDemografix expose the methods above;
await methods on AsyncDemografix. Each constructor takes api_key and an optional timeout=10.0.
The host URLs and the User-Agent are fixed constants, not options.
AsyncDemografix supports async with and aclose() to close its connection pool. The synchronous
client retains its existing lifecycle and requires no explicit cleanup.
API keys
An API key is required. Creating one is free and includes 2,500 names per month.
Quota counts names, not requests. A single-name call costs 1. A batch of 100 names costs 100. The free tier therefore covers 2,500 names in a month however they are split across calls.
Generate a key in your dashboard at genderize.io, agify.io, or nationalize.io. One key works across all three services. Full reference: genderize.io/documentation/api.
License
MIT. See LICENSE.
Release files for demografix 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| demografix-0.3.0.tar.gz | 15.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| demografix-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.9 kB
Release files / demografix-0.3.0.tar.gz
| Download URL | demografix-0.3.0.tar.gz |
|---|---|
| Size | 15.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
49d903dd97f2101ae2e58978f0fc4824c6244273f01e9b4972eab26823f11a1b
|
|
BLAKE2b-256 checksum How to use checksums |
2fcf977a63cb4448e5b96ad416c1fa32affd6ad8a07ff598f46239eef52e7d3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / demografix-0.3.0-py3-none-any.whl
| Download URL | demografix-0.3.0-py3-none-any.whl |
|---|---|
| Size | 12.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c99e606c7955efe55eb8bd688b82f7a2474e4c5c0f6586c6b8fb83f5d4ea90f0
|
|
BLAKE2b-256 checksum How to use checksums |
86549eb4aa3f34de2584fe6471a75377a97f93351109c2be8c8e7cc110c6db21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency log