Validate phone structure with GenderAPI.io V2 using number and country. Read data.valid, data.possible, formatting fields and usage metadata.
Validate and format a phone number
Parameter
Type
Required
Default
Description
Parameternumber
Typestring
RequiredYes
Default—
DescriptionRequired, 3–32 characters. ASCII digits, spaces, parentheses and hyphens, with an optional leading +. Extensions and alphabetic characters are not accepted.
Parametercountry
Typestring
RequiredConditional
Default—
DescriptionUppercase ISO 3166-1 alpha-2 code. Required for a national number; optional when number starts with +. Omit if unused.
Send number and optional country to POST https://api.genderapi.io/api/v2/phone/validate. Use an international number beginning with +, or supply an uppercase ISO country for a national number. GenderAPI.io V2 returns data.valid, data.possible, data.e164, data.country and data.country_calling_code, plus meta for access and billing. This checks number structure, not subscriber existence. A completed validation costs 1 credit, including invalid results. The synthetic response below illustrates an invalid number, independently of the request example.
Choose your language.Set up your API key, then run the example on your server.
Every prediction request is a new billable operation, including retries. These examples do not automatically retry. Check billing status before sending another request.
Before you run: access and error handling
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.
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.
// 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 = { "number": "+905321234567"};const response = await fetch("https://api.genderapi.io/api/v2/phone/validate", { 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.}
Node.js 22+; built-in fetch. Save as example.mjs and run node example.mjs. Runtime documentation
Python
import jsonimport osimport sysimport urllib.errorimport urllib.requestapi_key = os.environ.get("GENDERAPI_API_KEY")if not api_key: raise RuntimeError("Set GENDERAPI_API_KEY")body = json.loads("{\"number\":\"+905321234567\"}")class NoRedirect(urllib.request.HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): return Nonerequest = urllib.request.Request( "https://api.genderapi.io/api/v2/phone/validate", 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.
Python 3.10+; standard library. Save as example.py and run python3 example.py. Runtime documentation
PHP 8+ with the cURL extension. Save as example.php and run php example.php. Runtime documentation
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 = "{\"number\":\"+905321234567\"}"; 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/phone/validate")) .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. }}
Java 17+; standard HTTP client. Save as GenderApiExample.java and run java GenderApiExample.java. Runtime documentation
C# / .NET
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/phone/validate");request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);request.Content = new StringContent("{\"number\":\"+905321234567\"}", 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.}
.NET 8+ console application. Use as Program.cs in a console project, then run dotnet run. Runtime documentation
Go
package mainimport ( "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 := "{\"number\":\"+905321234567\"}" request, err := http.NewRequest("POST", "https://api.genderapi.io/api/v2/phone/validate", 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) }}
Go 1.22+; standard library. Save as main.go and run go run main.go. Runtime documentation
A 422 response rejects malformed input before billing. A well-formed request whose number cannot be parsed can complete with HTTP 200, valid: false and null formatting fields; that completed validation costs 1 credit. A parseable but invalid number may still have an e164 value. Check valid rather than treating formatted output as proof of validity.
Field
Type
Nullable
Example
Description
Fielddata.valid
Typeboolean
NullableNo
Examplefalse
DescriptionWhether the number matches the numbering-plan validation rules.
Fielddata.possible
Typeboolean
NullableNo
Examplefalse
DescriptionWhether the number has a plausible length for its numbering plan; weaker than valid.
Fielddata.e164
Typestring
NullableYes
Examplenull
DescriptionInternational formatted number when parsing succeeds, otherwise null.
Fielddata.country
Typestring
NullableYes
Examplenull
DescriptionRegion derived from the number, or null when unavailable; it does not locate the subscriber.
Fielddata.country_calling_code
Typeinteger
NullableYes
Examplenull
DescriptionNumeric international calling code, or null when unavailable.
Access, billing and request metadata
Field
Type
Nullable
Example
Description
Fieldmeta.request_id
Typestring
NullableNo
Example"550e8400-e29b-41d4-a716-446655440000"
DescriptionUnique identifier for this HTTP attempt; also sent in X-Request-ID.
Fieldmeta.duration_ms
Typeinteger
NullableNo
Example42
DescriptionRequest processing time in milliseconds.
Fieldmeta.access.mode
Typestring
NullableNo
Example"api_key"
Descriptionapi_key, ip_trial or unauthenticated. Verify api_key when using a paid account.
Fieldmeta.access.reason
Typestring
NullableYes
Examplenull
DescriptionTrial fallback reason: api_key_missing, api_key_invalid or api_key_not_found; null otherwise.
Fieldmeta.usage.charged_credits
Typeinteger
NullableYes
Example1
DescriptionNet credits charged. Zero before charging or after a full confirmed refund; null when billing is unconfirmed.
Fieldmeta.usage.remaining_credits
Typeinteger
NullableYes
Example99
DescriptionBalance when the operation completes. May be negative; null when unavailable or billing is unconfirmed.
Fieldmeta.usage.billing_status
Typestring
NullableNo
Example"confirmed"
Descriptionnot_charged, confirmed or unconfirmed. Check this before retrying a failed prediction.
Fieldmeta.usage.resets_at
Typestring
NullableYes
Example"2026-09-27T12:00:00.000Z"
DescriptionIP trial reset time in UTC (ISO 8601); null for API-key accounts or when unknown.
Fieldmeta.usage.limit
Typeinteger
NullableYes
Example10
DescriptionIP trial allowance; null for API-key accounts.
Fieldmeta.usage.period_seconds
Typeinteger
NullableYes
Example86400
DescriptionIP trial window in seconds; null for API-key accounts.
Authentication and retries
Send your existing Bearer API key and Content-Type: application/json. The code examples read GENDERAPI_API_KEY from the environment. National numbers require country; this field is optional when number begins with +.