- C#
 - Python
 - NodeJS
 
Address Validation 3 C# Rest Code Snippet
using System.Threading.Tasks;
namespace address_validation_us_3_dot_net.REST
{
    /// <summary>
    /// Client for the AV3 GetBestMatches operation, providing both sync and async methods.
    /// Handles live vs. trial endpoints, URL encoding, fallback logic, and JSON deserialization.
    /// </summary>
    public static class GetBestMatchesClient
    {
        // Base URL constants: production, backup, and trial
        /// <summary>
        /// Synchronously invoke the GetBestMatchesJson endpoint.
        /// </summary>
        /// <param name="input">Data for the request (address components, license key, isLive).</param>
        /// <returns>Deserialized <see cref="GBMResponse"/>.</returns>
        public static GBMResponse Invoke(GetBestMatchesInput input)
        {
            //Use query string parameters so missing/options fields don't break
            //the URL as path parameters would.
            var url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GBMResponse response = Helper.HttpGet<GBMResponse>(url, input.TimeoutSeconds);
            // Fallback on error payload in live mode
            if (input.IsLive && !IsValid(response))
            {
                var fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GBMResponse fallbackResponse = Helper.HttpGet<GBMResponse>(fallbackUrl, input.TimeoutSeconds);
                return fallbackResponse;
            }
            return response;
        }
        /// <summary>
        /// Asynchronously invoke the GetBestMatchesJson endpoint.
        /// </summary>
        /// <param name="input">Data for the request (address components, license key, isLive).</param>
        /// <returns>Deserialized <see cref="GBMResponse"/>.</returns>
        public static async Task<GBMResponse> InvokeAsync(GetBestMatchesInput input)
        {
            //Use query string parameters so missing/options fields don't break
            //the URL as path parameters would.
            var url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GBMResponse response = await Helper.HttpGetAsync<GBMResponse>(url, input.TimeoutSeconds).ConfigureAwait(false);
            if (input.IsLive && !IsValid(response))
            {
                var fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GBMResponse fallbackResponse = await Helper.HttpGetAsync<GBMResponse>(fallbackUrl, input.TimeoutSeconds).ConfigureAwait(false);
                return fallbackResponse;
            }
            return response;
        }
        // Build the full request URL, including URL-encoded query string
        private static string BuildUrl(GetBestMatchesInput input, string baseUrl)
        {
            var qs = $"GetBestMatchesJson?BusinessName={Helper.UrlEncode(input.BusinessName)}" +
                     $"&Address={Helper.UrlEncode(input.Address)}" +
                     $"&Address2={Helper.UrlEncode(input.Address2)}" +
                     $"&City={Helper.UrlEncode(input.City)}" +
                     $"&State={Helper.UrlEncode(input.State)}" +
                     $"&PostalCode={Helper.UrlEncode(input.PostalCode)}" +
                     $"&LicenseKey={Helper.UrlEncode(input.LicenseKey)}";
            return baseUrl + qs;
        }
        private static bool IsValid(GBMResponse response) => response?.Error == null || response.Error.TypeCode != "3";
        /// <summary>
        /// Input parameters for the GetBestMatchesSingleLine operation.
        /// </summary>
        /// <param name="BusinessName">Company name to assist suite parsing (e.g., "Acme Corp"). - Optional</param>
        /// <param name="Address">Address line 1 (e.g., "123 Main St") - Required.</param>
        /// <param name="Address2">Address line 2 - Optional</param>
        /// <param name="City">City - Required when PostalCode is missing.</param>
        /// <param name="State">State - Required when PostalCode is missing.</param>
        /// <param name="PostalCode">PostalCode - Required when City and state are missing.</param>
        /// <param name="LicenseKey">Service Objects AV3 license key. - Required</param>
        /// <param name="IsLive">True for live (production+backup) endpoints; false for trial only. - Required</param>
        /// <param name="TimeoutSeconds">Request timeout in seconds (default: 15).</param>
        public record GetBestMatchesInput(
                                            string BusinessName = "",
                                            string Address = "",
                                            string Address2 = "",
                                            string City = "",
                                            string State = "",
                                            string PostalCode = "",
                                            string LicenseKey = "",
                                            bool IsLive = true,
                                            int TimeoutSeconds = 15
        );
    }
}
using System.Runtime.Serialization;
namespace address_validation_us_3_dot_net.REST
{
    /// <summary>
    /// Response object for capturing the reponse from the API for GetBestMatches
    /// </summary>
    [DataContract]
    public class GBMResponse
    {
        [DataMember(Name = "Addresses")]
        public Address[] Addresses { get; set; }
        [DataMember(Name = "IsCASS")]
        public bool IsCASS { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            string Output = "";
            Output += $"GBM Response:\n";
            Output += $"List of all Addresses: [";
            if (Addresses != null && Addresses.Length > 0)
            {
                foreach (Address A in Addresses)
                {
                    Output += A.ToString() + "\n";
                }
            }
            Output += "]\n";
            Output += $"IsCASS: {IsCASS}\n";
            Output += $"Error: {{{Error}}}\n";
            return Output;
        }
    }
    /// <summary>
    /// Response object for capturing the reponse from the API for GetSecondaryNumbers
    /// </summary>
    public class GSNResponse
    {
        [DataMember(Name = "Address1")]
        public string Address1 { get; set; }
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "TotalCount")]
        public int TotalCount { get; set; }
        [DataMember(Name = "SecondaryNumbers")]
        public string[] SecondaryNumbers { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            string Output = "";
            Output += $"GSN Response:\n";
            Output += $"Address1: {Address1}\n";
            Output += $"City: {City}\n";
            Output += $"State: {State}\n";
            Output += $"Zip: {Zip}\n";
            Output += $"Total Count: {TotalCount}\n";
            Output += $"SecondaryNumbers: [";
            if (SecondaryNumbers != null && SecondaryNumbers.Length > 0)
            {
                foreach (string A in SecondaryNumbers)
                {
                    Output += A.ToString() + "\n";
                }
            }
            Output += "]\n";
            Output += $"Error: {Error}\n";
            return Output;
        }
    }
    /// <summary>
    /// Response object for capturing the reponse from the API for CityStateZip
    /// </summary>
    public class CSZResponse
    {
        [DataMember(Name = "CityStateZip")]
        public CityStateZip CityStateZip { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            return $"CSZ Response:\n" +
                $"CityStateZip: {{{CityStateZip}}}\n" +
                $"Error: {{{Error}}}\n";
        }
    }
    /// <summary>
    /// Object to house the addresses being returned.
    /// </summary>
    public class Address
    {
        [DataMember(Name = "Address1")]
        public string Address1 { get; set; }
        [DataMember(Name = "Address2")]
        public string Address2 { get; set; }
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "IsResidential")]
        public string IsResidential { get; set; }
        [DataMember(Name = "DPV")]
        public string DPV { get; set; }
        [DataMember(Name = "DPVDesc")]
        public string DPVDesc { get; set; }
        [DataMember(Name = "DPVNotes")]
        public string DPVNotes { get; set; }
        [DataMember(Name = "DPVNotesDesc")]
        public string DPVNotesDesc { get; set; }
        [DataMember(Name = "Corrections")]
        public string Corrections { get; set; }
        [DataMember(Name = "CorrectionsDesc")]
        public string CorrectionsDesc { get; set; }
        [DataMember(Name = "BarcodeDigits")]
        public string BarcodeDigits { get; set; }
        [DataMember(Name = "CarrierRoute")]
        public string CarrierRoute { get; set; }
        [DataMember(Name = "CongressCode")]
        public string CongressCode { get; set; }
        [DataMember(Name = "CountyCode")]
        public string CountyCode { get; set; }
        [DataMember(Name = "CountyName")]
        public string CountyName { get; set; }
        [DataMember(Name = "FragmentHouse")]
        public string FragmentHouse { get; set; }
        [DataMember(Name = "FragmentPreDir")]
        public string FragmentPreDir { get; set; }
        [DataMember(Name = "FragmentStreet")]
        public string FragmentStreet { get; set; }
        [DataMember(Name = "FragmentSuffix")]
        public string FragmentSuffix { get; set; }
        [DataMember(Name = "FragmentPostDir")]
        public string FragmentPostDir { get; set; }
        [DataMember(Name = "FragmentUnit")]
        public string FragmentUnit { get; set; }
        [DataMember(Name = "Fragment")]
        public string Fragment { get; set; }
        [DataMember(Name = "FragmentPMBPrefix")]
        public string FragmentPMBPrefix { get; set; }
        [DataMember(Name = "FragmentPMBNumber")]
        public string FragmentPMBNumber { get; set; }
        public override string ToString()
        {
            return $"\n{{Address1: {Address1}\n" +
                $"\tAddress2: {Address2}\n" +
                $"\tCity: {City}\n" +
                $"\tState: {State}\n" +
                $"\tZip: {Zip}\n" +
                $"\tIsResidential: {IsResidential}\n" +
                $"\tDPV: {DPV}\n" +
                $"\tDPVDesc: {DPVDesc}\n" +
                $"\tDPVNotes: {DPVNotes}\n" +
                $"\tDPVNotesDesc: {DPVNotesDesc}\n" +
                $"\tCorrections: {Corrections}\n" +
                $"\tCorrectionsDesc: {CorrectionsDesc}\n" +
                $"\tBarcodeDigits: {BarcodeDigits}\n" +
                $"\tCarrierRoute: {CarrierRoute}\n" +
                $"\tCongressCode: {CongressCode}\n" +
                $"\tCountyCode: {CountyCode}\n" +
                $"\tCountyName: {CountyName}\n" +
                $"\tFragmentHouse: {FragmentHouse}\n" +
                $"\tFragmentPreDir: {FragmentPreDir}\n" +
                $"\tFragmentStreet: {FragmentStreet}\n" +
                $"\tFragmentSuffix: {FragmentSuffix}\n" +
                $"\tFragmentPostDir: {FragmentPostDir}\n" +
                $"\tFragmentUnit: {FragmentUnit}\n" +
                $"\tFragment: {Fragment}\n" +
                $"\tFragmentPMBPrefix: {FragmentPMBPrefix}\n" +
                $"\tFragmentPMBNumber: {FragmentPMBNumber}}}\n";
        }
    }
    public class CityStateZip
    {
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "GeneralDeliveryService")]
        public string GeneralDeliveryService { get; set; }
        [DataMember(Name = "POBoxService")]
        public string POBoxService { get; set; }
        [DataMember(Name = "StreetService")]
        public string StreetService { get; set; }
        [DataMember(Name = "RRHCService")]
        public string RRHCService { get; set; }
        [DataMember(Name = "UrbanizationService")]
        public string UrbanizationService { get; set; }
        [DataMember(Name = "POBoxRangeLow")]
        public string POBoxRangeLow { get; set; }
        [DataMember(Name = "POBoxRangeHigh")]
        public string POBoxRangeHigh { get; set; }
        [DataMember(Name = "IsUniqueZipCode")]
        public string IsUniqueZipCode { get; set; }
        public override string ToString()
        {
            return $"City: {City} " +
                $"State: {State} " +
                $"Zip: {Zip} " +
                $"GeneralDeliveryService: {GeneralDeliveryService} " +
                $"POBoxService: {POBoxService} " +
                $"StreetService: {StreetService} " +
                $"RRHCService: {RRHCService} " +
                $"UrbanizationService: {UrbanizationService} " +
                $"POBoxRangeLow: {POBoxRangeLow} " +
                $"POBoxRangeHigh: {POBoxRangeHigh} " +
                $"IsUniqueZipCode: {IsUniqueZipCode} ";
        }
    }
    public class Error
    {
        [DataMember(Name = "Type")]
        public string Type { get; set; }
        [DataMember(Name = "TypeCode")]
        public string TypeCode { get; set; }
        [DataMember(Name = "Desc")]
        public string Desc { get; set; }
        [DataMember(Name = "DescCode")]
        public string DescCode { get; set; }
        public override string ToString()
        {
            return $"Type: {Type} " +
                $"TypeCode: {TypeCode} " +
                $"Desc: {Desc} " +
                $"DescCode: {DescCode} ";
        }
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;
namespace address_validation_us_3_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);
    }
}
using System.Threading.Tasks;
namespace address_validation_us_3_dot_net.REST
{
    /// <summary>
    /// Client for the AV3 GetBestMatches operation, providing both sync and async methods.
    /// Handles live vs. trial endpoints, URL encoding, fallback logic, and JSON deserialization.
    /// </summary>
    public static class GetBestMatchesClient
    {
        // Base URL constants: production, backup, and trial
        private const string LiveBaseUrl = "https://sws.serviceobjects.com/AV3/api.svc/";
        private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/AV3/api.svc/";
        private const string TrialBaseUrl = "https://trial.serviceobjects.com/AV3/api.svc/";
        /// <summary>
        /// Synchronously invoke the GetBestMatchesJson endpoint.
        /// </summary>
        /// <param name="input">Data for the request (address components, license key, isLive).</param>
        /// <returns>Deserialized <see cref="GBMResponse"/>.</returns>
        public static GBMResponse Invoke(GetBestMatchesInput input)
        {
            //Use query string parameters so missing/options fields don't break
            //the URL as path parameters would.
            var url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GBMResponse response = Helper.HttpGet<GBMResponse>(url, input.TimeoutSeconds);
            // Fallback on error payload in live mode
            if (input.IsLive && !IsValid(response))
            {
                var fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GBMResponse fallbackResponse = Helper.HttpGet<GBMResponse>(fallbackUrl, input.TimeoutSeconds);
                return fallbackResponse;
            }
            return response;
        }
        /// <summary>
        /// Asynchronously invoke the GetBestMatchesJson endpoint.
        /// </summary>
        /// <param name="input">Data for the request (address components, license key, isLive).</param>
        /// <returns>Deserialized <see cref="GBMResponse"/>.</returns>
        public static async Task<GBMResponse> InvokeAsync(GetBestMatchesInput input)
        {
            //Use query string parameters so missing/options fields don't break
            //the URL as path parameters would.
            var url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GBMResponse response = await Helper.HttpGetAsync<GBMResponse>(url, input.TimeoutSeconds).ConfigureAwait(false);
            if (input.IsLive && !IsValid(response))
            {
                var fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GBMResponse fallbackResponse = await Helper.HttpGetAsync<GBMResponse>(fallbackUrl, input.TimeoutSeconds).ConfigureAwait(false);
                return fallbackResponse;
            }
            return response;
        }
        // Build the full request URL, including URL-encoded query string
        private static string BuildUrl(GetBestMatchesInput input, string baseUrl)
        {
            var qs = $"GetBestMatchesJson?BusinessName={Helper.UrlEncode(input.BusinessName)}" +
                     $"&Address={Helper.UrlEncode(input.Address)}" +
                     $"&Address2={Helper.UrlEncode(input.Address2)}" +
                     $"&City={Helper.UrlEncode(input.City)}" +
                     $"&State={Helper.UrlEncode(input.State)}" +
                     $"&PostalCode={Helper.UrlEncode(input.PostalCode)}" +
                     $"&LicenseKey={Helper.UrlEncode(input.LicenseKey)}";
            return baseUrl + qs;
        }
        private static bool IsValid(GBMResponse response) => response?.Error == null || response.Error.TypeCode != "3";
        /// <summary>
        /// Input parameters for the GetBestMatchesSingleLine operation.
        /// </summary>
        /// <param name="BusinessName">Company name to assist suite parsing (e.g., "Acme Corp"). - Optional</param>
        /// <param name="Address">Address line 1 (e.g., "123 Main St") - Required.</param>
        /// <param name="Address2">Address line 2 - Optional</param>
        /// <param name="City">City - Required when PostalCode is missing.</param>
        /// <param name="State">State - Required when PostalCode is missing.</param>
        /// <param name="PostalCode">PostalCode - Required when City and state are missing.</param>
        /// <param name="LicenseKey">Service Objects AV3 license key. - Required</param>
        /// <param name="IsLive">True for live (production+backup) endpoints; false for trial only. - Required</param>
        /// <param name="TimeoutSeconds">Request timeout in seconds (default: 15).</param>
        public record GetBestMatchesInput(
                                            string BusinessName = "",
                                            string Address = "",
                                            string Address2 = "",
                                            string City = "",
                                            string State = "",
                                            string PostalCode = "",
                                            string LicenseKey = "",
                                            bool IsLive = true,
                                            int TimeoutSeconds = 15
        );
    }
}
using System.Runtime.Serialization;
namespace address_validation_us_3_dot_net.REST
{
    /// <summary>
    /// Response object for capturing the reponse from the API for GetBestMatches
    /// </summary>
    [DataContract]
    public class GBMResponse
    {
        [DataMember(Name = "Addresses")]
        public Address[] Addresses { get; set; }
        [DataMember(Name = "IsCASS")]
        public bool IsCASS { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            string Output = "";
            Output += $"GBM Response:\n";
            Output += $"List of all Addresses: [";
            if (Addresses != null && Addresses.Length > 0)
            {
                foreach (Address A in Addresses)
                {
                    Output += A.ToString() + "\n";
                }
            }
            Output += "]\n";
            Output += $"IsCASS: {IsCASS}\n";
            Output += $"Error: {{{Error}}}\n";
            return Output;
        }
    }
    /// <summary>
    /// Response object for capturing the reponse from the API for GetSecondaryNumbers
    /// </summary>
    public class GSNResponse
    {
        [DataMember(Name = "Address1")]
        public string Address1 { get; set; }
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "TotalCount")]
        public int TotalCount { get; set; }
        [DataMember(Name = "SecondaryNumbers")]
        public string[] SecondaryNumbers { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            string Output = "";
            Output += $"GSN Response:\n";
            Output += $"Address1: {Address1}\n";
            Output += $"City: {City}\n";
            Output += $"State: {State}\n";
            Output += $"Zip: {Zip}\n";
            Output += $"Total Count: {TotalCount}\n";
            Output += $"SecondaryNumbers: [";
            if (SecondaryNumbers != null && SecondaryNumbers.Length > 0)
            {
                foreach (string A in SecondaryNumbers)
                {
                    Output += A.ToString() + "\n";
                }
            }
            Output += "]\n";
            Output += $"Error: {Error}\n";
            return Output;
        }
    }
    /// <summary>
    /// Response object for capturing the reponse from the API for CityStateZip
    /// </summary>
    public class CSZResponse
    {
        [DataMember(Name = "CityStateZip")]
        public CityStateZip CityStateZip { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            return $"CSZ Response:\n" +
                $"CityStateZip: {{{CityStateZip}}}\n" +
                $"Error: {{{Error}}}\n";
        }
    }
    /// <summary>
    /// Object to house the addresses being returned.
    /// </summary>
    public class Address
    {
        [DataMember(Name = "Address1")]
        public string Address1 { get; set; }
        [DataMember(Name = "Address2")]
        public string Address2 { get; set; }
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "IsResidential")]
        public string IsResidential { get; set; }
        [DataMember(Name = "DPV")]
        public string DPV { get; set; }
        [DataMember(Name = "DPVDesc")]
        public string DPVDesc { get; set; }
        [DataMember(Name = "DPVNotes")]
        public string DPVNotes { get; set; }
        [DataMember(Name = "DPVNotesDesc")]
        public string DPVNotesDesc { get; set; }
        [DataMember(Name = "Corrections")]
        public string Corrections { get; set; }
        [DataMember(Name = "CorrectionsDesc")]
        public string CorrectionsDesc { get; set; }
        [DataMember(Name = "BarcodeDigits")]
        public string BarcodeDigits { get; set; }
        [DataMember(Name = "CarrierRoute")]
        public string CarrierRoute { get; set; }
        [DataMember(Name = "CongressCode")]
        public string CongressCode { get; set; }
        [DataMember(Name = "CountyCode")]
        public string CountyCode { get; set; }
        [DataMember(Name = "CountyName")]
        public string CountyName { get; set; }
        [DataMember(Name = "FragmentHouse")]
        public string FragmentHouse { get; set; }
        [DataMember(Name = "FragmentPreDir")]
        public string FragmentPreDir { get; set; }
        [DataMember(Name = "FragmentStreet")]
        public string FragmentStreet { get; set; }
        [DataMember(Name = "FragmentSuffix")]
        public string FragmentSuffix { get; set; }
        [DataMember(Name = "FragmentPostDir")]
        public string FragmentPostDir { get; set; }
        [DataMember(Name = "FragmentUnit")]
        public string FragmentUnit { get; set; }
        [DataMember(Name = "Fragment")]
        public string Fragment { get; set; }
        [DataMember(Name = "FragmentPMBPrefix")]
        public string FragmentPMBPrefix { get; set; }
        [DataMember(Name = "FragmentPMBNumber")]
        public string FragmentPMBNumber { get; set; }
        public override string ToString()
        {
            return $"\n{{Address1: {Address1}\n" +
                $"\tAddress2: {Address2}\n" +
                $"\tCity: {City}\n" +
                $"\tState: {State}\n" +
                $"\tZip: {Zip}\n" +
                $"\tIsResidential: {IsResidential}\n" +
                $"\tDPV: {DPV}\n" +
                $"\tDPVDesc: {DPVDesc}\n" +
                $"\tDPVNotes: {DPVNotes}\n" +
                $"\tDPVNotesDesc: {DPVNotesDesc}\n" +
                $"\tCorrections: {Corrections}\n" +
                $"\tCorrectionsDesc: {CorrectionsDesc}\n" +
                $"\tBarcodeDigits: {BarcodeDigits}\n" +
                $"\tCarrierRoute: {CarrierRoute}\n" +
                $"\tCongressCode: {CongressCode}\n" +
                $"\tCountyCode: {CountyCode}\n" +
                $"\tCountyName: {CountyName}\n" +
                $"\tFragmentHouse: {FragmentHouse}\n" +
                $"\tFragmentPreDir: {FragmentPreDir}\n" +
                $"\tFragmentStreet: {FragmentStreet}\n" +
                $"\tFragmentSuffix: {FragmentSuffix}\n" +
                $"\tFragmentPostDir: {FragmentPostDir}\n" +
                $"\tFragmentUnit: {FragmentUnit}\n" +
                $"\tFragment: {Fragment}\n" +
                $"\tFragmentPMBPrefix: {FragmentPMBPrefix}\n" +
                $"\tFragmentPMBNumber: {FragmentPMBNumber}}}\n";
        }
    }
    public class CityStateZip
    {
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "GeneralDeliveryService")]
        public string GeneralDeliveryService { get; set; }
        [DataMember(Name = "POBoxService")]
        public string POBoxService { get; set; }
        [DataMember(Name = "StreetService")]
        public string StreetService { get; set; }
        [DataMember(Name = "RRHCService")]
        public string RRHCService { get; set; }
        [DataMember(Name = "UrbanizationService")]
        public string UrbanizationService { get; set; }
        [DataMember(Name = "POBoxRangeLow")]
        public string POBoxRangeLow { get; set; }
        [DataMember(Name = "POBoxRangeHigh")]
        public string POBoxRangeHigh { get; set; }
        [DataMember(Name = "IsUniqueZipCode")]
        public string IsUniqueZipCode { get; set; }
        public override string ToString()
        {
            return $"City: {City} " +
                $"State: {State} " +
                $"Zip: {Zip} " +
                $"GeneralDeliveryService: {GeneralDeliveryService} " +
                $"POBoxService: {POBoxService} " +
                $"StreetService: {StreetService} " +
                $"RRHCService: {RRHCService} " +
                $"UrbanizationService: {UrbanizationService} " +
                $"POBoxRangeLow: {POBoxRangeLow} " +
                $"POBoxRangeHigh: {POBoxRangeHigh} " +
                $"IsUniqueZipCode: {IsUniqueZipCode} ";
        }
    }
    public class Error
    {
        [DataMember(Name = "Type")]
        public string Type { get; set; }
        [DataMember(Name = "TypeCode")]
        public string TypeCode { get; set; }
        [DataMember(Name = "Desc")]
        public string Desc { get; set; }
        [DataMember(Name = "DescCode")]
        public string DescCode { get; set; }
        public override string ToString()
        {
            return $"Type: {Type} " +
                $"TypeCode: {TypeCode} " +
                $"Desc: {Desc} " +
                $"DescCode: {DescCode} ";
        }
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;
namespace address_validation_us_3_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);
    }
}
using System.Threading.Tasks;
namespace address_validation_us_3_dot_net.REST
{
    /// <summary>
    /// Client for the AV3 GetBestMatches operation, providing both sync and async methods.
    /// Handles live vs. trial endpoints, URL encoding, fallback logic, and JSON deserialization.
    /// </summary>
    public static class GetBestMatchesClient
    {
        // Base URL constants: production, backup, and trial
        private const string LiveBaseUrl = "https://sws.serviceobjects.com/AV3/api.svc/";
        private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/AV3/api.svc/";
        private const string TrialBaseUrl = "https://trial.serviceobjects.com/AV3/api.svc/";
        /// <summary>
        /// Synchronously invoke the GetBestMatchesJson endpoint.
        /// </summary>
        /// <param name="input">Data for the request (address components, license key, isLive).</param>
        /// <returns>Deserialized <see cref="GBMResponse"/>.</returns>
        public static GBMResponse Invoke(GetBestMatchesInput input)
        {
            //Use query string parameters so missing/options fields don't break
            //the URL as path parameters would.
            var url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GBMResponse response = Helper.HttpGet<GBMResponse>(url, input.TimeoutSeconds);
            // Fallback on error payload in live mode
            if (input.IsLive && !IsValid(response))
            {
                var fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GBMResponse fallbackResponse = Helper.HttpGet<GBMResponse>(fallbackUrl, input.TimeoutSeconds);
                return fallbackResponse;
            }
            return response;
        }
        /// <summary>
        /// Asynchronously invoke the GetBestMatchesJson endpoint.
        /// </summary>
        /// <param name="input">Data for the request (address components, license key, isLive).</param>
        /// <returns>Deserialized <see cref="GBMResponse"/>.</returns>
        public static async Task<GBMResponse> InvokeAsync(GetBestMatchesInput input)
        {
            //Use query string parameters so missing/options fields don't break
            //the URL as path parameters would.
            var url = BuildUrl(input, input.IsLive ? LiveBaseUrl : TrialBaseUrl);
            GBMResponse response = await Helper.HttpGetAsync<GBMResponse>(url, input.TimeoutSeconds).ConfigureAwait(false);
            if (input.IsLive && !IsValid(response))
            {
                var fallbackUrl = BuildUrl(input, BackupBaseUrl);
                GBMResponse fallbackResponse = await Helper.HttpGetAsync<GBMResponse>(fallbackUrl, input.TimeoutSeconds).ConfigureAwait(false);
                return fallbackResponse;
            }
            return response;
        }
        // Build the full request URL, including URL-encoded query string
        private static string BuildUrl(GetBestMatchesInput input, string baseUrl)
        {
            var qs = $"GetBestMatchesJson?BusinessName={Helper.UrlEncode(input.BusinessName)}" +
                     $"&Address={Helper.UrlEncode(input.Address)}" +
                     $"&Address2={Helper.UrlEncode(input.Address2)}" +
                     $"&City={Helper.UrlEncode(input.City)}" +
                     $"&State={Helper.UrlEncode(input.State)}" +
                     $"&PostalCode={Helper.UrlEncode(input.PostalCode)}" +
                     $"&LicenseKey={Helper.UrlEncode(input.LicenseKey)}";
            return baseUrl + qs;
        }
        private static bool IsValid(GBMResponse response) => response?.Error == null || response.Error.TypeCode != "3";
        /// <summary>
        /// Input parameters for the GetBestMatchesSingleLine operation.
        /// </summary>
        /// <param name="BusinessName">Company name to assist suite parsing (e.g., "Acme Corp"). - Optional</param>
        /// <param name="Address">Address line 1 (e.g., "123 Main St") - Required.</param>
        /// <param name="Address2">Address line 2 - Optional</param>
        /// <param name="City">City - Required when PostalCode is missing.</param>
        /// <param name="State">State - Required when PostalCode is missing.</param>
        /// <param name="PostalCode">PostalCode - Required when City and state are missing.</param>
        /// <param name="LicenseKey">Service Objects AV3 license key. - Required</param>
        /// <param name="IsLive">True for live (production+backup) endpoints; false for trial only. - Required</param>
        /// <param name="TimeoutSeconds">Request timeout in seconds (default: 15).</param>
        public record GetBestMatchesInput(
                                            string BusinessName = "",
                                            string Address = "",
                                            string Address2 = "",
                                            string City = "",
                                            string State = "",
                                            string PostalCode = "",
                                            string LicenseKey = "",
                                            bool IsLive = true,
                                            int TimeoutSeconds = 15
        );
    }
}
using System.Runtime.Serialization;
namespace address_validation_us_3_dot_net.REST
{
    /// <summary>
    /// Response object for capturing the reponse from the API for GetBestMatches
    /// </summary>
    [DataContract]
    public class GBMResponse
    {
        [DataMember(Name = "Addresses")]
        public Address[] Addresses { get; set; }
        [DataMember(Name = "IsCASS")]
        public bool IsCASS { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            string Output = "";
            Output += $"GBM Response:\n";
            Output += $"List of all Addresses: [";
            if (Addresses != null && Addresses.Length > 0)
            {
                foreach (Address A in Addresses)
                {
                    Output += A.ToString() + "\n";
                }
            }
            Output += "]\n";
            Output += $"IsCASS: {IsCASS}\n";
            Output += $"Error: {{{Error}}}\n";
            return Output;
        }
    }
    /// <summary>
    /// Response object for capturing the reponse from the API for GetSecondaryNumbers
    /// </summary>
    public class GSNResponse
    {
        [DataMember(Name = "Address1")]
        public string Address1 { get; set; }
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "TotalCount")]
        public int TotalCount { get; set; }
        [DataMember(Name = "SecondaryNumbers")]
        public string[] SecondaryNumbers { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            string Output = "";
            Output += $"GSN Response:\n";
            Output += $"Address1: {Address1}\n";
            Output += $"City: {City}\n";
            Output += $"State: {State}\n";
            Output += $"Zip: {Zip}\n";
            Output += $"Total Count: {TotalCount}\n";
            Output += $"SecondaryNumbers: [";
            if (SecondaryNumbers != null && SecondaryNumbers.Length > 0)
            {
                foreach (string A in SecondaryNumbers)
                {
                    Output += A.ToString() + "\n";
                }
            }
            Output += "]\n";
            Output += $"Error: {Error}\n";
            return Output;
        }
    }
    /// <summary>
    /// Response object for capturing the reponse from the API for CityStateZip
    /// </summary>
    public class CSZResponse
    {
        [DataMember(Name = "CityStateZip")]
        public CityStateZip CityStateZip { get; set; }
        [DataMember(Name = "Error")]
        public Error Error { get; set; }
        public override string ToString()
        {
            return $"CSZ Response:\n" +
                $"CityStateZip: {{{CityStateZip}}}\n" +
                $"Error: {{{Error}}}\n";
        }
    }
    /// <summary>
    /// Object to house the addresses being returned.
    /// </summary>
    public class Address
    {
        [DataMember(Name = "Address1")]
        public string Address1 { get; set; }
        [DataMember(Name = "Address2")]
        public string Address2 { get; set; }
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "IsResidential")]
        public string IsResidential { get; set; }
        [DataMember(Name = "DPV")]
        public string DPV { get; set; }
        [DataMember(Name = "DPVDesc")]
        public string DPVDesc { get; set; }
        [DataMember(Name = "DPVNotes")]
        public string DPVNotes { get; set; }
        [DataMember(Name = "DPVNotesDesc")]
        public string DPVNotesDesc { get; set; }
        [DataMember(Name = "Corrections")]
        public string Corrections { get; set; }
        [DataMember(Name = "CorrectionsDesc")]
        public string CorrectionsDesc { get; set; }
        [DataMember(Name = "BarcodeDigits")]
        public string BarcodeDigits { get; set; }
        [DataMember(Name = "CarrierRoute")]
        public string CarrierRoute { get; set; }
        [DataMember(Name = "CongressCode")]
        public string CongressCode { get; set; }
        [DataMember(Name = "CountyCode")]
        public string CountyCode { get; set; }
        [DataMember(Name = "CountyName")]
        public string CountyName { get; set; }
        [DataMember(Name = "FragmentHouse")]
        public string FragmentHouse { get; set; }
        [DataMember(Name = "FragmentPreDir")]
        public string FragmentPreDir { get; set; }
        [DataMember(Name = "FragmentStreet")]
        public string FragmentStreet { get; set; }
        [DataMember(Name = "FragmentSuffix")]
        public string FragmentSuffix { get; set; }
        [DataMember(Name = "FragmentPostDir")]
        public string FragmentPostDir { get; set; }
        [DataMember(Name = "FragmentUnit")]
        public string FragmentUnit { get; set; }
        [DataMember(Name = "Fragment")]
        public string Fragment { get; set; }
        [DataMember(Name = "FragmentPMBPrefix")]
        public string FragmentPMBPrefix { get; set; }
        [DataMember(Name = "FragmentPMBNumber")]
        public string FragmentPMBNumber { get; set; }
        public override string ToString()
        {
            return $"\n{{Address1: {Address1}\n" +
                $"\tAddress2: {Address2}\n" +
                $"\tCity: {City}\n" +
                $"\tState: {State}\n" +
                $"\tZip: {Zip}\n" +
                $"\tIsResidential: {IsResidential}\n" +
                $"\tDPV: {DPV}\n" +
                $"\tDPVDesc: {DPVDesc}\n" +
                $"\tDPVNotes: {DPVNotes}\n" +
                $"\tDPVNotesDesc: {DPVNotesDesc}\n" +
                $"\tCorrections: {Corrections}\n" +
                $"\tCorrectionsDesc: {CorrectionsDesc}\n" +
                $"\tBarcodeDigits: {BarcodeDigits}\n" +
                $"\tCarrierRoute: {CarrierRoute}\n" +
                $"\tCongressCode: {CongressCode}\n" +
                $"\tCountyCode: {CountyCode}\n" +
                $"\tCountyName: {CountyName}\n" +
                $"\tFragmentHouse: {FragmentHouse}\n" +
                $"\tFragmentPreDir: {FragmentPreDir}\n" +
                $"\tFragmentStreet: {FragmentStreet}\n" +
                $"\tFragmentSuffix: {FragmentSuffix}\n" +
                $"\tFragmentPostDir: {FragmentPostDir}\n" +
                $"\tFragmentUnit: {FragmentUnit}\n" +
                $"\tFragment: {Fragment}\n" +
                $"\tFragmentPMBPrefix: {FragmentPMBPrefix}\n" +
                $"\tFragmentPMBNumber: {FragmentPMBNumber}}}\n";
        }
    }
    public class CityStateZip
    {
        [DataMember(Name = "City")]
        public string City { get; set; }
        [DataMember(Name = "State")]
        public string State { get; set; }
        [DataMember(Name = "Zip")]
        public string Zip { get; set; }
        [DataMember(Name = "GeneralDeliveryService")]
        public string GeneralDeliveryService { get; set; }
        [DataMember(Name = "POBoxService")]
        public string POBoxService { get; set; }
        [DataMember(Name = "StreetService")]
        public string StreetService { get; set; }
        [DataMember(Name = "RRHCService")]
        public string RRHCService { get; set; }
        [DataMember(Name = "UrbanizationService")]
        public string UrbanizationService { get; set; }
        [DataMember(Name = "POBoxRangeLow")]
        public string POBoxRangeLow { get; set; }
        [DataMember(Name = "POBoxRangeHigh")]
        public string POBoxRangeHigh { get; set; }
        [DataMember(Name = "IsUniqueZipCode")]
        public string IsUniqueZipCode { get; set; }
        public override string ToString()
        {
            return $"City: {City} " +
                $"State: {State} " +
                $"Zip: {Zip} " +
                $"GeneralDeliveryService: {GeneralDeliveryService} " +
                $"POBoxService: {POBoxService} " +
                $"StreetService: {StreetService} " +
                $"RRHCService: {RRHCService} " +
                $"UrbanizationService: {UrbanizationService} " +
                $"POBoxRangeLow: {POBoxRangeLow} " +
                $"POBoxRangeHigh: {POBoxRangeHigh} " +
                $"IsUniqueZipCode: {IsUniqueZipCode} ";
        }
    }
    public class Error
    {
        [DataMember(Name = "Type")]
        public string Type { get; set; }
        [DataMember(Name = "TypeCode")]
        public string TypeCode { get; set; }
        [DataMember(Name = "Desc")]
        public string Desc { get; set; }
        [DataMember(Name = "DescCode")]
        public string DescCode { get; set; }
        public override string ToString()
        {
            return $"Type: {Type} " +
                $"TypeCode: {TypeCode} " +
                $"Desc: {Desc} " +
                $"DescCode: {DescCode} ";
        }
    }
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;
namespace address_validation_us_3_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 Validation US 3 Python Code Snippet
'''
Service Objects, Inc.
This module provides the get_best_matches function to validate and standardize US addresses
using Service Objects AV3 API. It handles live/trial endpoints, fallback logic, and JSON parsing.
Functions:
    get_best_matches(business_name: str,
                     address: str,
                     address_2: str,
                     city: str,
                     state: str,
                     postal_code: str,
                     license_key: str,
                     is_live: bool) -> dict:
'''  
import requests  # HTTP client for RESTful API calls
# Endpoint URLs for AV3 service
primary_url = 'https://sws.serviceobjects.com/AV3/api.svc/GetBestMatchesJson?'
def get_best_matches(business_name: str,
                   address: str,
                   address_2: str,
                   city: str,
                   state: str,
                   postal_code: str,
                   license_key: str,
                   is_live: bool) -> dict:
    """
    Call AV3 get_best_matches API and return validation results.
    Parameters:
        business_name (str): Company name to assist suite parsing.
        address (str): Primary street address (e.g., '123 Main St'). Required.
        address_2 (str): Secondary address info (e.g., 'C/O John Smith').
        city (str): City name; required if PostalCode not provided.
        state (str): State code or full name; required if PostalCode not provided.
        postal_code (str): 5- or 9-digit ZIP; required if City/State not provided.
        license_key (str): Service Objects license key.
        is_live (bool): True for production endpoints, False for trial URL.
    Returns:
        dict: Parsed JSON response with address addresss or error details.
    Raises:
        RuntimeError: If the API returns an error payload.
        requests.RequestException: On network/HTTP failures (trial mode).
    """
    # Prepare query parameters for AV3 API
    params = {
        'BusinessName': business_name,
        'Address'     : address,
        'Address2'    : address_2,
        'City'        : city,
        'State'       : state,
        'PostalCode'  : postal_code,
        '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, 'TypeCode', None) != "3"):
            if is_live:
                # Try backup URL when live
                response = requests.get(backup_url, params=params, timeout=10)
                data = response.json()
                # If still error, propagate exception
                if 'Error' in data:
                    raise RuntimeError(f"AV3 service error: {data['Error']}")
            else:
                # Trial mode should not fallback; error is terminal
                raise RuntimeError(f"AV3 trial error: {data['Error']}")
        # Success: return parsed JSON data
        return data
    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"AV3 backup error: {data['Error']}") from req_exc
                return data
            except Exception as backup_exc:
                # Both primary and backup failed; escalate
                raise RuntimeError("AV3 service unreachable on both endpoints") from backup_exc
        else:
            # In trial mode, propagate the network exception
            raise RuntimeError(f"AV3 trial error: {str(req_exc)}") from req_exc
'''
Service Objects, Inc.
This module provides the get_best_matches function to validate and standardize US addresses
using Service Objects AV3 API. It handles live/trial endpoints, fallback logic, and JSON parsing.
Functions:
    get_best_matches(business_name: str,
                     address: str,
                     address_2: str,
                     city: str,
                     state: str,
                     postal_code: str,
                     license_key: str,
                     is_live: bool) -> dict:
'''  
import requests  # HTTP client for RESTful API calls
# Endpoint URLs for AV3 service
primary_url = 'https://sws.serviceobjects.com/AV3/api.svc/GetBestMatchesJson?'
backup_url = 'https://swsbackup.serviceobjects.com/AV3/api.svc/GetBestMatchesJson?'
trial_url = 'https://trial.serviceobjects.com/AV3/api.svc/GetBestMatchesJson?'
def get_best_matches(business_name: str,
                   address: str,
                   address_2: str,
                   city: str,
                   state: str,
                   postal_code: str,
                   license_key: str,
                   is_live: bool) -> dict:
    """
    Call AV3 get_best_matches API and return validation results.
    Parameters:
        business_name (str): Company name to assist suite parsing.
        address (str): Primary street address (e.g., '123 Main St'). Required.
        address_2 (str): Secondary address info (e.g., 'C/O John Smith').
        city (str): City name; required if PostalCode not provided.
        state (str): State code or full name; required if PostalCode not provided.
        postal_code (str): 5- or 9-digit ZIP; required if City/State not provided.
        license_key (str): Service Objects license key.
        is_live (bool): True for production endpoints, False for trial URL.
    Returns:
        dict: Parsed JSON response with address addresss or error details.
    Raises:
        RuntimeError: If the API returns an error payload.
        requests.RequestException: On network/HTTP failures (trial mode).
    """
    # Prepare query parameters for AV3 API
    params = {
        'BusinessName': business_name,
        'Address'     : address,
        'Address2'    : address_2,
        'City'        : city,
        'State'       : state,
        'PostalCode'  : postal_code,
        '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, 'TypeCode', None) != "3"):
            if is_live:
                # Try backup URL when live
                response = requests.get(backup_url, params=params, timeout=10)
                data = response.json()
                # If still error, propagate exception
                if 'Error' in data:
                    raise RuntimeError(f"AV3 service error: {data['Error']}")
            else:
                # Trial mode should not fallback; error is terminal
                raise RuntimeError(f"AV3 trial error: {data['Error']}")
        # Success: return parsed JSON data
        return data
    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"AV3 backup error: {data['Error']}") from req_exc
                return data
            except Exception as backup_exc:
                # Both primary and backup failed; escalate
                raise RuntimeError("AV3 service unreachable on both endpoints") from backup_exc
        else:
            # In trial mode, propagate the network exception
            raise RuntimeError(f"AV3 trial error: {str(req_exc)}") from req_exc
'''
Service Objects, Inc.
This module provides the get_best_matches function to validate and standardize US addresses
using Service Objects AV3 API. It handles live/trial endpoints, fallback logic, and JSON parsing.
Functions:
    get_best_matches(business_name: str,
                     address: str,
                     address_2: str,
                     city: str,
                     state: str,
                     postal_code: str,
                     license_key: str,
                     is_live: bool) -> dict:
'''  
import requests  # HTTP client for RESTful API calls
# Endpoint URLs for AV3 service
primary_url = 'https://sws.serviceobjects.com/AV3/api.svc/GetBestMatchesJson?'
backup_url = 'https://swsbackup.serviceobjects.com/AV3/api.svc/GetBestMatchesJson?'
trial_url = 'https://trial.serviceobjects.com/AV3/api.svc/GetBestMatchesJson?'
def get_best_matches(business_name: str,
                   address: str,
                   address_2: str,
                   city: str,
                   state: str,
                   postal_code: str,
                   license_key: str,
                   is_live: bool) -> dict:
    """
    Call AV3 get_best_matches API and return validation results.
    Parameters:
        business_name (str): Company name to assist suite parsing.
        address (str): Primary street address (e.g., '123 Main St'). Required.
        address_2 (str): Secondary address info (e.g., 'C/O John Smith').
        city (str): City name; required if PostalCode not provided.
        state (str): State code or full name; required if PostalCode not provided.
        postal_code (str): 5- or 9-digit ZIP; required if City/State not provided.
        license_key (str): Service Objects license key.
        is_live (bool): True for production endpoints, False for trial URL.
    Returns:
        dict: Parsed JSON response with address addresss or error details.
    Raises:
        RuntimeError: If the API returns an error payload.
        requests.RequestException: On network/HTTP failures (trial mode).
    """
    # Prepare query parameters for AV3 API
    params = {
        'BusinessName': business_name,
        'Address'     : address,
        'Address2'    : address_2,
        'City'        : city,
        'State'       : state,
        'PostalCode'  : postal_code,
        '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, 'TypeCode', None) != "3"):
            if is_live:
                # Try backup URL when live
                response = requests.get(backup_url, params=params, timeout=10)
                data = response.json()
                # If still error, propagate exception
                if 'Error' in data:
                    raise RuntimeError(f"AV3 service error: {data['Error']}")
            else:
                # Trial mode should not fallback; error is terminal
                raise RuntimeError(f"AV3 trial error: {data['Error']}")
        # Success: return parsed JSON data
        return data
    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"AV3 backup error: {data['Error']}") from req_exc
                return data
            except Exception as backup_exc:
                # Both primary and backup failed; escalate
                raise RuntimeError("AV3 service unreachable on both endpoints") from backup_exc
        else:
            # In trial mode, propagate the network exception
            raise RuntimeError(f"AV3 trial error: {str(req_exc)}") from req_exc
Address Validation 3 NodeJS REST Code Snippet
import axios from 'axios';
import querystring from 'querystring';
import { GetBestMatchesResponse } from './av3_response.js';
/**
 * @constant
 * @type {string}
 * @description The base URL for the live ServiceObjects Address Validation (AV3) API service.
 */
/**
 * @constant
 * @type {string}
 * @description The base URL for the backup ServiceObjects Address Validation (AV3) API service.
 */
/**
 * @constant
 * @type {string}
 * @description The base URL for the trial ServiceObjects Address Validation (AV3) API service.
 */
/**
 * <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 GetBestMatches 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}GetBestMatchesJson?${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<GetBestMatchesResponse>">A promise that resolves to a GetBestMatchesResponse 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 GetBestMatchesResponse(response.data);
    } catch (error) {
        throw new Error(`HTTP request failed: ${error.message}`);
    }
};
/**
 * <summary>
 * Provides functionality to call the ServiceObjects Address Validation (AV3) API's GetBestMatches endpoint,
 * retrieving validated and corrected address information for a given U.S. address with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
const GetBestMatchesClient = {
    /**
     * <summary>
     * Asynchronously invokes the GetBestMatches 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 - Name of business associated with this address. Optional.
     * @param {string} Address - Address line of the address to validate (e.g., "123 Main Street"). Required.
     * @param {string} Address2 - Additional address information (e.g., "C/O John Smith"). Optional.
     * @param {string} City - The city of the address to validate (e.g., "New York"). Optional, but required if PostalCode is not provided.
     * @param {string} State - The state of the address to validate (e.g., "NY"). Optional, but required if PostalCode is not provided.
     * @param {string} PostalCode - The zip code of the address to validate. Optional, but required if City and State are not provided.
     * @param {string} LicenseKey - Your license key to use the service. Required.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers. Defaults to true.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service. Defaults to 15.
     * @returns {Promise<GetBestMatchesResponse>} - A promise that resolves to a GetBestMatchesResponse object.
     */
    async invokeAsync(BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive = true, timeoutSeconds = 15) {
        const params = {
            BusinessName,
            Address,
            Address2,
            City,
            State,
            PostalCode,
            LicenseKey
        };
        // Remove null/undefined params to avoid empty query params
        Object.keys(params).forEach(key => params[key] == null && delete params[key]);
        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 GetBestMatches API endpoint by wrapping the async call
     * and awaiting its result immediately.
     * </summary>
     * @param {string} BusinessName - Name of business associated with this address. Optional.
     * @param {string} Address - Address line of the address to validate (e.g., "123 Main Street"). Required.
     * @param {string} Address2 - Additional address information (e.g., "C/O John Smith"). Optional.
     * @param {string} City - The city of the address to validate (e.g., "New York"). Optional, but required if PostalCode is not provided.
     * @param {string} State - The state of the address to validate (e.g., "NY"). Optional, but required if PostalCode is not provided.
     * @param {string} PostalCode - The zip code of the address to validate. Optional, but required if City and State are not provided.
     * @param {string} LicenseKey - Your license key to use the service. Required.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers. Defaults to true.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service. Defaults to 15.
     * @returns {GetBestMatchesResponse} - A GetBestMatchesResponse object with validated address details or an error.
     */
    invoke(BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive = true, timeoutSeconds = 15) {
        return (async () => await this.invokeAsync(
            BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive, timeoutSeconds
        ))();
    }
};
export { GetBestMatchesClient, GetBestMatchesResponse };
export class Address {
    constructor(data = {}) {
        this.Address1 = data.Address1;
        this.Address2 = data.Address2;
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.IsResidential = data.IsResidential;
        this.DPV = data.DPV;
        this.DPVDesc = data.DPVDesc;
        this.DPVNotes = data.DPVNotes;
        this.DPVNotesDesc = data.DPVNotesDesc;
        this.Corrections = data.Corrections;
        this.CorrectionsDesc = data.CorrectionsDesc;
        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.Fragment = data.Fragment;
        this.FragmentPMBPrefix = data.FragmentPMBPrefix;
        this.FragmentPMBNumber = data.FragmentPMBNumber;
    }
    toString() {
        return `Address1: ${this.Address1}, ` +
            `Address2: ${this.Address2}, ` +
            `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `IsResidential: ${this.IsResidential}, ` +
            `DPV: ${this.DPV}, ` +
            `DPVDesc: ${this.DPVDesc}, ` +
            `DPVNotes: ${this.DPVNotes}, ` +
            `DPVNotesDesc: ${this.DPVNotesDesc}, ` +
            `Corrections: ${this.Corrections}, ` +
            `CorrectionsDesc: ${this.CorrectionsDesc}, ` +
            `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}, ` +
            `Fragment: ${this.Fragment}, ` +
            `FragmentPMBPrefix: ${this.FragmentPMBPrefix}, ` +
            `FragmentPMBNumber: ${this.FragmentPMBNumber}`;
    }
}
export class Error {
    constructor(data = {}) {
        this.Type = data.Type;
        this.TypeCode = data.TypeCode;
        this.Desc = data.Desc;
        this.DescCode = data.DescCode;
    }
    toString() {
        return `Type: ${this.Type}, TypeCode: ${this.TypeCode}, Desc: ${this.Desc}, DescCode: ${this.DescCode}`;
    }
}
export class GetBestMatchesResponse {
    constructor(data = {}) {
        this.Addresses = (data.Addresses || []).map(address => new Address(address));
        this.IsCASS = data.IsCASS;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const addressesString = this.Addresses.length
            ? this.Addresses.map(address => address.toString()).join('\n')
            : 'None';
        return `GetBestMatchesResponse:\n` +
            `Addresses: [${addressesString}], ` +
            `IsCASS: ${this.IsCASS}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class GetSecondaryNumbersResponse {
    constructor(data = {}) {
        this.Address1 = data.Address1;
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.SecondaryNumbers = data.SecondaryNumbers || [];
        this.TotalCount = data.TotalCount || 0;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const secondaryNumbersString = this.SecondaryNumbers.length
            ? this.SecondaryNumbers.join(', ')
            : 'None';
        return `GetSecondaryNumbersResponse:\n` +
            `Address1: ${this.Address1}, ` +
            `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `SecondaryNumbers: [${secondaryNumbersString}], ` +
            `TotalCount: ${this.TotalCount}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class CityStateZip {
    constructor(data = {}) {
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.GeneralDeliveryService = data.GeneralDeliveryService;
        this.POBoxService = data.POBoxService;
        this.StreetService = data.StreetService;
        this.RRHCService = data.RRHCService;
        this.UrbanizationService = data.UrbanizationService;
        this.POBoxRangeLow = data.POBoxRangeLow;
        this.POBoxRangeHigh = data.POBoxRangeHigh;
        this.IsUniqueZipCode = data.IsUniqueZipCode;
    }
    toString() {
        return `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `GeneralDeliveryService: ${this.GeneralDeliveryService}, ` +
            `POBoxService: ${this.POBoxService}, ` +
            `StreetService: ${this.StreetService}, ` +
            `RRHCService: ${this.RRHCService}, ` +
            `UrbanizationService: ${this.UrbanizationService}, ` +
            `POBoxRangeLow: ${this.POBoxRangeLow}, ` +
            `POBoxRangeHigh: ${this.POBoxRangeHigh}, ` +
            `IsUniqueZipCode: ${this.IsUniqueZipCode}`;
    }
}
export class ValidateCityStateZipResponse {
    constructor(data = {}) {
        this.CityStateZip = data.CityStateZip ? new CityStateZip(data.CityStateZip) : null;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        return `ValidateCityStateZipResponse:\n` +
            `CityStateZip: ${this.CityStateZip ? this.CityStateZip.toString() : 'null'}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class GetBestMatchesSingleLineResponse {
    constructor(data = {}) {
        this.Addresses = (data.Addresses || []).map(address => new Address(address));
        this.IsCASS = data.IsCASS;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const addressesString = this.Addresses.length
            ? this.Addresses.map(address => address.toString()).join('\n')
            : 'None';
        return `GetBestMatchesSingleLineResponse:\n` +
            `Addresses: [${addressesString}], ` +
            `IsCASS: ${this.IsCASS}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export default {
    GetBestMatchesResponse,
    GetSecondaryNumbersResponse,
    ValidateCityStateZipResponse,
    GetBestMatchesSingleLineResponse
};
import axios from 'axios';
import querystring from 'querystring';
import { GetBestMatchesResponse } from './av3_response.js';
/**
 * @constant
 * @type {string}
 * @description The base URL for the live ServiceObjects Address Validation (AV3) API service.
 */
const LiveBaseUrl = 'https://sws.serviceobjects.com/av3/api.svc/json/';
/**
 * @constant
 * @type {string}
 * @description The base URL for the backup ServiceObjects Address Validation (AV3) API service.
 */
const BackupBaseUrl = 'https://swsbackup.serviceobjects.com/av3/api.svc/json/';
/**
 * @constant
 * @type {string}
 * @description The base URL for the trial ServiceObjects Address Validation (AV3) API service.
 */
const TrialBaseUrl = 'https://trial.serviceobjects.com/av3/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 GetBestMatches 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}GetBestMatchesJson?${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<GetBestMatchesResponse>">A promise that resolves to a GetBestMatchesResponse 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 GetBestMatchesResponse(response.data);
    } catch (error) {
        throw new Error(`HTTP request failed: ${error.message}`);
    }
};
/**
 * <summary>
 * Provides functionality to call the ServiceObjects Address Validation (AV3) API's GetBestMatches endpoint,
 * retrieving validated and corrected address information for a given U.S. address with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
const GetBestMatchesClient = {
    /**
     * <summary>
     * Asynchronously invokes the GetBestMatches 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 - Name of business associated with this address. Optional.
     * @param {string} Address - Address line of the address to validate (e.g., "123 Main Street"). Required.
     * @param {string} Address2 - Additional address information (e.g., "C/O John Smith"). Optional.
     * @param {string} City - The city of the address to validate (e.g., "New York"). Optional, but required if PostalCode is not provided.
     * @param {string} State - The state of the address to validate (e.g., "NY"). Optional, but required if PostalCode is not provided.
     * @param {string} PostalCode - The zip code of the address to validate. Optional, but required if City and State are not provided.
     * @param {string} LicenseKey - Your license key to use the service. Required.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers. Defaults to true.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service. Defaults to 15.
     * @returns {Promise<GetBestMatchesResponse>} - A promise that resolves to a GetBestMatchesResponse object.
     */
    async invokeAsync(BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive = true, timeoutSeconds = 15) {
        const params = {
            BusinessName,
            Address,
            Address2,
            City,
            State,
            PostalCode,
            LicenseKey
        };
        // Remove null/undefined params to avoid empty query params
        Object.keys(params).forEach(key => params[key] == null && delete params[key]);
        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 GetBestMatches API endpoint by wrapping the async call
     * and awaiting its result immediately.
     * </summary>
     * @param {string} BusinessName - Name of business associated with this address. Optional.
     * @param {string} Address - Address line of the address to validate (e.g., "123 Main Street"). Required.
     * @param {string} Address2 - Additional address information (e.g., "C/O John Smith"). Optional.
     * @param {string} City - The city of the address to validate (e.g., "New York"). Optional, but required if PostalCode is not provided.
     * @param {string} State - The state of the address to validate (e.g., "NY"). Optional, but required if PostalCode is not provided.
     * @param {string} PostalCode - The zip code of the address to validate. Optional, but required if City and State are not provided.
     * @param {string} LicenseKey - Your license key to use the service. Required.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers. Defaults to true.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service. Defaults to 15.
     * @returns {GetBestMatchesResponse} - A GetBestMatchesResponse object with validated address details or an error.
     */
    invoke(BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive = true, timeoutSeconds = 15) {
        return (async () => await this.invokeAsync(
            BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive, timeoutSeconds
        ))();
    }
};
export { GetBestMatchesClient, GetBestMatchesResponse };
export class Address {
    constructor(data = {}) {
        this.Address1 = data.Address1;
        this.Address2 = data.Address2;
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.IsResidential = data.IsResidential;
        this.DPV = data.DPV;
        this.DPVDesc = data.DPVDesc;
        this.DPVNotes = data.DPVNotes;
        this.DPVNotesDesc = data.DPVNotesDesc;
        this.Corrections = data.Corrections;
        this.CorrectionsDesc = data.CorrectionsDesc;
        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.Fragment = data.Fragment;
        this.FragmentPMBPrefix = data.FragmentPMBPrefix;
        this.FragmentPMBNumber = data.FragmentPMBNumber;
    }
    toString() {
        return `Address1: ${this.Address1}, ` +
            `Address2: ${this.Address2}, ` +
            `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `IsResidential: ${this.IsResidential}, ` +
            `DPV: ${this.DPV}, ` +
            `DPVDesc: ${this.DPVDesc}, ` +
            `DPVNotes: ${this.DPVNotes}, ` +
            `DPVNotesDesc: ${this.DPVNotesDesc}, ` +
            `Corrections: ${this.Corrections}, ` +
            `CorrectionsDesc: ${this.CorrectionsDesc}, ` +
            `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}, ` +
            `Fragment: ${this.Fragment}, ` +
            `FragmentPMBPrefix: ${this.FragmentPMBPrefix}, ` +
            `FragmentPMBNumber: ${this.FragmentPMBNumber}`;
    }
}
export class Error {
    constructor(data = {}) {
        this.Type = data.Type;
        this.TypeCode = data.TypeCode;
        this.Desc = data.Desc;
        this.DescCode = data.DescCode;
    }
    toString() {
        return `Type: ${this.Type}, TypeCode: ${this.TypeCode}, Desc: ${this.Desc}, DescCode: ${this.DescCode}`;
    }
}
export class GetBestMatchesResponse {
    constructor(data = {}) {
        this.Addresses = (data.Addresses || []).map(address => new Address(address));
        this.IsCASS = data.IsCASS;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const addressesString = this.Addresses.length
            ? this.Addresses.map(address => address.toString()).join('\n')
            : 'None';
        return `GetBestMatchesResponse:\n` +
            `Addresses: [${addressesString}], ` +
            `IsCASS: ${this.IsCASS}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class GetSecondaryNumbersResponse {
    constructor(data = {}) {
        this.Address1 = data.Address1;
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.SecondaryNumbers = data.SecondaryNumbers || [];
        this.TotalCount = data.TotalCount || 0;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const secondaryNumbersString = this.SecondaryNumbers.length
            ? this.SecondaryNumbers.join(', ')
            : 'None';
        return `GetSecondaryNumbersResponse:\n` +
            `Address1: ${this.Address1}, ` +
            `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `SecondaryNumbers: [${secondaryNumbersString}], ` +
            `TotalCount: ${this.TotalCount}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class CityStateZip {
    constructor(data = {}) {
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.GeneralDeliveryService = data.GeneralDeliveryService;
        this.POBoxService = data.POBoxService;
        this.StreetService = data.StreetService;
        this.RRHCService = data.RRHCService;
        this.UrbanizationService = data.UrbanizationService;
        this.POBoxRangeLow = data.POBoxRangeLow;
        this.POBoxRangeHigh = data.POBoxRangeHigh;
        this.IsUniqueZipCode = data.IsUniqueZipCode;
    }
    toString() {
        return `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `GeneralDeliveryService: ${this.GeneralDeliveryService}, ` +
            `POBoxService: ${this.POBoxService}, ` +
            `StreetService: ${this.StreetService}, ` +
            `RRHCService: ${this.RRHCService}, ` +
            `UrbanizationService: ${this.UrbanizationService}, ` +
            `POBoxRangeLow: ${this.POBoxRangeLow}, ` +
            `POBoxRangeHigh: ${this.POBoxRangeHigh}, ` +
            `IsUniqueZipCode: ${this.IsUniqueZipCode}`;
    }
}
export class ValidateCityStateZipResponse {
    constructor(data = {}) {
        this.CityStateZip = data.CityStateZip ? new CityStateZip(data.CityStateZip) : null;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        return `ValidateCityStateZipResponse:\n` +
            `CityStateZip: ${this.CityStateZip ? this.CityStateZip.toString() : 'null'}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class GetBestMatchesSingleLineResponse {
    constructor(data = {}) {
        this.Addresses = (data.Addresses || []).map(address => new Address(address));
        this.IsCASS = data.IsCASS;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const addressesString = this.Addresses.length
            ? this.Addresses.map(address => address.toString()).join('\n')
            : 'None';
        return `GetBestMatchesSingleLineResponse:\n` +
            `Addresses: [${addressesString}], ` +
            `IsCASS: ${this.IsCASS}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export default {
    GetBestMatchesResponse,
    GetSecondaryNumbersResponse,
    ValidateCityStateZipResponse,
    GetBestMatchesSingleLineResponse
};
import axios from 'axios';
import querystring from 'querystring';
import { GetBestMatchesResponse } from './av3_response.js';
/**
 * @constant
 * @type {string}
 * @description The base URL for the live ServiceObjects Address Validation (AV3) API service.
 */
const LiveBaseUrl = 'https://sws.serviceobjects.com/av3/api.svc/json/';
/**
 * @constant
 * @type {string}
 * @description The base URL for the backup ServiceObjects Address Validation (AV3) API service.
 */
const BackupBaseUrl = 'https://swsbackup.serviceobjects.com/av3/api.svc/json/';
/**
 * @constant
 * @type {string}
 * @description The base URL for the trial ServiceObjects Address Validation (AV3) API service.
 */
const TrialBaseUrl = 'https://trial.serviceobjects.com/av3/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 GetBestMatches 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}GetBestMatchesJson?${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<GetBestMatchesResponse>">A promise that resolves to a GetBestMatchesResponse 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 GetBestMatchesResponse(response.data);
    } catch (error) {
        throw new Error(`HTTP request failed: ${error.message}`);
    }
};
/**
 * <summary>
 * Provides functionality to call the ServiceObjects Address Validation (AV3) API's GetBestMatches endpoint,
 * retrieving validated and corrected address information for a given U.S. address with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
const GetBestMatchesClient = {
    /**
     * <summary>
     * Asynchronously invokes the GetBestMatches 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 - Name of business associated with this address. Optional.
     * @param {string} Address - Address line of the address to validate (e.g., "123 Main Street"). Required.
     * @param {string} Address2 - Additional address information (e.g., "C/O John Smith"). Optional.
     * @param {string} City - The city of the address to validate (e.g., "New York"). Optional, but required if PostalCode is not provided.
     * @param {string} State - The state of the address to validate (e.g., "NY"). Optional, but required if PostalCode is not provided.
     * @param {string} PostalCode - The zip code of the address to validate. Optional, but required if City and State are not provided.
     * @param {string} LicenseKey - Your license key to use the service. Required.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers. Defaults to true.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service. Defaults to 15.
     * @returns {Promise<GetBestMatchesResponse>} - A promise that resolves to a GetBestMatchesResponse object.
     */
    async invokeAsync(BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive = true, timeoutSeconds = 15) {
        const params = {
            BusinessName,
            Address,
            Address2,
            City,
            State,
            PostalCode,
            LicenseKey
        };
        // Remove null/undefined params to avoid empty query params
        Object.keys(params).forEach(key => params[key] == null && delete params[key]);
        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 GetBestMatches API endpoint by wrapping the async call
     * and awaiting its result immediately.
     * </summary>
     * @param {string} BusinessName - Name of business associated with this address. Optional.
     * @param {string} Address - Address line of the address to validate (e.g., "123 Main Street"). Required.
     * @param {string} Address2 - Additional address information (e.g., "C/O John Smith"). Optional.
     * @param {string} City - The city of the address to validate (e.g., "New York"). Optional, but required if PostalCode is not provided.
     * @param {string} State - The state of the address to validate (e.g., "NY"). Optional, but required if PostalCode is not provided.
     * @param {string} PostalCode - The zip code of the address to validate. Optional, but required if City and State are not provided.
     * @param {string} LicenseKey - Your license key to use the service. Required.
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers. Defaults to true.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service. Defaults to 15.
     * @returns {GetBestMatchesResponse} - A GetBestMatchesResponse object with validated address details or an error.
     */
    invoke(BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive = true, timeoutSeconds = 15) {
        return (async () => await this.invokeAsync(
            BusinessName, Address, Address2, City, State, PostalCode, LicenseKey, isLive, timeoutSeconds
        ))();
    }
};
export { GetBestMatchesClient, GetBestMatchesResponse };
export class Address {
    constructor(data = {}) {
        this.Address1 = data.Address1;
        this.Address2 = data.Address2;
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.IsResidential = data.IsResidential;
        this.DPV = data.DPV;
        this.DPVDesc = data.DPVDesc;
        this.DPVNotes = data.DPVNotes;
        this.DPVNotesDesc = data.DPVNotesDesc;
        this.Corrections = data.Corrections;
        this.CorrectionsDesc = data.CorrectionsDesc;
        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.Fragment = data.Fragment;
        this.FragmentPMBPrefix = data.FragmentPMBPrefix;
        this.FragmentPMBNumber = data.FragmentPMBNumber;
    }
    toString() {
        return `Address1: ${this.Address1}, ` +
            `Address2: ${this.Address2}, ` +
            `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `IsResidential: ${this.IsResidential}, ` +
            `DPV: ${this.DPV}, ` +
            `DPVDesc: ${this.DPVDesc}, ` +
            `DPVNotes: ${this.DPVNotes}, ` +
            `DPVNotesDesc: ${this.DPVNotesDesc}, ` +
            `Corrections: ${this.Corrections}, ` +
            `CorrectionsDesc: ${this.CorrectionsDesc}, ` +
            `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}, ` +
            `Fragment: ${this.Fragment}, ` +
            `FragmentPMBPrefix: ${this.FragmentPMBPrefix}, ` +
            `FragmentPMBNumber: ${this.FragmentPMBNumber}`;
    }
}
export class Error {
    constructor(data = {}) {
        this.Type = data.Type;
        this.TypeCode = data.TypeCode;
        this.Desc = data.Desc;
        this.DescCode = data.DescCode;
    }
    toString() {
        return `Type: ${this.Type}, TypeCode: ${this.TypeCode}, Desc: ${this.Desc}, DescCode: ${this.DescCode}`;
    }
}
export class GetBestMatchesResponse {
    constructor(data = {}) {
        this.Addresses = (data.Addresses || []).map(address => new Address(address));
        this.IsCASS = data.IsCASS;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const addressesString = this.Addresses.length
            ? this.Addresses.map(address => address.toString()).join('\n')
            : 'None';
        return `GetBestMatchesResponse:\n` +
            `Addresses: [${addressesString}], ` +
            `IsCASS: ${this.IsCASS}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class GetSecondaryNumbersResponse {
    constructor(data = {}) {
        this.Address1 = data.Address1;
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.SecondaryNumbers = data.SecondaryNumbers || [];
        this.TotalCount = data.TotalCount || 0;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const secondaryNumbersString = this.SecondaryNumbers.length
            ? this.SecondaryNumbers.join(', ')
            : 'None';
        return `GetSecondaryNumbersResponse:\n` +
            `Address1: ${this.Address1}, ` +
            `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `SecondaryNumbers: [${secondaryNumbersString}], ` +
            `TotalCount: ${this.TotalCount}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class CityStateZip {
    constructor(data = {}) {
        this.City = data.City;
        this.State = data.State;
        this.Zip = data.Zip;
        this.GeneralDeliveryService = data.GeneralDeliveryService;
        this.POBoxService = data.POBoxService;
        this.StreetService = data.StreetService;
        this.RRHCService = data.RRHCService;
        this.UrbanizationService = data.UrbanizationService;
        this.POBoxRangeLow = data.POBoxRangeLow;
        this.POBoxRangeHigh = data.POBoxRangeHigh;
        this.IsUniqueZipCode = data.IsUniqueZipCode;
    }
    toString() {
        return `City: ${this.City}, ` +
            `State: ${this.State}, ` +
            `Zip: ${this.Zip}, ` +
            `GeneralDeliveryService: ${this.GeneralDeliveryService}, ` +
            `POBoxService: ${this.POBoxService}, ` +
            `StreetService: ${this.StreetService}, ` +
            `RRHCService: ${this.RRHCService}, ` +
            `UrbanizationService: ${this.UrbanizationService}, ` +
            `POBoxRangeLow: ${this.POBoxRangeLow}, ` +
            `POBoxRangeHigh: ${this.POBoxRangeHigh}, ` +
            `IsUniqueZipCode: ${this.IsUniqueZipCode}`;
    }
}
export class ValidateCityStateZipResponse {
    constructor(data = {}) {
        this.CityStateZip = data.CityStateZip ? new CityStateZip(data.CityStateZip) : null;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        return `ValidateCityStateZipResponse:\n` +
            `CityStateZip: ${this.CityStateZip ? this.CityStateZip.toString() : 'null'}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export class GetBestMatchesSingleLineResponse {
    constructor(data = {}) {
        this.Addresses = (data.Addresses || []).map(address => new Address(address));
        this.IsCASS = data.IsCASS;
        this.Error = data.Error ? new Error(data.Error) : null;
    }
    toString() {
        const addressesString = this.Addresses.length
            ? this.Addresses.map(address => address.toString()).join('\n')
            : 'None';
        return `GetBestMatchesSingleLineResponse:\n` +
            `Addresses: [${addressesString}], ` +
            `IsCASS: ${this.IsCASS}, ` +
            `Error: ${this.Error ? this.Error.toString() : 'null'}`;
    }
}
export default {
    GetBestMatchesResponse,
    GetSecondaryNumbersResponse,
    ValidateCityStateZipResponse,
    GetBestMatchesSingleLineResponse
};