- C#
- Python
- NodeJS
GeoPhone C# Rest Code Snippet
using System.Web;
namespace geophone_dot_net.REST
{
/// <summary>
/// Provides functionality to call the ServiceObjects GeoPhone REST API's GetPhoneInfo_V2 endpoint,
/// retrieving phone-related information (e.g., provider and contact details) with fallback to a backup endpoint
/// for reliability in live mode.
/// </summary>
public class GetPhoneInfoClient
{
private const string LiveBaseUrl = "https://sws.serviceobjects.com/GP/api.svc/";
private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/GP/api.svc/";
private const string TrailBaseUrl = "https://trial.serviceobjects.com/GP/api.svc/";
/// <summary>
/// Synchronously calls the GetPhoneInfo_V2 REST endpoint to retrieve phone information,
/// attempting the primary endpoint first and falling back to the backup if the response is invalid
/// (Error.Number == "4") in live mode.
/// </summary>
/// <param name="input">The input parameters including phone number, license key, is live and timeout.</param>
/// <returns>Deserialized <see cref="GPResponse"/>.</returns>
public static GPResponse Invoke(GetPhoneInfoInput input)
{
//Use query string parameters so missing/options fields don't break
//the URL as path parameters would.
string url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrailBaseUrl);
GPResponse response = Helper.HttpGet<GPResponse>(url, input.TimeoutSeconds);
// Fallback on error payload in live mode
if (input.IsLive && !IsValid(response))
{
var fallbackUrl = BuildUrl(input, BackupBaseUrl);
GPResponse fallbackResponse = Helper.HttpGet<GPResponse>(fallbackUrl, input.TimeoutSeconds);
return fallbackResponse;
}
return response;
}
/// <summary>
/// Asynchronously calls the GetPhoneInfo_V2 REST endpoint to retrieve phone information,
/// attempting the primary endpoint first and falling back to the backup if the response is invalid
/// (Error.Number == "4") in live mode.
/// </summary>
/// <param name="input">The input parameters including phone number, license key, is live and timeout.</param>
/// <returns>Deserialized <see cref="GPResponse"/>.</returns>
public static async Task<GPResponse> InvokeAsync(GetPhoneInfoInput input)
{
//Use query string parameters so missing/options fields don't break
//the URL as path parameters would.
string url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrailBaseUrl);
GPResponse response = await Helper.HttpGetAsync<GPResponse>(url, input.TimeoutSeconds).ConfigureAwait(false);
if (input.IsLive && !IsValid(response))
{
var fallbackUrl = BuildUrl(input, BackupBaseUrl);
GPResponse fallbackResponse = await Helper.HttpGetAsync<GPResponse>(fallbackUrl, input.TimeoutSeconds).ConfigureAwait(false);
return fallbackResponse;
}
return response;
}
// Build the full request URL, including URL-encoded query string
public static string BuildUrl(GetPhoneInfoInput input,string baseUrl)
{
var qs = $"json/GetPhoneInfo_V2?PhoneNumber={HttpUtility.UrlEncode(input.PhoneNumber)}" +
$"&LicenseKey={HttpUtility.UrlEncode(input.LicenseKey)}";
return baseUrl + qs;
}
private static bool IsValid(GPResponse response) => response?.Error == null || response.Error.Number != "4";
/// <summary>
/// Defines the input parameters for the GetPhoneInfo_V2 REST operation.
/// </summary>
/// <param name="PhoneNumber">The phone number to look up (e.g., "805-963-1700") - Required.</param>
/// <param name="LicenseKey">Service Objects GeoPhone license key. - Required</param>
/// <param name="IsLive">True for live (production+backup) endpoints; false for trial only. - Required.</param>
/// <param name="TimeoutSeconds">The timeout duration in seconds for the request (default: 15).</param>
public record GetPhoneInfoInput(
string PhoneNumber = "",
string LicenseKey = "",
bool IsLive = true,
int TimeoutSeconds = 15
);
}
}
using System.Runtime.Serialization;
namespace geophone_dot_net.REST
{
/// <summary>
/// Response object for the GeoPhone REST API, containing provider and contact information,
/// </summary>
[DataContract]
public class GPResponse
{
public Provider[] Providers { get; set; }
public Contact[] Contacts { get; set; }
public Error Error { get; set; }
public override string ToString()
{
string providers = Providers != null ? string.Join(", ", Providers.Select(p => p.ToString())) : "null";
string contacts = Contacts != null ? string.Join(", ", Contacts.Select(c => c.ToString())) : "null";
string error = Error != null ? Error.ToString() : "null";
return $"GP GPResponse: Providers = {providers}, Contacts = {contacts}, Error = {error}";
}
}
/// <summary>
/// Represents a provider's information in the GeoPhone REST API response.
/// </summary>
public class Provider
{
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string LineType { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public string Quality { get; set; }
public override string ToString()
{
return $"ProviderOutput: Name = {Name}, City = {City}, State = {State}, LineType = {LineType}, Latitude = {Latitude}, Longitude = {Longitude}, Quality = {Quality}";
}
}
/// <summary>
/// Represents a contact's information in the GeoPhone REST API response.
/// </summary>
public class Contact
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Type { get; set; }
public override string ToString()
{
return $"ContactOutput: Name = {Name}, Address = {Address}, City = {City}, State = {State}, Zip = {Zip}, Type = {Type}";
}
}
public class Error
{
public string Desc { get; set; }
public string Number { get; set; }
public string Location { get; set; }
public override string ToString()
{
return $"Desc: {Desc} " +
$"Number: {Number} " +
$"Location: {Location} ";
}
}
public class LF_Provider
{
public string Name { get; set; }
public string City { get; set; }
public string State { get; set; }
public string LineType { get; set; }
public override string ToString()
{
return $"Name: {Name}, City: {City}, State: {State}, LineType: {LineType}";
}
}
public class LF_Contact
{
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Type { get; set; }
public override string ToString()
{
return $"Name: {Name}, Address: {Address}, City: {City}, State: {State}, Zip: {Zip}, Type: {Type}";
}
}
public class LF_ResponseObject
{
public Provider[] Providers { get; set; }
public Contact[] Contacts { get; set; }
public Error Error { get; set; }
public override string ToString()
{
string providers = Providers?.Any() == true
? string.Join(", ", Providers.Select(p => p.ToString()))
: "None";
string contacts = Contacts?.Any() == true
? string.Join(", ", Contacts.Select(c => c.ToString()))
: "None";
return $"GP LF_Response: Providers: [{providers}], Contacts: [{contacts}], Error: {Error}";
}
}
}
using System.Text.Json;
using System.Web;
namespace geophone_dot_net.REST
{
public class Helper
{
public static T HttpGet<T>(string url, int timeoutSeconds)
{
using var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(timeoutSeconds)
};
using var request = new HttpRequestMessage(HttpMethod.Get, url);
using HttpResponseMessage response = httpClient
.SendAsync(request)
.GetAwaiter()
.GetResult();
response.EnsureSuccessStatusCode();
using Stream responseStream = response.Content
.ReadAsStreamAsync()
.GetAwaiter()
.GetResult();
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
object? obj = JsonSerializer.Deserialize(responseStream, typeof(T), options);
T result = (T)obj!;
return result;
}
// Asynchronous HTTP GET and JSON deserialize
public static async Task<T> HttpGetAsync<T>(string url, int timeoutSeconds)
{
HttpClient HttpClient = new HttpClient();
HttpClient.Timeout = TimeSpan.FromSeconds(timeoutSeconds);
using var httpResponse = await HttpClient.GetAsync(url).ConfigureAwait(false);
httpResponse.EnsureSuccessStatusCode();
var stream = await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
return JsonSerializer.Deserialize<T>(stream)!;
}
public static string UrlEncode(string value) => HttpUtility.UrlEncode(value ?? string.Empty);
}
}
GeoPhone Python Rest Code Snippet
import requests # HTTP client for RESTful API calls
from gp_response import GPResponse, Provider, Contact, Error
# Endpoint URLs for GP GetPhoneInfoLastFirst service
primary_url = 'https://sws.serviceobjects.com/GP/api.svc/json/GetPhoneInfoLastFirst?'
backup_url = 'https://swsbackup.serviceobjects.com/GP/api.svc/json/GetPhoneInfoLastFirst?'
trial_url = 'https://trial.serviceobjects.com/GP/api.svc/json/GetPhoneInfoLastFirst?'
def get_phone_info(phone_number: str, license_key: str, is_live: bool = True) -> GPResponse:
"""
Call GP GetPhoneInfo_V2 API to retrieve phone number information.
Parameters:
phone_number (str): Phone number to look up (e.g., '805-963-1700'). Required.
license_key (str): Service Objects license key. Required.
is_live (bool): True for production endpoints, False for trial URL. Defaults to True.
timeout_seconds (int): Timeout for API calls in seconds. Defaults to 15.
Returns:
dict: Parsed JSON response with phone information or error details.
"""
# Prepare query parameters for GP API
params = {
'PhoneNumber': phone_number,
'LicenseKey': license_key
}
# Select the base URL: production vs trial
url = primary_url if is_live else trial_url
# Attempt primary (or trial) endpoint first
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
# If API returned an error in JSON payload, trigger fallback
error = getattr(response, 'Error', None)
if not (error is None or getattr(error, 'Number', None) != "4"):
if is_live:
# Try backup URL when live
response = requests.get(backup_url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
# If still error, propagate exception
if 'Error' in data:
raise RuntimeError(f"GP service error: {data['Error']}")
else:
# Trial mode should not fallback; error is terminal
raise RuntimeError(f"GP trial error: {data['Error']}")
# Convert JSON response to GPResponse for structured access
error = Error(**data.get('Error', {})) if data.get('Error') else None
providers = [Provider(**prov) for prov in data.get('Providers', [])] if data.get('Providers') else None
contacts = [Contact(**cont) for cont in data.get('Contacts', [])] if data.get('Contacts') else None
response = GPResponse(providers=providers, contacts=contacts, error=error)
return response
except requests.RequestException as req_exc:
# Network or HTTP-level error occurred
if is_live:
try:
# Fallback to backup URL on network failure
response = requests.get(backup_url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if 'Error' in data:
raise RuntimeError(f"GP backup error: {data['Error']}") from req_exc
# Convert JSON response to GPResponse for structured access
error = Error(**data.get('Error', {})) if data.get('Error') else None
providers = [Provider(**prov) for prov in data.get('Providers', [])] if data.get('Providers') else None
contacts = [Contact(**cont) for cont in data.get('Contacts', [])] if data.get('Contacts') else None
response = GPResponse(providers=providers, contacts=contacts, error=error)
return response
except Exception as backup_exc:
# Both primary and backup failed; escalate
raise RuntimeError("GP service unreachable on both endpoints") from backup_exc
else:
# In trial mode, propagate the network exception
raise RuntimeError(f"GP trial error: {str(req_exc)}") from req_exc
from dataclasses import dataclass
from typing import Optional, List
@dataclass
class Provider:
"""Represents provider information returned by the GP API."""
Name: Optional[str] = None
City: Optional[str] = None
State: Optional[str] = None
LineType: Optional[str] = None
Latitude: Optional[str] = None
Longitude: Optional[str] = None
Quality: Optional[str] = None
def __str__(self) -> str:
return (f"ProviderOutput: Name = {self.Name}, City = {self.City}, State = {self.State}, "
f"LineType = {self.LineType}, Latitude = {self.Latitude}, Longitude = {self.Longitude}, "
f"Quality = {self.Quality}")
@dataclass
class Contact:
"""Represents contact information returned by the GP API."""
Name: Optional[str] = None
Address: Optional[str] = None
City: Optional[str] = None
State: Optional[str] = None
Zip: Optional[str] = None
Type: Optional[str] = None
def __str__(self) -> str:
return (f"ContactOutput: Name = {self.Name}, Address = {self.Address}, City = {self.City}, "
f"State = {self.State}, Zip = {self.Zip}, Type = {self.Type}")
@dataclass
class Error:
"""Represents error information returned by the GP API."""
Desc: Optional[str] = None
Number: Optional[str] = None
Location: Optional[str] = None
def __str__(self) -> str:
return f"Desc: {self.Desc} Number: {self.Number} Location: {self.Location}"
@dataclass
class GPResponse:
"""Represents the full response from the GP API."""
providers: Optional[List[Provider]] = None
contacts: Optional[List[Contact]] = None
error: Optional[Error] = None
def __str__(self) -> str:
providers_str = ", ".join(str(p) for p in self.providers) if self.providers else "null"
contacts_str = ", ".join(str(c) for c in self.contacts) if self.contacts else "null"
error_str = str(self.error) if self.error else "null"
return f"GP GPResponse: Providers = {providers_str}, Contacts = {contacts_str}, Error = {error_str}"
GeoPhone NodeJS Rest Code Snippet
import axios from 'axios';
import querystring from 'querystring';
import { GPResponse } from './gp-response.js';
/**
* @constant
* @type {string}
* @description The base URL for the live ServiceObjects GeoPhone API service.
*/
const LiveBaseUrl = 'https://sws.serviceobjects.com/GP/api.svc/';
/**
* @constant
* @type {string}
* @description The base URL for the backup ServiceObjects GeoPhone API service.
*/
const BackupBaseUrl = 'https://swsbackup.serviceobjects.com/GP/api.svc/';
/**
* @constant
* @type {string}
* @description The base URL for the trial ServiceObjects GeoPhone API service.
*/
const TrialBaseUrl = 'https://trial.serviceobjects.com/GP/api.svc/';
/**
* <summary>
* Checks if a response from the API is valid by verifying that it either has no Error object
* or the Error.Number is not equal to '4'.
* </summary>
* <param name="response" type="Object">The API response object to validate.</param>
* <returns type="boolean">True if the response is valid, false otherwise.</returns>
*/
const isValid = (response) => !response?.Error || response.Error.Number !== '4';
/**
* <summary>
* Constructs a full URL for the GetPhoneInfo_V2 API endpoint by combining the base URL
* with query parameters derived from the input object.
* </summary>
* <param name="phoneNumber" type="string">The phone number to query.</param>
* <param name="licenseKey" type="string">The license key for the API.</param>
* <param name="baseUrl" type="string">The base URL for the API service (live, backup, or trial).</param>
* <returns type="string">The constructed URL with query parameters.</returns>
*/
const buildUrl = (phoneNumber, licenseKey, baseUrl) =>
`${baseUrl}json/GetPhoneInfo_V2?${querystring.stringify({ PhoneNumber: phoneNumber, LicenseKey: licenseKey })}`;
/**
* <summary>
* Performs an HTTP GET request to the specified URL with a given timeout.
* </summary>
* <param name="url" type="string">The URL to send the GET request to.</param>
* <param name="timeoutSeconds" type="number">The timeout duration in seconds for the request.</param>
* <returns type="Promise<GPResponse>">A promise that resolves to a GPResponse object containing the API response data.</returns>
* <exception cref="Error">Thrown if the HTTP request fails, with a message detailing the error.</exception>
*/
const httpGet = async (url, timeoutSeconds) => {
try {
const response = await axios.get(url, { timeout: timeoutSeconds * 1000 });
return new GPResponse(response.data);
} catch (error) {
throw new Error(`HTTP request failed: ${error.message}`);
}
};
/**
* <summary>
* Provides functionality to call the ServiceObjects GeoPhone API's GetPhoneInfo_V2 endpoint,
* retrieving phone-related information with fallback to a backup endpoint for reliability in live mode.
* </summary>
*/
const GetPhoneInfoClient = {
/**
* <summary>
* Asynchronously invokes the GetPhoneInfo_V2 API endpoint, attempting the primary endpoint
* first and falling back to the backup if the response is invalid (Error.Number == "4") in live mode.
* </summary>
* <param name="phoneNumber" type="string">The phone number to query.</param>
* <param name="licenseKey" type="string">The license key for the API.</param>
* <param name="isLive" type="boolean">Whether to use live or trial endpoints.</param>
* <param name="timeoutSeconds" type="number">The timeout duration in seconds for the request.</param>
* <returns type="Promise<GPResponse>">A promise that resolves to a GPResponse object with provider and contact details or an error.</returns>
* <exception cref="Error">Thrown if both primary and backup endpoints fail, with details of the failure.</exception>
*/
async invokeAsync(phoneNumber, licenseKey, isLive, timeoutSeconds) {
const url = buildUrl(phoneNumber, licenseKey, isLive ? LiveBaseUrl : TrialBaseUrl);
let response = await httpGet(url, timeoutSeconds || 15);
if (isLive && !isValid(response)) {
const fallbackUrl = buildUrl(phoneNumber, licenseKey, BackupBaseUrl);
const fallbackResponse = await httpGet(fallbackUrl, timeoutSeconds || 15);
return fallbackResponse;
}
return response;
},
/**
* <summary>
* Synchronously invokes the GetPhoneInfo_V2 API endpoint by wrapping the async call
* and awaiting its result immediately.
* </summary>
* <param name="phoneNumber" type="string">The phone number to query.</param>
* <param name="licenseKey" type="string">The license key for the API.</param>
* <param name="isLive" type="boolean">Whether to use live or trial endpoints.</param>
* <param name="timeoutSeconds" type="number">The timeout duration in seconds for the request.</param>
* <returns type="GPResponse">A GPResponse object with provider and contact details or an error.</returns>
* <exception cref="Error">Thrown if both primary and backup endpoints fail, with details of the failure.</exception>
*/
invoke(phoneNumber, licenseKey, isLive, timeoutSeconds) {
return (async () => await this.invokeAsync(phoneNumber, licenseKey, isLive, timeoutSeconds))();
},
};
export { GetPhoneInfoClient, GPResponse };
export class Provider {
constructor(data) {
this.Name = data.Name;
this.City = data.City;
this.State = data.State;
this.LineType = data.LineType;
this.Latitude = data.Latitude;
this.Longitude = data.Longitude;
this.Quality = data.Quality;
}
toString() {
return `ProviderOutput: Name = ${this.Name}, City = ${this.City}, State = ${this.State}, LineType = ${this.LineType}, Latitude = ${this.Latitude}, Longitude = ${this.Longitude}, Quality = ${this.Quality}`;
}
}
export class Contact {
constructor(data) {
this.Name = data.Name;
this.Address = data.Address;
this.City = data.City;
this.State = data.State;
this.Zip = data.Zip;
this.Type = data.Type;
}
toString() {
return `ContactOutput: Name = ${this.Name}, Address = ${this.Address}, City = ${this.City}, State = ${this.State}, Zip = ${this.Zip}, Type = ${this.Type}`;
}
}
export class Error {
constructor(data) {
this.Desc = data.Desc;
this.Number = data.Number;
this.Location = data.Location;
}
toString() {
return `Desc: ${this.Desc} Number: ${this.Number} Location: ${this.Location}`;
}
}
export class GPResponse {
constructor(data) {
this.Providers = (data.Providers || []).map(p => new Provider(p));
this.Contacts = (data.Contacts || []).map(c => new Contact(c));
this.Error = data.Error ? new Error(data.Error) : null;
}
toString() {
const providers = this.Providers.length ? this.Providers.map(p => p.toString()).join(', ') : 'null';
const contacts = this.Contacts.length ? this.Contacts.map(c => c.toString()).join(', ') : 'null';
const error = this.Error ? this.Error.toString() : 'null';
return `GPResponse: Providers = ${providers}, Contacts = ${contacts}, Error = ${error}`;
}
}
export default GPResponse;