Address Insight C# Rest Code Snippet

using System.Web;

namespace address_insight_us_dot_net.REST
{
    /// <summary>
    /// Provides functionality to call the ServiceObjects AddressInsight (AIN) REST API's GetAddressInsight endpoint,
    /// retrieving address validation, geocoding, and demographic information for a given US address
    /// with fallback to a backup endpoint for reliability in live mode.
    /// </summary>
    public class GetAddressInsightClient
    {
        // Base URL constants: production, backup, and trial
        private const string LiveBaseUrl = "https://sws.serviceobjects.com/ain/api.svc/json/";
        private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/ain/api.svc/json/";
        private const string TrialBaseUrl = "https://trial.serviceobjects.com/ain/api.svc/json/";

        /// <summary>
        /// Synchronously calls the GetAddressInsight REST endpoint to retrieve address insight information,
        /// attempting the primary endpoint first and falling back to the backup if the response is invalid
        /// (Error.TypeCode == "3") in live mode.
        /// </summary>
        /// <param name="input">The input parameters including business name, address, city, state, zip, test type, and license key.</param>
        /// <returns>Deserialized <see cref="AINResponse"/> containing address insight data or an error.</returns>
        public static AINResponse Invoke(GetAddressInsightInput input)
        {
            // Use query string parameters so missing/optional fields don't break the URL
            string url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            AINResponse response = Helper.HttpGet<AINResponse>(url, input.TimeoutSeconds);

            // Fallback on error in live mode
            if (input.IsLive && !IsValid(response))
            {
                string fallbackUrl = BuildUrl(input, BackupBaseUrl);
                AINResponse fallbackResponse = Helper.HttpGet<AINResponse>(fallbackUrl, input.TimeoutSeconds);
                return fallbackResponse;
            }

            return response;
        }

        /// <summary>
        /// Asynchronously calls the GetAddressInsight REST endpoint to retrieve address insight information,
        /// attempting the primary endpoint first and falling back to the backup if the response is invalid
        /// (Error.TypeCode == "3") in live mode.
        /// </summary>
        /// <param name="input">The input parameters including business name, address, city, state, zip, test type, and license key.</param>
        /// <returns>Deserialized <see cref="AINResponse"/> containing address insight data or an error.</returns>
        public static async Task<AINResponse> InvokeAsync(GetAddressInsightInput input)
        {
            // Use query string parameters so missing/optional fields don't break the URL
            string url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            AINResponse response = await Helper.HttpGetAsync<AINResponse>(url, input.TimeoutSeconds).ConfigureAwait(false);

            // Fallback on error in live mode
            if (input.IsLive && !IsValid(response))
            {
                string fallbackUrl = BuildUrl(input, BackupBaseUrl);
                AINResponse fallbackResponse = await Helper.HttpGetAsync<AINResponse>(fallbackUrl, input.TimeoutSeconds).ConfigureAwait(false);
                return fallbackResponse;
            }

            return response;
        }

        // Build the full request URL, including URL-encoded query string
        private static string BuildUrl(GetAddressInsightInput input, string baseUrl)
        {
            // Construct query string with URL-encoded parameters
            string qs = $"GetAddressInsight?" +
                        $"BusinessName={Helper.UrlEncode(input.BusinessName)}" +
                        $"&Address1={Helper.UrlEncode(input.Address1)}" +
                        $"&Address2={Helper.UrlEncode(input.Address2)}" +
                        $"&City={Helper.UrlEncode(input.City)}" +
                        $"&State={Helper.UrlEncode(input.State)}" +
                        $"&Zip={Helper.UrlEncode(input.Zip)}" +
                        $"&TestType={Helper.UrlEncode(input.TestType)}" +
                        $"&LicenseKey={Helper.UrlEncode(input.LicenseKey)}";
            return baseUrl + qs;
        }

        private static bool IsValid(AINResponse response) => response?.Error == null || response.Error.TypeCode != "3";

        /// <summary>
        /// Input parameters for the GetAddressInsight API call. Represents a US address to retrieve address insights
        /// with cascading logic for partial matches.
        /// </summary>
        /// <param name="BusinessName">A company name for a business, can provide additional insight or append a SuiteLink value. Optional.</param>
        /// <param name="Address1">Address line 1 of the contact or business address (e.g., "123 Main Street").</param>
        /// <param name="Address2">Address line 2 of the contact or business address (e.g., "Apt 4B"). Optional.</param>
        /// <param name="City">The city of the address (e.g., "New York"). Optional if zip is provided.</param>
        /// <param name="State">The state of the address (e.g., "NY"). Optional if zip is provided.</param>
        /// <param name="Zip">The ZIP code of the address. Optional if city and state are provided.</param>
        /// <param name="TestType">The test type, either empty or "census_loose" for best possible match based on census data. Optional.</param>
        /// <param name="LicenseKey">The license key to authenticate the API request.</param>
        /// <param name="IsLive">Indicates whether to use the live service (true) or trial service (false).</param>
        /// <param name="TimeoutSeconds">Timeout duration for the API call, in seconds.</param>
        public record GetAddressInsightInput(
            string BusinessName = "",
            string Address1 = "",
            string Address2 = "",
            string City = "",
            string State = "",
            string Zip = "",
            string TestType = "",
            string LicenseKey = "",
            bool IsLive = true,
            int TimeoutSeconds = 15
        );
    }
}



/// <summary>
/// This is the return from the AIN endpoint
/// </summary>
///
public class AINResponse
{
    public string Status { get; set; }
    public string StatusScore { get; set; }
    public string AddressStatus { get; set; }
    public string DPV { get; set; }
    public string DPVDesc { get; set; }
    public string Address { get; set; }
    public string AddressExtra { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    public string BarcodeDigits { get; set; }
    public string CarrierRoute { get; set; }
    public string CongressCode { get; set; }
    public string CountyCode { get; set; }
    public string CountyName { get; set; }
    public string FragmentHouse { get; set; }
    public string FragmentPreDir { get; set; }
    public string FragmentStreet { get; set; }
    public string FragmentSuffix { get; set; }
    public string FragmentPostDir { get; set; }
    public string FragmentUnit { get; set; }
    public string FragmentUnitNumber { get; set; }
    public string Fragment { get; set; }
    public string FragmentPMBPrefix { get; set; }
    public string FragmentPMBNumber { get; set; }
    public string Corrections { get; set; }
    public string CorrectionsDesc { get; set; }
    public string AddressNotes { get; set; }
    public string AddressNotesCodes { get; set; }
    public string GeocodeStatus { get; set; }
    public string LocationLatitude { get; set; }
    public string LocationLongitude { get; set; }
    public string CensusTract { get; set; }
    public string CensusBlock { get; set; }
    public string PlaceName { get; set; }
    public string ClassFP { get; set; }
    public string SLDUST { get; set; }
    public string SLDLST { get; set; }
    public string CountyFIPS { get; set; }
    public string StateFIPS { get; set; }
    public string GeocodeNotes { get; set; }
    public string GeocodeNotesCodes { get; set; }
    public string ZipStatus { get; set; }
    public string ZipLatitude { get; set; }
    public string ZipLongitude { get; set; }
    public string CityType { get; set; }
    public string CityAliasName { get; set; }
    public string AreaCode { get; set; }
    public string TimeZone { get; set; }
    public string DaylightSaving { get; set; }
    public string MSA { get; set; }
    public string CBSA { get; set; }
    public string CBSA_Div { get; set; }
    public string PMSA { get; set; }
    public string DMA { get; set; }
    public string ZipHouseholdValue { get; set; }
    public string ZipPersonsPerHousehold { get; set; }
    public string ZipHouseholdIncome { get; set; }
    public string CountyHouseholdIncome { get; set; }
    public string StateHouseholdIncome { get; set; }
    public string ZipNotes { get; set; }
    public string ZipNotesCodes { get; set; }
    public string Debug { get; set; }
    public Error Error { get; set; }
    public override string ToString()
    {
        return $"\n{{\n\tStatus: {Status}\n\t" +
            $"StatusScore: {StatusScore}\n\t" +
            $"AddressStatus: {AddressStatus}\n\t" +
            $"DPV: {DPV}\n\t" +
            $"DPVDesc: {DPVDesc}\n\t" +
            $"Address: {Address}\n\t" +
            $"AddressExtra: {AddressExtra}\n\t" +
            $"City: {City}\n\t" +
            $"State: {State}\n\t" +
            $"Zip: {Zip}\n\t" +
            $"BarcodeDigits: {BarcodeDigits}\n\t" +
            $"CarrierRoute: {CarrierRoute}\n\t" +
            $"CongressCode: {CongressCode}\n\t" +
            $"CountyCode: {CountyCode}\n\t" +
            $"CountyName: {CountyName}\n\t" +
            $"FragmentHouse: {FragmentHouse}\n\t" +
            $"FragmentPreDir: {FragmentPreDir}\n\t" +
            $"FragmentStreet: {FragmentStreet}\n\t" +
            $"FragmentSuffix: {FragmentSuffix}\n\t" +
            $"FragmentPostDir: {FragmentPostDir}\n\t" +
            $"FragmentUnit: {FragmentUnit}\n\t" +
            $"FragmentUnitNumber: {FragmentUnitNumber}\n\t" +
            $"FragmentPMBPrefix: {FragmentPMBPrefix}\n\t" +
            $"FragmentPMBNumber: {FragmentPMBNumber}\n\t" +
            $"Corrections: {Corrections}\n\t" +
            $"CorrectionsDesc: {CorrectionsDesc}\n\t" +
            $"AddressNotes: {AddressNotes}\n\t" +
            $"AddressNotesCodes: {AddressNotesCodes}\n\t" +
            $"GeocodeStatus: {GeocodeStatus}\n\t" +
            $"LocationLatitude: {LocationLatitude}\n\t" +
            $"LocationLongitude: {LocationLongitude}\n\t" +
            $"CensusTract: {CensusTract}\n\t" +
            $"CensusBlock: {CensusBlock}\n\t" +
            $"PlaceName: {PlaceName}\n\t" +
            $"ClassFP: {ClassFP}\n\t" +
            $"SLDUST: {SLDUST}\n\t" +
            $"SLDLST: {SLDLST}\n\t" +
            $"CountyFIPS: {CountyFIPS}\n\t" +
            $"StateFIPS: {StateFIPS}\n\t" +
            $"GeocodeNotes: {GeocodeNotes}\n\t" +
            $"GeocodeNotesCodes: {GeocodeNotesCodes}\n\t" +
            $"ZipStatus: {ZipStatus}\n\t" +
            $"ZipLatitude: {ZipLatitude}\n\t" +
            $"ZipLongitude: {ZipLongitude}\n\t" +
            $"CityType: {CityType}\n\t" +
            $"CityAliasName: {CityAliasName}\n\t" +
            $"AreaCode: {AreaCode}\n\t" +
            $"TimeZone: {TimeZone}\n\t" +
            $"DaylightSaving: {DaylightSaving}\n\t" +
            $"MSA: {MSA}\n\t" +
            $"CBSA: {CBSA}\n\t" +
            $"CBSA_Div: {CBSA_Div}\n\t" +
            $"PMSA: {PMSA}\n\t" +
            $"DMA: {DMA}\n\t" +
            $"ZipHouseholdValue: {ZipHouseholdValue}\n\t" +
            $"ZipPersonsPerHousehold: {ZipPersonsPerHousehold}\n\t" +
            $"ZipHouseholdIncome: {ZipHouseholdIncome}\n\t" +
            $"CountyHouseholdIncome: {CountyHouseholdIncome}\n\t" +
            $"StateHouseholdIncome: {StateHouseholdIncome}\n\t" +
            $"ZipNotes: {ZipNotes}\n\t" +
            $"ZipNotesCodes: {ZipNotesCodes}\n\t" +
            $"Error: {{{Error}}}\n}}";
    }
}

public class Error
{
    public string Type { get; set; }
    public string TypeCode { get; set; }
    public string Desc { get; set; }
    public string DescCode { get; set; }
    public override string ToString()
    {
        return $"Type: {Type} " +
            $"TypeCode: {TypeCode} " +
            $"Desc: {Desc} " +
            $"DescCode: {DescCode} ";
    }
}

using System.Text.Json;
using System.Web;

namespace address_insight_us_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);
    }
}

Address Insight Python Rest Code Snippet

from ain_response import AINResponse, Error
import requests

# Endpoint URLs for ServiceObjects AddressInsight (AIN) API
primary_url = "https://sws.serviceobjects.com/ain/api.svc/json/GetAddressInsight?"
backup_url = "https://swsbackup.serviceobjects.com/ain/api.svc/json/GetAddressInsight?"
trial_url = "https://trial.serviceobjects.com/ain/api.svc/json/GetAddressInsight?"

def get_address_insight(
    business_name: str,
    address1: str,
    address2: str,
    city: str,
    state: str,
    zip_code: str,
    test_type: str,
    license_key: str,
    is_live: bool = True
) -> AINResponse:
    """
    Call ServiceObjects AddressInsight (AIN) API's GetAddressInsight endpoint
    to retrieve address validation, geocoding, and demographic information for a given US address.

    Parameters:
        business_name: A company name for a business, can provide additional insight or append a SuiteLink value. Optional.
        address1: Address line 1 of the contact or business address (e.g., "123 Main Street").
        address2: Address line 2 of the contact or business address (e.g., "Apt 4B"). Optional.
        city: The city of the address (e.g., "New York"). Optional if zip is provided.
        state: The state of the address (e.g., "NY"). Optional if zip is provided.
        zip_code: The ZIP code of the address. Optional if city and state are provided.
        test_type: The test type, either empty or "census_loose" for best possible match based on census data. Optional.
        license_key: Your ServiceObjects license key.
        is_live: Use live or trial servers.

    Returns:
        AINResponse: Parsed JSON response with address insight results or error details.

    Raises:
        RuntimeError: If the API returns an error payload.
        requests.RequestException: On network/HTTP failures (trial mode).
    """
    params = {
        "BusinessName": business_name,
        "Address1": address1,
        "Address2": address2,
        "City": city,
        "State": state,
        "Zip": zip_code,
        "TestType": test_type,
        "LicenseKey": license_key,
    }
    # Select the base URL: production vs trial
    url = primary_url if is_live else trial_url

    try:
        # Attempt primary (or trial) endpoint
        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 = data.get('Error')
        if not (error is None or error.get('TypeCode') != "3"):
            if is_live:
                # Try backup URL
                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"AddressInsight service error: {data['Error']}")
            else:
                # Trial mode error is terminal
                raise RuntimeError(f"AddressInsight trial error: {data['Error']}")

        # Convert JSON response to AINResponse for structured access
        error = Error(**data.get("Error", {})) if data.get("Error") else None

        return AINResponse(
            Status=data.get("Status"),
            StatusScore=data.get("StatusScore"),
            AddressStatus=data.get("AddressStatus"),
            DPV=data.get("DPV"),
            DPVDesc=data.get("DPVDesc"),
            Address=data.get("Address"),
            AddressExtra=data.get("AddressExtra"),
            City=data.get("City"),
            State=data.get("State"),
            Zip=data.get("Zip"),
            BarcodeDigits=data.get("BarcodeDigits"),
            CarrierRoute=data.get("CarrierRoute"),
            CongressCode=data.get("CongressCode"),
            CountyCode=data.get("CountyCode"),
            CountyName=data.get("CountyName"),
            FragmentHouse=data.get("FragmentHouse"),
            FragmentPreDir=data.get("FragmentPreDir"),
            FragmentStreet=data.get("FragmentStreet"),
            FragmentSuffix=data.get("FragmentSuffix"),
            FragmentPostDir=data.get("FragmentPostDir"),
            FragmentUnit=data.get("FragmentUnit"),
            FragmentUnitNumber=data.get("FragmentUnitNumber"),
            Fragment=data.get("Fragment"),
            FragmentPMBPrefix=data.get("FragmentPMBPrefix"),
            FragmentPMBNumber=data.get("FragmentPMBNumber"),
            Corrections=data.get("Corrections"),
            CorrectionsDesc=data.get("CorrectionsDesc"),
            AddressNotes=data.get("AddressNotes"),
            AddressNotesCodes=data.get("AddressNotesCodes"),
            GeocodeStatus=data.get("GeocodeStatus"),
            LocationLatitude=data.get("LocationLatitude"),
            LocationLongitude=data.get("LocationLongitude"),
            CensusTract=data.get("CensusTract"),
            CensusBlock=data.get("CensusBlock"),
            PlaceName=data.get("PlaceName"),
            ClassFP=data.get("ClassFP"),
            SLDUST=data.get("SLDUST"),
            SLDLST=data.get("SLDLST"),
            CountyFIPS=data.get("CountyFIPS"),
            StateFIPS=data.get("StateFIPS"),
            GeocodeNotes=data.get("GeocodeNotes"),
            GeocodeNotesCodes=data.get("GeocodeNotesCodes"),
            ZipStatus=data.get("ZipStatus"),
            ZipLatitude=data.get("ZipLatitude"),
            ZipLongitude=data.get("ZipLongitude"),
            CityType=data.get("CityType"),
            CityAliasName=data.get("CityAliasName"),
            AreaCode=data.get("AreaCode"),
            TimeZone=data.get("TimeZone"),
            DaylightSaving=data.get("DaylightSaving"),
            MSA=data.get("MSA"),
            CBSA=data.get("CBSA"),
            CBSA_Div=data.get("CBSA_Div"),
            PMSA=data.get("PMSA"),
            DMA=data.get("DMA"),
            ZipHouseholdValue=data.get("ZipHouseholdValue"),
            ZipPersonsPerHousehold=data.get("ZipPersonsPerHousehold"),
            ZipHouseholdIncome=data.get("ZipHouseholdIncome"),
            CountyHouseholdIncome=data.get("CountyHouseholdIncome"),
            StateHouseholdIncome=data.get("StateHouseholdIncome"),
            ZipNotes=data.get("ZipNotes"),
            ZipNotesCodes=data.get("ZipNotesCodes"),
            Debug=data.get("Debug", []),
            Error=error
        )

    except requests.RequestException as req_exc:
        # Network or HTTP-level error occurred
        if is_live:
            try:
                # Fallback to backup URL
                response = requests.get(backup_url, params=params, timeout=10)
                response.raise_for_status()
                data = response.json()
                if "Error" in data:
                    raise RuntimeError(f"AddressInsight backup error: {data['Error']}") from req_exc

                error = Error(**data.get("Error", {})) if data.get("Error") else None

                return AINResponse(
                    Status=data.get("Status"),
                    StatusScore=data.get("StatusScore"),
                    AddressStatus=data.get("AddressStatus"),
                    DPV=data.get("DPV"),
                    DPVDesc=data.get("DPVDesc"),
                    Address=data.get("Address"),
                    AddressExtra=data.get("AddressExtra"),
                    City=data.get("City"),
                    State=data.get("State"),
                    Zip=data.get("Zip"),
                    BarcodeDigits=data.get("BarcodeDigits"),
                    CarrierRoute=data.get("CarrierRoute"),
                    CongressCode=data.get("CongressCode"),
                    CountyCode=data.get("CountyCode"),
                    CountyName=data.get("CountyName"),
                    FragmentHouse=data.get("FragmentHouse"),
                    FragmentPreDir=data.get("FragmentPreDir"),
                    FragmentStreet=data.get("FragmentStreet"),
                    FragmentSuffix=data.get("FragmentSuffix"),
                    FragmentPostDir=data.get("FragmentPostDir"),
                    FragmentUnit=data.get("FragmentUnit"),
                    FragmentUnitNumber=data.get("FragmentUnitNumber"),
                    Fragment=data.get("Fragment"),
                    FragmentPMBPrefix=data.get("FragmentPMBPrefix"),
                    FragmentPMBNumber=data.get("FragmentPMBNumber"),
                    Corrections=data.get("Corrections"),
                    CorrectionsDesc=data.get("CorrectionsDesc"),
                    AddressNotes=data.get("AddressNotes"),
                    AddressNotesCodes=data.get("AddressNotesCodes"),
                    GeocodeStatus=data.get("GeocodeStatus"),
                    LocationLatitude=data.get("LocationLatitude"),
                    LocationLongitude=data.get("LocationLongitude"),
                    CensusTract=data.get("CensusTract"),
                    CensusBlock=data.get("CensusBlock"),
                    PlaceName=data.get("PlaceName"),
                    ClassFP=data.get("ClassFP"),
                    SLDUST=data.get("SLDUST"),
                    SLDLST=data.get("SLDLST"),
                    CountyFIPS=data.get("CountyFIPS"),
                    StateFIPS=data.get("StateFIPS"),
                    GeocodeNotes=data.get("GeocodeNotes"),
                    GeocodeNotesCodes=data.get("GeocodeNotesCodes"),
                    ZipStatus=data.get("ZipStatus"),
                    ZipLatitude=data.get("ZipLatitude"),
                    ZipLongitude=data.get("ZipLongitude"),
                    CityType=data.get("CityType"),
                    CityAliasName=data.get("CityAliasName"),
                    AreaCode=data.get("AreaCode"),
                    TimeZone=data.get("TimeZone"),
                    DaylightSaving=data.get("DaylightSaving"),
                    MSA=data.get("MSA"),
                    CBSA=data.get("CBSA"),
                    CBSA_Div=data.get("CBSA_Div"),
                    PMSA=data.get("PMSA"),
                    DMA=data.get("DMA"),
                    ZipHouseholdValue=data.get("ZipHouseholdValue"),
                    ZipPersonsPerHousehold=data.get("ZipPersonsPerHousehold"),
                    ZipHouseholdIncome=data.get("ZipHouseholdIncome"),
                    CountyHouseholdIncome=data.get("CountyHouseholdIncome"),
                    StateHouseholdIncome=data.get("StateHouseholdIncome"),
                    ZipNotes=data.get("ZipNotes"),
                    ZipNotesCodes=data.get("ZipNotesCodes"),
                    Debug=data.get("Debug", []),
                    Error=error
                )
            except Exception as backup_exc:
                raise RuntimeError("AddressInsight service unreachable on both endpoints") from backup_exc
        else:
            raise RuntimeError(f"AddressInsight trial error: {str(req_exc)}") from req_exc



from dataclasses import dataclass
from typing import Optional, List

@dataclass
class Error:
    Type: Optional[str] = None
    TypeCode: Optional[str] = None
    Desc: Optional[str] = None
    DescCode: Optional[str] = None

    def __str__(self) -> str:
        return f"Error: Type={self.Type}, TypeCode={self.TypeCode}, Desc={self.Desc}, DescCode={self.DescCode}"

@dataclass
class AINResponse:
    Status: Optional[str] = None
    StatusScore: Optional[str] = None
    AddressStatus: Optional[str] = None
    DPV: Optional[str] = None
    DPVDesc: Optional[str] = None
    Address: Optional[str] = None
    AddressExtra: Optional[str] = None
    City: Optional[str] = None
    State: Optional[str] = None
    Zip: Optional[str] = None
    BarcodeDigits: Optional[str] = None
    CarrierRoute: Optional[str] = None
    CongressCode: Optional[str] = None
    CountyCode: Optional[str] = None
    CountyName: Optional[str] = None
    FragmentHouse: Optional[str] = None
    FragmentPreDir: Optional[str] = None
    FragmentStreet: Optional[str] = None
    FragmentSuffix: Optional[str] = None
    FragmentPostDir: Optional[str] = None
    FragmentUnit: Optional[str] = None
    FragmentUnitNumber: Optional[str] = None
    Fragment: Optional[str] = None
    FragmentPMBPrefix: Optional[str] = None
    FragmentPMBNumber: Optional[str] = None
    Corrections: Optional[str] = None
    CorrectionsDesc: Optional[str] = None
    AddressNotes: Optional[str] = None
    AddressNotesCodes: Optional[str] = None
    GeocodeStatus: Optional[str] = None
    LocationLatitude: Optional[str] = None
    LocationLongitude: Optional[str] = None
    CensusTract: Optional[str] = None
    CensusBlock: Optional[str] = None
    PlaceName: Optional[str] = None
    ClassFP: Optional[str] = None
    SLDUST: Optional[str] = None
    SLDLST: Optional[str] = None
    CountyFIPS: Optional[str] = None
    StateFIPS: Optional[str] = None
    GeocodeNotes: Optional[str] = None
    GeocodeNotesCodes: Optional[str] = None
    ZipStatus: Optional[str] = None
    ZipLatitude: Optional[str] = None
    ZipLongitude: Optional[str] = None
    CityType: Optional[str] = None
    CityAliasName: Optional[str] = None
    AreaCode: Optional[str] = None
    TimeZone: Optional[str] = None
    DaylightSaving: Optional[str] = None
    MSA: Optional[str] = None
    CBSA: Optional[str] = None
    CBSA_Div: Optional[str] = None
    PMSA: Optional[str] = None
    DMA: Optional[str] = None
    ZipHouseholdValue: Optional[str] = None
    ZipPersonsPerHousehold: Optional[str] = None
    ZipHouseholdIncome: Optional[str] = None
    CountyHouseholdIncome: Optional[str] = None
    StateHouseholdIncome: Optional[str] = None
    ZipNotes: Optional[str] = None
    ZipNotesCodes: Optional[str] = None
    Debug: Optional[List[str]] = None
    Error: Optional['Error'] = None

    def __post_init__(self):
        if self.Debug is None:
            self.Debug = []

    def __str__(self) -> str:
        debug_string = ', '.join(self.Debug) if self.Debug else 'None'
        error = str(self.Error) if self.Error else 'None'
        return (f"AINResponse: Status={self.Status}, StatusScore={self.StatusScore}, AddressStatus={self.AddressStatus}, "
                f"DPV={self.DPV}, DPVDesc={self.DPVDesc}, Address={self.Address}, AddressExtra={self.AddressExtra}, "
                f"City={self.City}, State={self.State}, Zip={self.Zip}, BarcodeDigits={self.BarcodeDigits}, "
                f"CarrierRoute={self.CarrierRoute}, CongressCode={self.CongressCode}, CountyCode={self.CountyCode}, "
                f"CountyName={self.CountyName}, FragmentHouse={self.FragmentHouse}, FragmentPreDir={self.FragmentPreDir}, "
                f"FragmentStreet={self.FragmentStreet}, FragmentSuffix={self.FragmentSuffix}, FragmentPostDir={self.FragmentPostDir}, "
                f"FragmentUnit={self.FragmentUnit}, FragmentUnitNumber={self.FragmentUnitNumber}, Fragment={self.Fragment}, "
                f"FragmentPMBPrefix={self.FragmentPMBPrefix}, FragmentPMBNumber={self.FragmentPMBNumber}, "
                f"Corrections={self.Corrections}, CorrectionsDesc={self.CorrectionsDesc}, AddressNotes={self.AddressNotes}, "
                f"AddressNotesCodes={self.AddressNotesCodes}, GeocodeStatus={self.GeocodeStatus}, LocationLatitude={self.LocationLatitude}, "
                f"LocationLongitude={self.LocationLongitude}, CensusTract={self.CensusTract}, CensusBlock={self.CensusBlock}, "
                f"PlaceName={self.PlaceName}, ClassFP={self.ClassFP}, SLDUST={self.SLDUST}, SLDLST={self.SLDLST}, "
                f"CountyFIPS={self.CountyFIPS}, StateFIPS={self.StateFIPS}, GeocodeNotes={self.GeocodeNotes}, "
                f"GeocodeNotesCodes={self.GeocodeNotesCodes}, ZipStatus={self.ZipStatus}, ZipLatitude={self.ZipLatitude}, "
                f"ZipLongitude={self.ZipLongitude}, CityType={self.CityType}, CityAliasName={self.CityAliasName}, "
                f"AreaCode={self.AreaCode}, TimeZone={self.TimeZone}, DaylightSaving={self.DaylightSaving}, "
                f"MSA={self.MSA}, CBSA={self.CBSA}, CBSA_Div={self.CBSA_Div}, PMSA={self.PMSA}, DMA={self.DMA}, "
                f"ZipHouseholdValue={self.ZipHouseholdValue}, ZipPersonsPerHousehold={self.ZipPersonsPerHousehold}, "
                f"ZipHouseholdIncome={self.ZipHouseholdIncome}, CountyHouseholdIncome={self.CountyHouseholdIncome}, "
                f"StateHouseholdIncome={self.StateHouseholdIncome}, ZipNotes={self.ZipNotes}, ZipNotesCodes={self.ZipNotesCodes}, "
                f"Debug=[{debug_string}], Error={error}")

Address Insight NodeJS Rest Code Snippet

import axios from 'axios';
import querystring from 'querystring';
import { AINResponse } from './ain_response.js';

/**
 * @constant
 * @type {string}
 * @description The base URL for the live ServiceObjects AddressInsight (AIN) API service.
 */
const LiveBaseUrl = 'https://sws.serviceobjects.com/ain/api.svc/json/';

/**
 * @constant
 * @type {string}
 * @description The base URL for the backup ServiceObjects AddressInsight (AIN) API service.
 */
const BackupBaseUrl = 'https://swsbackup.serviceobjects.com/ain/api.svc/json/';

/**
 * @constant
 * @type {string}
 * @description The base URL for the trial ServiceObjects AddressInsight (AIN) API service.
 */
const TrialBaseUrl = 'https://trial.serviceobjects.com/ain/api.svc/json/';

/**
 * <summary>
 * Checks if a response from the API is valid by verifying that it either has no Error object
 * or the Error.TypeCode is not equal to '3'.
 * </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.TypeCode !== '3';

/**
 * <summary>
 * Constructs a full URL for the GetAddressInsight API endpoint by combining the base URL
 * with query parameters derived from the input parameters.
 * </summary>
 * <param name="params" type="Object">An object containing all the input parameters.</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 = (params, baseUrl) =>
    `${baseUrl}GetAddressInsight?${querystring.stringify(params)}`;

/**
 * <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<AINResponse>">A promise that resolves to a AINResponse 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 AINResponse(response.data);
    } catch (error) {
        throw new Error(`HTTP request failed: ${error.message}`);
    }
};

/**
 * <summary>
 * Provides functionality to call the ServiceObjects AddressInsight (AIN) API's GetAddressInsight endpoint,
 * retrieving address validation, geocoding, and demographic information for a given US address
 * with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
const GetAddressInsightClient = {
    /**
     * <summary>
     * Asynchronously invokes the GetAddressInsight API endpoint, attempting the primary endpoint
     * first and falling back to the backup if the response is invalid (Error.TypeCode == '3') in live mode.
     * </summary>
     * @param {string} BusinessName - A company name for a business, can provide additional insight or append a SuiteLink value. Optional.
     * @param {string} Address1 - Address line 1 of the contact or business address (e.g., "123 Main Street").
     * @param {string} Address2 - Address line 2 of the contact or business address (e.g., "Apt 4B"). Optional.
     * @param {string} City - The city of the address (e.g., "New York"). Optional if zip is provided.
     * @param {string} State - The state of the address (e.g., "NY"). Optional if zip is provided.
     * @param {string} Zip - The ZIP code of the address. Optional if city and state are provided.
     * @param {string} TestType - The test type, either empty or "census_loose" for best possible match based on census data. Optional.
     * @param {string} LicenseKey - Your license key to use the service.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service.
     * @returns {Promise<AINResponse>} - A promise that resolves to a AINResponse object.
     */
    async invokeAsync(BusinessName, Address1, Address2, City, State, Zip, TestType, LicenseKey, isLive = true, timeoutSeconds = 15) {
        const params = {
            BusinessName,
            Address1,
            Address2,
            City,
            State,
            Zip,
            TestType,
            LicenseKey
        };

        const url = buildUrl(params, isLive ? LiveBaseUrl : TrialBaseUrl);
        let response = await httpGet(url, timeoutSeconds);

        if (isLive && !isValid(response)) {
            const fallbackUrl = buildUrl(params, BackupBaseUrl);
            const fallbackResponse = await httpGet(fallbackUrl, timeoutSeconds);
            return fallbackResponse;
        }
        return response;
    },

    /**
     * <summary>
     * Synchronously invokes the GetAddressInsight API endpoint by wrapping the async call
     * and awaiting its result immediately.
     * </summary>
     * @param {string} BusinessName - A company name for a business, can provide additional insight or append a SuiteLink value. Optional.
     * @param {string} Address1 - Address line 1 of the contact or business address (e.g., "123 Main Street").
     * @param {string} Address2 - Address line 2 of the contact or business address (e.g., "Apt 4B"). Optional.
     * @param {string} City - The city of the address (e.g., "New York"). Optional if zip is provided.
     * @param {string} State - The state of the address (e.g., "NY"). Optional if zip is provided.
     * @param {string} Zip - The ZIP code of the address. Optional if city and state are provided.
     * @param {string} TestType - The test type, either empty or "census_loose" for best possible match based on census data. Optional.
     * @param {string} LicenseKey - Your license key to use the service.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service.
     * @returns {AINResponse} - A AINResponse object with address insight details or an error.
     */
    invoke(BusinessName, Address1, Address2, City, State, Zip, TestType, LicenseKey, isLive = true, timeoutSeconds = 15) {
        return (async () => await this.invokeAsync(
            BusinessName, Address1, Address2, City, State, Zip, TestType, LicenseKey, isLive, timeoutSeconds
        ))();
    }
};

export { GetAddressInsightClient, AINResponse };


/**
 * Error object for API responses.
 */
export class Error {
    constructor(data = {}) {
        this.Type = data.Type;
        this.TypeCode = data.TypeCode;
        this.Desc = data.Desc;
        this.DescCode = data.DescCode;
    }

    toString() {
        return `Error: Type = ${this.Type}, TypeCode = ${this.TypeCode}, Desc = ${this.Desc}, DescCode = ${this.DescCode}`;
    }
}

/**
 * Response from GetAddressInsight API, containing address validation, geocoding, and demographic information.
 */
export class AINResponse {
    constructor(data = {}) {
        this.Status = data.Status;
        this.StatusScore = data.StatusScore;
        this.AddressStatus = data.AddressStatus;
        this.DPV = data.DPV;
        this.DPVDesc = data.DPVDesc;
        this.Address = data.Address;
        this.AddressExtra = data.AddressExtra;
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.BarcodeDigits = data.BarcodeDigits;
        this.CarrierRoute = data.CarrierRoute;
        this.CongressCode = data.CongressCode;
        this.CountyCode = data.CountyCode;
        this.CountyName = data.CountyName;
        this.FragmentHouse = data.FragmentHouse;
        this.FragmentPreDir = data.FragmentPreDir;
        this.FragmentStreet = data.FragmentStreet;
        this.FragmentSuffix = data.FragmentSuffix;
        this.FragmentPostDir = data.FragmentPostDir;
        this.FragmentUnit = data.FragmentUnit;
        this.FragmentUnitNumber = data.FragmentUnitNumber;
        this.Fragment = data.Fragment;
        this.FragmentPMBPrefix = data.FragmentPMBPrefix;
        this.FragmentPMBNumber = data.FragmentPMBNumber;
        this.Corrections = data.Corrections;
        this.CorrectionsDesc = data.CorrectionsDesc;
        this.AddressNotes = data.AddressNotes;
        this.AddressNotesCodes = data.AddressNotesCodes;
        this.GeocodeStatus = data.GeocodeStatus;
        this.LocationLatitude = data.LocationLatitude;
        this.LocationLongitude = data.LocationLongitude;
        this.CensusTract = data.CensusTract;
        this.CensusBlock = data.CensusBlock;
        this.PlaceName = data.PlaceName;
        this.ClassFP = data.ClassFP;
        this.SLDUST = data.SLDUST;
        this.SLDLST = data.SLDLST;
        this.CountyFIPS = data.CountyFIPS;
        this.StateFIPS = data.StateFIPS;
        this.GeocodeNotes = data.GeocodeNotes;
        this.GeocodeNotesCodes = data.GeocodeNotesCodes;
        this.ZipStatus = data.ZipStatus;
        this.ZipLatitude = data.ZipLatitude;
        this.ZipLongitude = data.ZipLongitude;
        this.CityType = data.CityType;
        this.CityAliasName = data.CityAliasName;
        this.AreaCode = data.AreaCode;
        this.TimeZone = data.TimeZone;
        this.DaylightSaving = data.DaylightSaving;
        this.MSA = data.MSA;
        this.CBSA = data.CBSA;
        this.CBSA_Div = data.CBSA_Div;
        this.PMSA = data.PMSA;
        this.DMA = data.DMA;
        this.ZipHouseholdValue = data.ZipHouseholdValue;
        this.ZipPersonsPerHousehold = data.ZipPersonsPerHousehold;
        this.ZipHouseholdIncome = data.ZipHouseholdIncome;
        this.CountyHouseholdIncome = data.CountyHouseholdIncome;
        this.StateHouseholdIncome = data.StateHouseholdIncome;
        this.ZipNotes = data.ZipNotes;
        this.ZipNotesCodes = data.ZipNotesCodes;
        this.Debug = data.Debug || [];
        this.Error = data.Error ? new Error(data.Error) : null;
    }

    toString() {
        const debugString = this.Debug.length ? this.Debug.join(', ') : 'null';
        return `AINResponse: Status = ${this.Status}, StatusScore = ${this.StatusScore}, AddressStatus = ${this.AddressStatus}, ` +
            `DPV = ${this.DPV}, DPVDesc = ${this.DPVDesc}, Address = ${this.Address}, AddressExtra = ${this.AddressExtra}, ` +
            `City = ${this.City}, State = ${this.State}, Zip = ${this.Zip}, BarcodeDigits = ${this.BarcodeDigits}, ` +
            `CarrierRoute = ${this.CarrierRoute}, CongressCode = ${this.CongressCode}, CountyCode = ${this.CountyCode}, ` +
            `CountyName = ${this.CountyName}, FragmentHouse = ${this.FragmentHouse}, FragmentPreDir = ${this.FragmentPreDir}, ` +
            `FragmentStreet = ${this.FragmentStreet}, FragmentSuffix = ${this.FragmentSuffix}, FragmentPostDir = ${this.FragmentPostDir}, ` +
            `FragmentUnit = ${this.FragmentUnit}, FragmentUnitNumber = ${this.FragmentUnitNumber}, Fragment = ${this.Fragment}, ` +
            `FragmentPMBPrefix = ${this.FragmentPMBPrefix}, FragmentPMBNumber = ${this.FragmentPMBNumber}, ` +
            `Corrections = ${this.Corrections}, CorrectionsDesc = ${this.CorrectionsDesc}, AddressNotes = ${this.AddressNotes}, ` +
            `AddressNotesCodes = ${this.AddressNotesCodes}, GeocodeStatus = ${this.GeocodeStatus}, LocationLatitude = ${this.LocationLatitude}, ` +
            `LocationLongitude = ${this.LocationLongitude}, CensusTract = ${this.CensusTract}, CensusBlock = ${this.CensusBlock}, ` +
            `PlaceName = ${this.PlaceName}, ClassFP = ${this.ClassFP}, SLDUST = ${this.SLDUST}, SLDLST = ${this.SLDLST}, ` +
            `CountyFIPS = ${this.CountyFIPS}, StateFIPS = ${this.StateFIPS}, GeocodeNotes = ${this.GeocodeNotes}, ` +
            `GeocodeNotesCodes = ${this.GeocodeNotesCodes}, ZipStatus = ${this.ZipStatus}, ZipLatitude = ${this.ZipLatitude}, ` +
            `ZipLongitude = ${this.ZipLongitude}, CityType = ${this.CityType}, CityAliasName = ${this.CityAliasName}, ` +
            `AreaCode = ${this.AreaCode}, TimeZone = ${this.TimeZone}, DaylightSaving = ${this.DaylightSaving}, ` +
            `MSA = ${this.MSA}, CBSA = ${this.CBSA}, CBSA_Div = ${this.CBSA_Div}, PMSA = ${this.PMSA}, DMA = ${this.DMA}, ` +
            `ZipHouseholdValue = ${this.ZipHouseholdValue}, ZipPersonsPerHousehold = ${this.ZipPersonsPerHousehold}, ` +
            `ZipHouseholdIncome = ${this.ZipHouseholdIncome}, CountyHouseholdIncome = ${this.CountyHouseholdIncome}, ` +
            `StateHouseholdIncome = ${this.StateHouseholdIncome}, ZipNotes = ${this.ZipNotes}, ZipNotesCodes = ${this.ZipNotesCodes}, ` +
            `Debug = [${debugString}], Error = ${this.Error ? this.Error.toString() : 'null'}`;
    }
}

export default AINResponse;