Address Geocode – International C# Code Snippet

using address_geocode_international_dot_net.REST;
using AGIService;
using System;
using System.Text.Json;
using System.Threading.Tasks;

namespace address_geocode_international_dot_net.SOAP
{
    /// <summary>
    /// SOAP client wrapper for the ServiceObjects AGI PlaceSearch operation.
    /// Handles SOAP requests to primary and backup endpoints, supporting both trial and live modes.
    /// Provides async invocation with resiliency via failover mechanism.
    /// </summary>
    public class PlaceSearchValidation
    {
        private const string LiveBaseUrl = "https://sws.serviceobjects.com/AGI/soap.svc/SOAP";
        private const string BackupBaseUrl = "https://wsbackup.serviceobjects.com/AGI/soap.svc/SOAP";
        private const string TrailBaseUrl = "https://trial.serviceobjects.com/AGI/soap.svc/SOAP";

        private readonly string _primaryUrl;
        private readonly string _backupUrl;
        private readonly int _timeoutMs;
        private readonly bool _isLive;

        /// <summary>
        /// Initializes the PlaceSearch SOAP client with endpoint configuration based on mode (live or trial).
        /// </summary>
        /// <param name="isLive">True for live (production) endpoint; false for trial usage.</param>
        public PlaceSearchValidation(bool isLive)
        {
            // Read timeout (milliseconds) and isLive flag
            _timeoutMs = 10000;
            _isLive = isLive;

            // Depending on isLive, pick the correct SOAP endpoint URLs
            if (_isLive)
            {
                _primaryUrl = LiveBaseUrl;
                _backupUrl = BackupBaseUrl;
            }
            else
            {
                _primaryUrl = TrailBaseUrl;
                _backupUrl = TrailBaseUrl;
            }

            if (string.IsNullOrWhiteSpace(_primaryUrl))
                throw new InvalidOperationException("Primary URL not set. Check endpoint configuration.");

            if (string.IsNullOrWhiteSpace(_backupUrl))
                throw new InvalidOperationException("Backup URL not set. Check endpoint configuration.");
        }


        /// <summary>
        /// Executes the PlaceSearch SOAP request asynchronously.
        /// Attempts call to primary endpoint first; upon failure, retries with backup URL.
        /// </summary>
        /// <param name="SingleLine">Single-line address input. - Optional</param>
        /// <param name="Address1">Address line 1. - Optional</param>
        /// <param name="Address2">Address line 2. - Optional</param>
        /// <param name="Address3">Address line 3. - Optional</param>
        /// <param name="Address4">Address line 4. - Optional</param>
        /// <param name="Address5">Address line 5. - Optional</param>
        /// <param name="Locality">City or locality. - Optional</param>
        /// <param name="AdministrativeArea">State or province. - Optional</param>
        /// <param name="PostalCode">Zip or postal code. - Optional</param>
        /// <param name="Country">Country ISO code (e.g., "US", "CA"). - Optional</param>
        /// <param name="Boundaries">Geolocation search boundaries. - Optional</param>
        /// <param name="MaxResults">Maximum number of results to return. - Optional</param>
        /// <param name="SearchType">Specifies place type to search for. - Optional</param>
        /// <param name="Extras">Additional search attributes. - Optional</param>
        /// <param name="LicenseKey">Service Objects license key (live or trial). - Required</param>
        /// <returns><see cref="ResponseObject"/> containing matched place search results.</returns>
        /// <exception cref="Exception">Throws when both primary and backup endpoints fail.</exception>
        public async Task<ResponseObject> PlaceSearch(
            string SingleLine,
            string Address1,
            string Address2,
            string Address3,
            string Address4,
            string Address5,
            string Locality,
            string AdministrativeArea,
            string PostalCode,
            string Country,
            string Boundaries,
            string MaxResults,
            string SearchType,
            string Extras,
            string LicenseKey)
        {
            AGISoapServiceClient clientPrimary = null;
            AGISoapServiceClient clientBackup = null;

            try
            {
                // 1) Attempt Primary
                clientPrimary = new AGISoapServiceClient();
                clientPrimary.Endpoint.Address = new System.ServiceModel.EndpointAddress(_primaryUrl);
                clientPrimary.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);
                
                
                ResponseObject response = await clientPrimary.PlaceSearchAsync(
                    SingleLine,
                    Address1,
                    Address2,
                    Address3,
                    Address4,
                    Address5,
                    Locality,
                    AdministrativeArea,
                    PostalCode,
                    Country,
                    Boundaries,
                    MaxResults,
                    SearchType,
                    Extras,
                    LicenseKey
                ).ConfigureAwait(false);

                // Client requirement: failover only if response is null or error TypeCode == "3"
                if (response == null)
                {
                    throw new InvalidOperationException("Primary endpoint returned null or a fatal TypeCode=3 error for PlaceSearch");
                }

                return response;
            }
            catch (Exception primaryEx)
            {
                try
                {
                    // 2) Fallback: Attempt Backup
                    clientBackup = new AGISoapServiceClient();
                    clientBackup.Endpoint.Address = new System.ServiceModel.EndpointAddress(_backupUrl);
                    clientBackup.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);

                    return await clientBackup.PlaceSearchAsync(
                        SingleLine,
                        Address1,
                        Address2,
                        Address3,
                        Address4,
                        Address5,
                        Locality,
                        AdministrativeArea,
                        PostalCode,
                        Country,
                        Boundaries,
                        MaxResults,
                        SearchType,
                        Extras,
                        LicenseKey
                    ).ConfigureAwait(false);
                }
                catch (Exception backupEx)
                {
                    throw new Exception(
                        "Both primary and backup PlaceSearch endpoints failed.\n" +
                        "Primary error: " + primaryEx.Message + "\n" +
                        "Backup error: " + backupEx.Message);
                }
                finally
                {
                    clientBackup?.Close();
                }
            }
            finally
            {
                clientPrimary?.Close();
            }
        }


    }
}

Address Geocode – International Python Code Snippet

from suds.client import Client
from suds import WebFault
from suds.sudsobject import Object

class PlaceSearch:
    def __init__(self, license_key: str, is_live: bool, timeout_ms: int = 10000):
        """
        Initialize the PlaceSearch SOAP client.

        Parameters:
            license_key (str): Service Objects Address Geocode International license key.
            is_live (bool): whether to use live or trial endpoints.
            timeout_ms (int): SOAP call timeout in milliseconds.
        """
        self._timeout_s = timeout_ms / 1000.0
        self.license_key = license_key
        self._is_live = is_live

        # WSDL URLs
        self._primary_wsdl = (
            "https://sws.serviceobjects.com/AGI/soap.svc?wsdl"
            if is_live
            else "https://trial.serviceobjects.com/AGI/soap.svc?wsdl"
        )
        self._backup_wsdl = (
            "https://swsbackup.serviceobjects.com/AGI/soap.svc?wsdl"
            if is_live
            else "https://trialbackup.serviceobjects.com/AGI/soap.svc?wsdl"
        )

    def place_search(self,
                    single_line: str,
                    address1: str,
                    address2: str,
                    address3: str,
                    address4: str,
                    address5: str,
                    locality: str,
                    administrative_area: str,
                    postal_code: str,
                    country: str,
                    max_results: int,
                    search_type: str,
                    boundaries: str,
                    extras: str) -> Object:
        """
        Call PlaceSearch SOAP API using primary endpoint; on None response,
        WebFault, or Error.TypeCode == '3' falls back to the backup endpoint.
        returns: the suds response object
        raises RuntimeError: if both endpoints fail
        """

        # Common kwargs for both calls
        call_kwargs = dict(
            SingleLine=single_line,
            Address1=address1,
            Address2=address2,
            Address3=address3,
            Address4=address4,
            Address5=address5,
            Locality=locality,
            AdministrativeArea=administrative_area,
            PostalCode=postal_code,
            Country=country,
            MaxResults=max_results,
            SearchType=search_type,
            Boundaries=boundaries,
            Extras=extras,
            LicenseKey=self.license_key,
        )

        # Attempt primary
        try:
            client = Client(self._primary_wsdl)

            # Override endpoint URL if needed:
            response = client.service.PlaceSearch(**call_kwargs)

            # If response is None or fatal error code, trigger fallback
            if response is None or (hasattr(response, 'Error') and response.Error and response.Error.TypeCode == '3'):
                raise ValueError("Primary returned no result or fatal Error.TypeCode=3")

            return response

        except (WebFault, ValueError, Exception) as primary_ex:
            try:
                # Attempt backup
                client = Client(self._backup_wsdl, timeout=self._timeout_s)
                response = client.service.GetBestMatches(**call_kwargs)
                if response is None:
                    raise ValueError("Backup returned no result")
                return response

            except (WebFault, Exception) as backup_ex:
                msg = (
                    "Both primary and backup endpoints failed.\n"
                    f"Primary error: {primary_ex}\n"
                    f"Backup error: {backup_ex}"
                )
                raise RuntimeError(msg)

Address Geocode – International NodeJS Code Snippet

import { soap } from 'strong-soap';

/**
 * <summary>
 * A class that provides functionality to call the ServiceObjects Address Geocode International (AGI) SOAP service's PlaceSearch endpoint,
 * retrieving geocoded location information for a given address or place with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
class PlaceSearchSoap {
    /**
     * <summary>
     * Initializes a new instance of the PlaceSearchSoap class with the provided input parameters,
     * setting up primary and backup WSDL URLs based on the live/trial mode.
     * </summary>
     * @param {string} SingleLine - The full address on one line. Optional; for best results, use parsed inputs.
     * @param {string} Address1 - Address Line 1 of the international address. Optional.
     * @param {string} Address2 - Address Line 2 of the international address. Optional.
     * @param {string} Address3 - Address Line 3 of the international address. Optional.
     * @param {string} Address4 - Address Line 4 of the international address. Optional.
     * @param {string} Address5 - Address Line 5 of the international address. Optional.
     * @param {string} Locality - The name of the locality (e.g., city, town). Optional.
     * @param {string} AdministrativeArea - The administrative area (e.g., state, province). Optional.
     * @param {string} PostalCode - The postal code. Optional.
     * @param {string} Country - The country name or ISO 3166-1 Alpha-2/Alpha-3 code. Required.
     * @param {string} Boundaries - A comma-delimited list of coordinates for search boundaries. Optional; not currently used.
     * @param {string} MaxResults - Maximum number of results (1-10). Defaults to '10'.
     * @param {string} SearchType - Type of search (e.g., 'BestMatch', 'All'). Defaults to 'BestMatch'.
     * @param {string} Extras - Comma-delimited list of extra features. Optional.
     * @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.
     * @throws {Error} Thrown if LicenseKey is empty or null.
     */
    constructor(SingleLine, Address1, Address2, Address3, Address4, Address5, Locality, AdministrativeArea, PostalCode, Country, Boundaries, MaxResults = '10', SearchType = 'BestMatch', Extras, LicenseKey, isLive = true, timeoutSeconds = 15) {
        this.args = {
            SingleLine,
            Address1,
            Address2,
            Address3,
            Address4,
            Address5,
            Locality,
            AdministrativeArea,
            PostalCode,
            Country,
            Boundaries,
            MaxResults,
            SearchType,
            Extras,
            LicenseKey
        };

        this.isLive = isLive;
        this.timeoutSeconds = timeoutSeconds;

        this.LiveBaseUrl = 'https://sws.serviceobjects.com/agi/soap.svc?wsdl';
        this.BackupBaseUrl = 'https://swsbackup.serviceobjects.com/agi/soap.svc?wsdl';
        this.TrialBaseUrl = 'https://trial.serviceobjects.com/agi/soap.svc?wsdl';

        this._primaryWsdl = this.isLive ? this.LiveBaseUrl : this.TrialBaseUrl;
        this._backupWsdl = this.isLive ? this.BackupBaseUrl : this.TrialBaseUrl;
    }

    /**
     * <summary>
     * Asynchronously calls the PlaceSearch SOAP endpoint, attempting the primary endpoint
     * first and falling back to the backup if the response is invalid (Error.TypeCode == '3') in live mode
     * or if the primary call fails.
     * </summary>
     * @returns {Promise<Object>} A promise that resolves to the raw SOAP response data containing geocoded location details or an error.
     * @throws {Error} Thrown if both primary and backup calls fail, with detailed error messages.
     */
    async invokeAsync() {
        try {
            const primaryResult = await this._callSoap(this._primaryWsdl, this.args);

            if (this.isLive && !this._isValid(primaryResult)) {
                console.warn("Primary returned Error.TypeCode == '3', falling back to backup...");
                const backupResult = await this._callSoap(this._backupWsdl, this.args);
                return backupResult;
            }

            return primaryResult;
        } catch (primaryErr) {
            try {
                console.warn("Primary call failed, falling back to backup...");
                const backupResult = await this._callSoap(this._backupWsdl, this.args);
                return backupResult;
            } catch (backupErr) {
                throw new Error(`Both primary and backup calls failed:\nPrimary: ${primaryErr.message}\nBackup: ${backupErr.message}`);
            }
        }
    }

    /**
     * <summary>
     * Performs a SOAP service call to the specified WSDL URL with the given arguments,
     * creating a client and returning the raw SOAP response data.
     * </summary>
     * @param {string} wsdlUrl - The WSDL URL of the SOAP service endpoint (primary or backup).
     * @param {Object} args - The arguments to pass to the PlaceSearch method.
     * @returns {Promise<Object>} A promise that resolves to the raw SOAP response data.
     * @throws {Error} Thrown if the SOAP client creation fails, the service call fails, or the response cannot be parsed.
     */
    _callSoap(wsdlUrl, args) {
        return new Promise((resolve, reject) => {
            soap.createClient(wsdlUrl, { timeout: this.timeoutSeconds * 1000 }, (err, client) => {
                if (err) return reject(err);

                client.PlaceSearch(args, (err, result) => {
                    const response = result?.PlaceSearchResult;
                    try {
                        if (!response) {
                            return reject(new Error("SOAP response is empty or undefined."));
                        }
                        resolve(response);
                    } catch (parseErr) {
                        reject(new Error(`Failed to parse SOAP response: ${parseErr.message}`));
                    }
                });
            });
        });
    }

    /**
     * <summary>
     * Checks if a SOAP response is valid by verifying that it exists and either has no Error object
     * or the Error.TypeCode is not equal to '3'.
     * </summary>
     * @param {Object} response - The raw response object to validate.
     * @returns {boolean} True if the response is valid, false otherwise.
     */
    _isValid(response) {
        return response && (!response.Error || response.Error.TypeCode !== '3');
    }
}

export { PlaceSearchSoap };