genderapi 1.0.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +193 -219
- data/lib/genderapi/client.rb +279 -180
- data/lib/genderapi/errors.rb +236 -0
- data/lib/genderapi/models.rb +278 -0
- data/lib/genderapi/validation.rb +158 -0
- data/lib/genderapi/version.rb +2 -2
- data/lib/genderapi.rb +8 -2
- metadata +19 -56
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19c443b45bcc60a40cebfd3d8ad9c961b3353b74dce046db0d66082c1a00e827
|
|
4
|
+
data.tar.gz: f6c444dd0d3744d885b08c809f60bb3fa89a3cd1178c59b1030a551e0da9a906
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 507c514f022c2db81220a69831a8409147ec517f12c4df613d0909165a0179645e1b382c4a10f22359a173a969039b6819695f8a2e4f0a8175703cb7076695af
|
|
7
|
+
data.tar.gz: 4040e050a3278cb070541f28c588c188980fe0ef5ff89501a242baa1cc66ea78c8672bd4369f2aa6b2f3fe83c152aa62bda407dc0bc69b20278a57c63a525a9c
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.0.0 - 2026-09-30
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- The client now targets the GenderAPI.io V2 API (`https://api.genderapi.io/api/v2`). 1.x (V1 API) stays available and installable indefinitely (`gem install genderapi -v "~> 1.0"`); no deprecation or shutdown is planned. The source stays on the `v1` branch.
|
|
8
|
+
- New interface: `GenderAPI::Client#gender(type, value, country:, ai_mode:, force_to_genderize:, id:)` with `#name`, `#email` and `#username`, plus `#gender_batch(items)`, `#usage`, `#validate_phone(number, country:)`, `#capabilities` and `#error_catalog`. The V1 methods (`get_gender_by_*` and `get_gender_by_*_bulk`) have been removed.
|
|
9
|
+
- Responses are V2 `data`/`meta` objects wrapped in typed readers (`GenderResult`, `BatchResult`, `UsageResult`, `PhoneResult`) that keep every field. `probability` is replaced by `confidence` (0-1) and `confidence_kind`; `used_credits` is replaced by `meta.usage.charged_credits`.
|
|
10
|
+
- HTTP errors raise `GenderAPI::APIError` subclasses exposing `status`, `code`, `action`, `detail`, `errors`, `request_id`, `retry_after` and `billing_status`, instead of a generic `RuntimeError`.
|
|
11
|
+
- Ruby >= 3.0 is required. The runtime dependencies on `httparty` and `json` have been removed; the gem uses only the standard library.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `api_key` defaults to `ENV["GENDERAPI_API_KEY"]`. Without a key, the server applies the shared IP trial.
|
|
16
|
+
- Client-side validation that mirrors the V2 request schema. Invalid input raises `GenderAPI::ValidationError` without making a network request.
|
|
17
|
+
- Partial batch success is returned together with `failed_items` and `summary`. Batches where every item failed raise an error whose `items` holds the item outcomes.
|
|
18
|
+
- `UnexpectedAccessModeError` when a configured key is answered with IP-trial access.
|
|
19
|
+
|
|
20
|
+
### Safety
|
|
21
|
+
|
|
22
|
+
- No automatic retries (including on 429 and for GET requests), no redirects followed, a 10-second default timeout, HTTPS required (plain http only for localhost tests), no request when the gem is loaded or a client is constructed, and the key is never placed in a URL, logged or shown by `inspect`.
|
data/README.md
CHANGED
|
@@ -1,289 +1,263 @@
|
|
|
1
|
-
# genderapi
|
|
1
|
+
# genderapi (Ruby)
|
|
2
2
|
|
|
3
|
-
Official
|
|
3
|
+
Official GenderAPI.io V2 client for Ruby.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It sends names, email addresses and usernames to the [GenderAPI.io V2 API](https://www.genderapi.io/api-documentation) and returns the complete V2 response: the prediction in `data` and access and billing information in `meta`. Results are inferences, not identity verification, and they can be unknown.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
> **Version 2.0.0 is a breaking release.** It targets the V2 API (`https://api.genderapi.io/api/v2`). 1.x (V1 API) stays available and installable indefinitely; no deprecation or shutdown is planned. To keep using it, pin 1.x with `gem install genderapi -v "~> 1.0"` (Gemfile: `gem "genderapi", "~> 1.0"`). The source stays on the [`v1` branch](https://github.com/GenderAPI/genderapi-ruby/tree/v1). See [Migrating from 1.x](#migrating-from-1x).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- Ruby >= 3.0
|
|
10
|
+
- No runtime dependencies (standard library `net/http` and `json`)
|
|
11
|
+
- **Server-side only.** Keep your API key in the server environment. Never embed it in browser, mobile or other client-side code.
|
|
10
12
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
Add this line to your Gemfile:
|
|
13
|
+
## Installation
|
|
14
14
|
|
|
15
15
|
```ruby
|
|
16
|
-
|
|
16
|
+
# Gemfile
|
|
17
|
+
gem "genderapi", "~> 2.0"
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
Then execute:
|
|
20
|
-
|
|
21
20
|
```bash
|
|
22
21
|
bundle install
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Or install it manually:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
22
|
+
# or
|
|
28
23
|
gem install genderapi
|
|
29
24
|
```
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## 📝 Usage
|
|
34
|
-
|
|
35
|
-
### 🔹 Get Gender by Name
|
|
26
|
+
## Quick start
|
|
36
27
|
|
|
37
28
|
```ruby
|
|
38
|
-
require
|
|
29
|
+
require "genderapi"
|
|
39
30
|
|
|
40
|
-
|
|
31
|
+
# Reads ENV["GENDERAPI_API_KEY"] when api_key is not given.
|
|
32
|
+
client = GenderAPI::Client.new(api_key: ENV["GENDERAPI_API_KEY"])
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
puts result
|
|
34
|
+
result = client.name("Andrea", country: "IT")
|
|
35
|
+
prediction = result.data
|
|
45
36
|
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
prediction.gender # => "female", "male" or nil
|
|
38
|
+
prediction.result_status # => "identified" or "unknown"
|
|
39
|
+
prediction.confidence # => 0..1 or nil (not a calibrated probability)
|
|
40
|
+
prediction.confidence_kind # => "observed_frequency", "model_reported" or nil
|
|
41
|
+
result.usage.charged_credits
|
|
42
|
+
result.usage.remaining_credits
|
|
43
|
+
result.request_id
|
|
49
44
|
```
|
|
50
45
|
|
|
51
|
-
|
|
46
|
+
Requiring the gem or constructing a client never makes a network request. Every method call makes exactly one HTTP request.
|
|
52
47
|
|
|
53
|
-
###
|
|
48
|
+
### Email and username
|
|
54
49
|
|
|
55
50
|
```ruby
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
client.email("alex.smith@example.com")
|
|
52
|
+
client.username("prenses", country: "TR", force_to_genderize: true)
|
|
58
53
|
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
puts result
|
|
54
|
+
# Generic form: type is "name", "email" or "username"
|
|
55
|
+
client.gender("username", "michael_dev", ai_mode: "off", id: "row-17")
|
|
62
56
|
```
|
|
63
57
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
### 🔹 Get Gender by Username
|
|
58
|
+
### Batch (1-50 items)
|
|
67
59
|
|
|
68
60
|
```ruby
|
|
69
|
-
result =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
result = client.gender_batch([
|
|
62
|
+
{ type: "name", value: "Andrea", country: "IT", id: "a-1" },
|
|
63
|
+
{ type: "email", value: "alex@example.com", id: "a-2" },
|
|
64
|
+
{ type: "username", value: "prenses", id: "a-3", ai_mode: "fallback", force_to_genderize: true }
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
result.items.each do |item|
|
|
68
|
+
if item.success?
|
|
69
|
+
puts "#{item.id}: #{item.data.gender.inspect} (#{item.data.result_status})"
|
|
70
|
+
else
|
|
71
|
+
puts "#{item.id}: #{item.error.code} (#{item.error.action})"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
result.summary.to_h # => {"total"=>3, "succeeded"=>2, "identified"=>1, "unknown"=>1, "failed"=>1}
|
|
76
|
+
result.failed_items # failed rows; partial success is returned, not raised
|
|
75
77
|
```
|
|
76
78
|
|
|
77
|
-
|
|
79
|
+
The server may allow fewer items than 50 (IP-trial batches allow at most 10). Split larger jobs yourself. Item ids are optional, but when you give them they must be unique and at most 64 characters.
|
|
78
80
|
|
|
79
|
-
###
|
|
81
|
+
### Usage (free)
|
|
80
82
|
|
|
81
83
|
```ruby
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
result = api.get_gender_by_name_bulk(data: bulk_data)
|
|
88
|
-
puts result
|
|
84
|
+
usage = client.usage
|
|
85
|
+
usage.data.remaining_credits
|
|
86
|
+
usage.data.expires_at
|
|
87
|
+
usage.meta.access.mode # "api_key", "ip_trial" or "unauthenticated"
|
|
89
88
|
```
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
### 🔹 Get Gender by Email (Bulk)
|
|
90
|
+
### Phone validation
|
|
94
91
|
|
|
95
92
|
```ruby
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{ email: "maria@domain.de", country: "DE", id: "def456" }
|
|
99
|
-
]
|
|
100
|
-
|
|
101
|
-
result = api.get_gender_by_email_bulk(data: bulk_data)
|
|
102
|
-
puts result
|
|
93
|
+
client.validate_phone("+1 415 555 0100")
|
|
94
|
+
client.validate_phone("415 555 0100", country: "US") # country is required without a leading +
|
|
103
95
|
```
|
|
104
96
|
|
|
105
|
-
|
|
97
|
+
This checks the number's structure, not whether a subscriber exists. It costs 1 credit, including for invalid numbers.
|
|
106
98
|
|
|
107
|
-
###
|
|
99
|
+
### Discovery (no key sent)
|
|
108
100
|
|
|
109
101
|
```ruby
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{ username: "maria2025", country: "DE", id: "u002" }
|
|
113
|
-
]
|
|
114
|
-
|
|
115
|
-
result = api.get_gender_by_username_bulk(data: bulk_data)
|
|
116
|
-
puts result
|
|
102
|
+
client.capabilities # GET /api/v2
|
|
103
|
+
client.error_catalog # GET /api/v2/errors
|
|
117
104
|
```
|
|
118
105
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
136
|
-
|
|
106
|
+
## Options
|
|
107
|
+
|
|
108
|
+
### Client
|
|
109
|
+
|
|
110
|
+
| Option | Default | Description |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `api_key` | `ENV["GENDERAPI_API_KEY"]` | Your API key. It is sent only as `Authorization: Bearer <key>`, never in a URL, and it is never logged or shown by `inspect`. |
|
|
113
|
+
| `base_url` | `https://api.genderapi.io/api/v2` | HTTPS is required. Plain `http://` is accepted only for `localhost`, `127.0.0.1` and `[::1]`, for tests. |
|
|
114
|
+
| `timeout` | `10` | Seconds allowed for each connect, write and read operation. |
|
|
115
|
+
| `user_agent` | `nil` | Text appended to the default `genderapi-ruby/2.0.0 (Ruby x.y.z)` User-Agent. |
|
|
116
|
+
| `require_api_key_access` | `true` | When a key is configured and the response reports IP-trial or unauthenticated access (the key was not accepted), raise `GenderAPI::UnexpectedAccessModeError`. The request has already been processed, so IP-trial credits may have been used. The complete result is in `error.result`, the mode in `error.access_mode`. Set `false` to return the result instead. Never applies without a key, or to `capabilities`/`error_catalog`. |
|
|
117
|
+
|
|
118
|
+
### Prediction (`gender`, `name`, `email`, `username`, batch items)
|
|
119
|
+
|
|
120
|
+
| Ruby argument | Wire field | Description |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| `type` | `type` | `"name"`, `"email"` or `"username"` |
|
|
123
|
+
| `value` | `value` | 1-254 characters that are not all whitespace and contain no control characters |
|
|
124
|
+
| `country:` | `country` | Optional uppercase ISO 3166-1 alpha-2 code such as `"US"`. Leave it out when the country is unknown. |
|
|
125
|
+
| `ai_mode:` | `options.ai_mode` | `"off"`, `"fallback"` or `"always"`. If you leave it out, the server uses its default: `fallback` for single requests and `off` for batch items. |
|
|
126
|
+
| `force_to_genderize:` | `forceToGenderize` | `true` checks the dataset first (1 credit). If that result is unknown, it uses nickname-aware AI inference (2 credits total). It cannot be combined with `ai_mode` `off` or `always`. |
|
|
127
|
+
| `id:` | `id` | Optional correlation id, 1-64 characters |
|
|
128
|
+
|
|
129
|
+
Invalid input raises `GenderAPI::ValidationError` (with `#field`) **before** any network request. The API remains authoritative for email syntax and ISO country membership, and it reports problems with those as HTTP 422.
|
|
130
|
+
|
|
131
|
+
Credits (server rules): a dataset result or automatic AI fallback costs 1 credit, including unknown results. `ai_mode: "always"` costs 2. A request needs a positive starting balance, and the full tariff can take the balance below zero (for example, 1 - 2 = -1).
|
|
132
|
+
|
|
133
|
+
## Response fields
|
|
134
|
+
|
|
135
|
+
Every result wraps the parsed JSON. Typed readers are provided for the documented fields. `[]`, `dig` and `to_h` give access to all fields, including ones added in future API versions. Values are never converted: `confidence` stays on its 0-1 scale and is never turned into a percentage or probability.
|
|
136
|
+
|
|
137
|
+
| Reader | Meaning |
|
|
138
|
+
| --- | --- |
|
|
139
|
+
| `data.gender` | `"male"`, `"female"` or `nil` |
|
|
140
|
+
| `data.result_status` | `"identified"` (gender is set) or `"unknown"` (gender is nil). An unknown result is a successful, billed outcome. |
|
|
141
|
+
| `data.reason` | `nil`, `"not_found"`, `"no_name_candidate"`, `"ambiguous"` or `"insufficient_evidence"` |
|
|
142
|
+
| `data.confidence` / `data.confidence_kind` | Score from 0 to 1 and what it is: `observed_frequency` (dominant dataset count / total) or `model_reported` (AI score, not calibrated). Both are nil when gender is nil. |
|
|
143
|
+
| `data.sample_count` | Dataset sample count; nil for AI |
|
|
144
|
+
| `data.source` | `"dataset"`, `"ai"` or `"none"` |
|
|
145
|
+
| `data.name`, `data.country`, `data.country_source` | The returned name, the country and where the country came from (`dataset`, `ai_association` or nil). These fields never indicate nationality or residence. |
|
|
146
|
+
| `data.match` | `{"name", "method", "scope", "country"}`: the dataset candidate and lookup scope |
|
|
147
|
+
| `data.input` | The input as the server understood it |
|
|
148
|
+
| `meta.request_id`, `meta.duration_ms` | Identifier and duration of this HTTP attempt |
|
|
149
|
+
| `meta.access.mode` / `.reason` | `api_key`, `ip_trial` or `unauthenticated`; the trial reason, if any |
|
|
150
|
+
| `meta.usage.billing_status` | `not_charged`, `confirmed` or `unconfirmed` |
|
|
151
|
+
| `meta.usage.charged_credits` | Net charge. It is nil when billing is unconfirmed. |
|
|
152
|
+
| `meta.usage.remaining_credits` | Balance when the request completed. It can be negative, or nil if unknown. |
|
|
153
|
+
| `meta.usage.resets_at`, `.limit`, `.period_seconds` | IP-trial window (nil otherwise) |
|
|
154
|
+
| Batch `items[i].index`, `.id`, `.charged_credits` | Each row has exactly one of `.data` (Prediction) or `.error` (item problem) |
|
|
155
|
+
| Batch `meta.summary` | `total`, `succeeded`, `identified`, `unknown`, `failed` |
|
|
156
|
+
|
|
157
|
+
## Errors
|
|
158
|
+
|
|
159
|
+
All errors inherit from `GenderAPI::Error`. Messages never include your key or input values.
|
|
160
|
+
|
|
161
|
+
| Class | When |
|
|
162
|
+
| --- | --- |
|
|
163
|
+
| `ValidationError` | Invalid arguments. No request was sent. |
|
|
164
|
+
| `APIError` | HTTP >= 400. Subclasses: `BadRequestError` (400), `AuthenticationError` (401), `PermissionDeniedError` (403), `NotFoundError` (404), `UnprocessableEntityError` (422), `RateLimitError` (429), `ServerError` (5xx) |
|
|
165
|
+
| `RedirectError` | The server answered with a 3xx. Redirects are never followed, so your key is never forwarded to another location. |
|
|
166
|
+
| `TransportError` / `TimeoutError` | No usable response was received. The request may still have completed and been billed. |
|
|
167
|
+
| `InvalidResponseError` | A 2xx response that is not the expected JSON structure |
|
|
168
|
+
| `UnexpectedAccessModeError` | A key was configured, but the response reports IP-trial or unauthenticated access (see `require_api_key_access`). The request has already been processed and trial credits may have been used; the complete result is in `error.result`. Do not resend automatically. |
|
|
169
|
+
|
|
170
|
+
`APIError` exposes `status`, `code` (a stable machine code; match on this, never on `detail`), `title`, `detail`, `type`, `instance`, `action`, `documentation`, `errors` (validation pointers such as `[{"pointer" => "/value", "message" => "..."}]`), `request_id` (from `meta.request_id`, the body, or the `X-Request-ID` header), `retry_after` (from the `Retry-After` header: Integer seconds, or the raw String for an HTTP date), `usage`, `billing_status`, `billing_unconfirmed?`, `items` / `data` (item outcomes when every batch item failed), `body` (parsed) and `raw_body`. Proxy errors can be non-JSON. In that case `code` is nil and `raw_body` holds the response text. The error body can contain your inputs, so inspect it securely and do not log it wholesale.
|
|
137
171
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
| username | String | Yes | Username to query. |
|
|
153
|
-
| country | String | No | Two-letter country code (e.g. "US"). Helps narrow down gender detection results by region. |
|
|
154
|
-
| askToAI | Boolean | No | Default is `false`. If `true`, sends the query directly to AI for maximum accuracy, consuming 3 credits per request. If `false`, GenderAPI first tries its internal database and uses AI only if necessary, without spending 3 credits. Recommended for non-latin characters or unusual strings. |
|
|
155
|
-
| forceToGenderize | Boolean | No | Default is `false`. When `true`, analyzes even nicknames, emojis, or unconventional strings like "spider man" instead of returning `null` for non-standard names. |
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
### Name Lookup (Bulk)
|
|
160
|
-
|
|
161
|
-
| Parameter | Type | Required | Description |
|
|
162
|
-
|--------------------|----------|----------|-------------|
|
|
163
|
-
| data | Array<Hash> | Yes | Array of objects containing name, optional country, and optional id. Limit is 100 records per request. |
|
|
164
|
-
|
|
165
|
-
Each object in the data array may include:
|
|
166
|
-
- `name`: The name to analyze (required).
|
|
167
|
-
- `country`: Two-letter country code (optional).
|
|
168
|
-
- `id`: Custom identifier to correlate input/output (optional).
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
### Email Lookup (Bulk)
|
|
173
|
-
|
|
174
|
-
| Parameter | Type | Required | Description |
|
|
175
|
-
|--------------------|----------|----------|-------------|
|
|
176
|
-
| data | Array<Hash> | Yes | Array of objects containing email, optional country, and optional id. Limit is 50 records per request. |
|
|
172
|
+
```ruby
|
|
173
|
+
begin
|
|
174
|
+
client.name("Andrea")
|
|
175
|
+
rescue GenderAPI::RateLimitError => e
|
|
176
|
+
# Wait e.retry_after seconds. A later request is a new, billable operation.
|
|
177
|
+
rescue GenderAPI::APIError => e
|
|
178
|
+
if e.billing_unconfirmed?
|
|
179
|
+
# Contact support with e.request_id. Do not retry automatically.
|
|
180
|
+
end
|
|
181
|
+
warn "GenderAPI #{e.status} #{e.code} #{e.action} #{e.request_id}"
|
|
182
|
+
rescue GenderAPI::TransportError => e
|
|
183
|
+
# The outcome is unknown. Check client.usage before sending again.
|
|
184
|
+
end
|
|
185
|
+
```
|
|
177
186
|
|
|
178
|
-
|
|
179
|
-
- `email`: The email address to analyze (required).
|
|
180
|
-
- `country`: Two-letter country code (optional).
|
|
181
|
-
- `id`: Custom identifier to correlate input/output (optional).
|
|
187
|
+
The machine-readable catalog of codes and actions is at [`/api/v2/errors`](https://api.genderapi.io/api/v2/errors) (`client.error_catalog`).
|
|
182
188
|
|
|
183
|
-
|
|
189
|
+
## Billing and no-retry rules
|
|
184
190
|
|
|
185
|
-
|
|
191
|
+
- **No automatic retries, ever.** Every prediction or phone request is a new, billable operation. If a response is lost, the request may still have been billed, so the client never retries, not even on 429.
|
|
192
|
+
- **429:** wait for `retry_after` before sending another request. That request is a new operation with normal charges.
|
|
193
|
+
- **`billing_status: "unconfirmed"`** (for example `billing_reconciliation_required`): contact support with the `request_id` before retrying.
|
|
194
|
+
- **5xx prediction failures:** check `billing_status` and fix the cause before sending another request.
|
|
195
|
+
- **Partial batch success:** retry only the failed items, and only after billing is confirmed. Resubmitting successful items charges them again.
|
|
196
|
+
- **Timeouts or transport errors:** check `client.usage` before sending again.
|
|
197
|
+
- Redirects are never followed. HTTPS is required. The default timeout is 10 seconds.
|
|
186
198
|
|
|
187
|
-
|
|
188
|
-
|--------------------|----------|----------|-------------|
|
|
189
|
-
| data | Array<Hash> | Yes | Array of objects containing username, optional country, and optional id. Limit is 50 records per request. |
|
|
199
|
+
## IP trial (no key)
|
|
190
200
|
|
|
191
|
-
|
|
192
|
-
- `username`: The username to analyze (required).
|
|
193
|
-
- `country`: Two-letter country code (optional).
|
|
194
|
-
- `id`: Custom identifier to correlate input/output (optional).
|
|
201
|
+
The client also works without an API key. The server then applies a shared IP trial: 10 credits per 24 hours per public IP address, normal tariffs, and batches of at most 10 items. Clients behind the same public IP share this quota. `meta.access.mode` is `ip_trial`, and `meta.usage.resets_at` shows when the window resets. The client has no trial logic of its own; the server decides.
|
|
195
202
|
|
|
196
|
-
|
|
203
|
+
If you configure a key and the server does not accept it, the request can fall back to the IP trial. By default the client then raises `UnexpectedAccessModeError`. The request has already been processed and may have used IP-trial credits. Check your key.
|
|
197
204
|
|
|
198
|
-
##
|
|
205
|
+
## Migrating from 1.x
|
|
199
206
|
|
|
200
|
-
|
|
207
|
+
You do not have to migrate. 1.x (V1 API) stays available and installable indefinitely, with no deprecation or shutdown planned. To stay on it:
|
|
201
208
|
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
"status": true,
|
|
205
|
-
"used_credits": 1,
|
|
206
|
-
"remaining_credits": 4999,
|
|
207
|
-
"expires": 1743659200,
|
|
208
|
-
"q": "michael.smith@example.com",
|
|
209
|
-
"name": "Michael",
|
|
210
|
-
"gender": "male",
|
|
211
|
-
"country": "US",
|
|
212
|
-
"total_names": 325,
|
|
213
|
-
"probability": 98,
|
|
214
|
-
"duration": "4ms"
|
|
215
|
-
}
|
|
209
|
+
```bash
|
|
210
|
+
gem install genderapi -v "~> 1.0"
|
|
216
211
|
```
|
|
217
212
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
### Response Fields
|
|
221
|
-
|
|
222
|
-
| Field | Type | Description |
|
|
223
|
-
|-------------------|--------------------|-----------------------------------------------------|
|
|
224
|
-
| status | Boolean | `true` or `false`. Check errors if false. |
|
|
225
|
-
| used_credits | Integer | Credits used for this request. |
|
|
226
|
-
| remaining_credits | Integer | Remaining credits on your package. |
|
|
227
|
-
| expires | Integer (timestamp)| Package expiration date (in seconds). |
|
|
228
|
-
| q | String | Your input query (name, email, or username). |
|
|
229
|
-
| name | String | Found name. |
|
|
230
|
-
| gender | Enum[String] | `"male"`, `"female"`, or `"null"`. |
|
|
231
|
-
| country | Enum[String] | Most likely country (e.g. `"US"`, `"DE"`, etc.). |
|
|
232
|
-
| total_names | Integer | Number of samples behind the prediction. |
|
|
233
|
-
| probability | Integer | Likelihood percentage (50-100). |
|
|
234
|
-
| duration | String | Processing time (e.g. `"4ms"`). |
|
|
235
|
-
|
|
236
|
-
---
|
|
237
|
-
|
|
238
|
-
## ⚠️ Error Codes
|
|
239
|
-
|
|
240
|
-
When `status` is `false`, check the following error codes:
|
|
241
|
-
|
|
242
|
-
| errno | errmsg | Description |
|
|
243
|
-
|-------|-----------------------------|-------------------------------------------------------------------|
|
|
244
|
-
| 50 | access denied | Unauthorized IP Address or Referrer. Check your access privileges. |
|
|
245
|
-
| 90 | invalid country code | Check supported country codes. [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) |
|
|
246
|
-
| 91 | name not set \|\| email not set | Missing `name` or `email` parameter on your request. |
|
|
247
|
-
| 92 | too many names \|\| too many emails | Limit is 100 for names, 50 for emails in one request. |
|
|
248
|
-
| 93 | limit reached | The API key credit has been finished. |
|
|
249
|
-
| 94 | invalid or missing key | The API key cannot be found. |
|
|
250
|
-
| 99 | API key has expired | Please renew your API key. |
|
|
251
|
-
|
|
252
|
-
Example error response:
|
|
253
|
-
|
|
254
|
-
```json
|
|
255
|
-
{
|
|
256
|
-
"status": false,
|
|
257
|
-
"errno": 94,
|
|
258
|
-
"errmsg": "invalid or missing key"
|
|
259
|
-
}
|
|
260
|
-
```
|
|
213
|
+
or in your Gemfile:
|
|
261
214
|
|
|
262
|
-
|
|
215
|
+
```ruby
|
|
216
|
+
gem "genderapi", "~> 1.0"
|
|
217
|
+
```
|
|
263
218
|
|
|
264
|
-
|
|
219
|
+
The 1.x source stays on the [`v1` branch](https://github.com/GenderAPI/genderapi-ruby/tree/v1).
|
|
265
220
|
|
|
266
|
-
|
|
221
|
+
V2 is a different request and response contract, so changing only the URL is not enough. Your API key and credit balance stay the same.
|
|
267
222
|
|
|
268
|
-
|
|
269
|
-
|
|
223
|
+
| 1.x (V1) | 2.x (V2) |
|
|
224
|
+
| --- | --- |
|
|
225
|
+
| `get_gender_by_name(name:)`, `get_gender_by_email(email:)`, `get_gender_by_username(username:)` | `client.name(value)`, `client.email(value)`, `client.username(value)` or `client.gender(type, value)` |
|
|
226
|
+
| V1 routes `/api`, `/api/email`, `/api/username` | `POST /api/v2/gender` with `type` and `value` |
|
|
227
|
+
| `get_gender_by_*_bulk(data:)` on `/api/*/multi/country` | `client.gender_batch(items)` -> `POST /api/v2/gender/batch` with `items` (1-50) |
|
|
228
|
+
| `ask_to_ai:` / `askToAI` | `ai_mode:` -> `options.ai_mode` (`off`, `fallback`, `always`). Single requests already default to `fallback`. |
|
|
229
|
+
| `force_to_genderize:` (name, username) | `force_to_genderize:` -> `forceToGenderize` for name, email and username; dataset first, then nickname-aware AI |
|
|
230
|
+
| Flat response fields (`q`, `name`, `gender`, ...) | `data` for the result and `meta` for access and billing |
|
|
231
|
+
| `probability` (percentage) | `data.confidence` (0-1) plus `data.confidence_kind`. AI scores are not calibrated probabilities. |
|
|
232
|
+
| `total_names` | `data.sample_count` (nullable) |
|
|
233
|
+
| `used_credits` / `remaining_credits` | `meta.usage.charged_credits` / `meta.usage.remaining_credits` |
|
|
234
|
+
| `expires` | `client.usage.data.expires_at` |
|
|
235
|
+
| `status: false` with `errno` / `errmsg` | HTTP status plus a Problem Details `code` and `action`, raised as `GenderAPI::APIError` |
|
|
236
|
+
| Generic `RuntimeError` on 5xx | Typed errors with `billing_status`, `request_id` and `retry_after` |
|
|
237
|
+
| HTTParty dependency | Standard library only |
|
|
238
|
+
| Ruby >= 2.6 | Ruby >= 3.0 |
|
|
270
239
|
|
|
271
|
-
|
|
272
|
-
[https://www.genderapi.io/determine-gender-from-email](https://www.genderapi.io/determine-gender-from-email)
|
|
240
|
+
## Documentation
|
|
273
241
|
|
|
274
|
-
-
|
|
275
|
-
|
|
242
|
+
- API documentation: https://www.genderapi.io/api-documentation
|
|
243
|
+
- V2 guides: [responses](https://www.genderapi.io/docs/v2/responses), [request parameters](https://www.genderapi.io/docs/v2/request-parameters), [AI options](https://www.genderapi.io/docs/v2/ai-options), [batch](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), [authentication](https://www.genderapi.io/docs/v2/authentication), [phone validation](https://www.genderapi.io/docs/v2/phone-validation), [migration](https://www.genderapi.io/docs/v2/migration)
|
|
244
|
+
- OpenAPI: https://api.genderapi.io/api/v2/openapi.json
|
|
276
245
|
|
|
277
|
-
|
|
246
|
+
## Development
|
|
278
247
|
|
|
279
|
-
|
|
248
|
+
```bash
|
|
249
|
+
bundle config set --local path vendor/bundle
|
|
250
|
+
bundle install
|
|
251
|
+
bundle exec rake test # local stub server only: no real API, no credits
|
|
252
|
+
gem build genderapi.gemspec
|
|
253
|
+
```
|
|
280
254
|
|
|
281
|
-
|
|
255
|
+
Test fixtures in `test/fixtures/openapi_examples.json` are the response examples from the V2 OpenAPI document.
|
|
282
256
|
|
|
283
|
-
|
|
257
|
+
### Releasing
|
|
284
258
|
|
|
285
|
-
|
|
259
|
+
Pushing a `v*` tag (for example `v2.0.0`) runs `.github/workflows/publish.yml`. The workflow tests the gem, checks that the tag matches `GenderAPI::VERSION`, builds it and pushes it to RubyGems. It authenticates through RubyGems trusted publishing (OIDC) configured for this repository and workflow, so no API key is stored.
|
|
286
260
|
|
|
287
|
-
##
|
|
261
|
+
## License
|
|
288
262
|
|
|
289
|
-
MIT
|
|
263
|
+
MIT
|