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;