Educational

Complete Guide to Address Verification APIs in 2025

Everything you need to know about address verification APIs - from basic concepts to advanced implementation strategies with code examples.

Sthan.io Team
Sthan.io Team
October 28, 2025 · 12 min read
Complete Guide to Address Verification APIs

Let's face it—incorrect addresses cost businesses millions of dollars every year. From failed deliveries and returned packages to frustrated customers and damaged brand reputation, the impact of bad address data ripples through your entire operation.

Address verification APIs solve this problem by validating, standardizing, and enriching address data in real-time. Whether you're building an e-commerce checkout, a logistics platform, or a CRM system, integrating address verification is one of the smartest investments you can make.

In this comprehensive guide, we'll walk you through everything you need to know about address verification APIs—from the fundamentals to advanced implementation strategies with real code examples.

1. What is Address Verification?

Address verification (also called address validation) is the process of checking whether a given address exists and is deliverable. It involves comparing an address against authoritative databases maintained by postal services and other data providers.

A typical address verification process involves three key steps:

  • Parsing: Breaking down the address into components (street number, street name, city, state, ZIP code)
  • Validation: Checking if the address exists in authoritative databases
  • Standardization: Formatting the address according to postal service standards (e.g., USPS for US addresses)
Did you know? The USPS processes over 472 million pieces of mail every day. Standardized addresses help ensure your mail reaches its destination efficiently.

Real-World Examples: See It In Action

Address verification APIs can handle various types of input errors and incomplete data. Here are actual examples from Sthan.io demonstrating its powerful capabilities:

Correcting Misspelled Street Names & Cities

INPUT 1345 avvenue of americas new york ny
OUTPUT 1345 Avenue Of The Americas, New York, NY 10105-0302

INPUT 6000 j street sacrmento ca
OUTPUT 6000 J St, Sacramento, CA 95819-2605

INPUT 325 w broaddwey # m ny 10013
OUTPUT 325 W Broadway PH M, New York, NY 10013-1835

INPUT 3808 avenue faradiya davis ca
OUTPUT 3808 Faraday Ave, Davis, CA 95618

Filling in Missing State & ZIP Codes

INPUT 543 sanders road cross
OUTPUT 543 Sanders Rd, Cross, SC 29436

INPUT 1199 w shaw avenue fresno
OUTPUT 1199 W Shaw Ave, Fresno, CA 93711

Handling Abbreviated & Expanded Forms

The API intelligently handles both abbreviated and expanded forms of street types, directions, and states:

INPUT (Expanded) 1199 west shaw avenue fresno california
OUTPUT (Standardized) 1199 W Shaw Ave, Fresno, CA 93711

INPUT (Abbreviated) 6000 j st sacramento ca
OUTPUT (Standardized) 6000 J St, Sacramento, CA 95819-2605
Smart Standardization: Whether you input "Street" or "St", "Avenue" or "Ave", "West" or "W", "California" or "CA"—the API understands and standardizes to USPS format automatically.

Restructuring Out-of-Order Addresses

INPUT 1345 americas avenue NY
OUTPUT 1345 Ave Of The Americas, New York, NY 10105-0302

INPUT 10094 delware bay raod derdenelle
OUTPUT 10094 Delaware Bay Rd, Dardanelle, AR 72834-2609
Key Capabilities Demonstrated:
  • Corrects misspellings in street names, city names, and more
  • Adds missing ZIP codes (including ZIP+4 for precision)
  • Infers missing state information from context
  • Handles both abbreviated (St, Ave, W, CA) and expanded (Street, Avenue, West, California) forms
  • Restructures out-of-order address components
  • Standardizes formatting to USPS specifications

2. Why Address Verification Matters for Your Business

The business case for address verification is compelling. Here's what you stand to gain:

💰 Reduced Shipping Costs

Failed deliveries are expensive. Between returned packages, re-shipping costs, and customer service overhead, a single failed delivery can cost $10-$30. For businesses shipping thousands of orders per month, this adds up quickly.

📈 Improved Conversion Rates

Address autocomplete and validation reduce form friction during checkout. Studies show that optimized address entry can increase conversion rates by 15-25% and reduce cart abandonment significantly.

🎯 Better Data Quality

Clean, standardized address data improves your entire operation: more accurate analytics, better customer segmentation, reliable route optimization, and effective direct mail campaigns.

😊 Enhanced Customer Experience

When customers receive their orders on time at the correct address, they're more likely to become repeat buyers and recommend your business to others.

Real-World Example: E-commerce Success

A mid-sized e-commerce company processing 50,000 orders per month implemented address verification and saw:

  • Failed deliveries reduced from 3.5% to 0.4%
  • Annual shipping cost savings of $186,000
  • Checkout conversion rate improved by 18%
  • Customer satisfaction scores increased by 22 points

3. How Address Verification Works Behind the Scenes

Modern address verification APIs use sophisticated multi-layered approaches to ensure accuracy. Here's what happens when you submit an address to Sthan.io:

Step 1: Intelligent Address Parsing

The API first normalizes and parses your input address:

Input Address
123 main st apt 4b new york ny 10001
Parsed Components
Street Number: 123
Street Name: Main St
Unit: Apt 4B
City: New York
State: NY
ZIP: 10001

Key Features:

  • Unit Detection: Automatically identifies unit types (Apt, Suite, Unit, #) and numbers
  • Normalization: Removes commas, standardizes hyphens, and converts to lowercase for consistent matching
  • Smart Parsing: Handles various address formats and abbreviations

Step 2: Multi-Source Verification Strategy

Sthan.io uses a sophisticated parallel verification approach with multiple data sources:

Verification Flow (Executed in Parallel)

  1. Exact Match Lookup: Searches proprietary database (160M+ addresses) using high-performance open-source search engine for instant exact matches
  2. Census GeoCoding API: Validates against official U.S. Census Bureau data for geocoding and address components
  3. USPS Verification: Cross-references with official USPS data for postal accuracy and deliverability
  4. Intelligent Fuzzy Matching: Uses Levenshtein distance algorithm to match misspellings and variations (as fallback)
Why Multiple Sources? Using parallel verification ensures high accuracy even when addresses are misspelled or formatted inconsistently. The API returns the first successful match, typically in under 100ms.

Step 3: Advanced Fuzzy Matching (When Needed)

If exact matching fails, the API employs advanced algorithms:

  • Levenshtein Distance: Calculates edit distance to find closest matches to misspelled addresses
  • Word Permutations: Generates permutations of address words to handle word-order variations
  • Correctness Scoring: Ranks matches by percentage of correctness (based on character similarity)
  • Occurrence Analysis: When multiple matches have equal distance, selects the most frequently occurring address

Step 4: Standardization & Enrichment

The verified address is standardized and enriched with additional metadata:

USPS Standardization
  • Proper abbreviations (Street → ST, Avenue → AVE)
  • Capitalization per USPS standards
  • ZIP+4 code for delivery point precision
  • Carrier route information
Additional Enrichment
  • County name
  • Full address hash for de-duplication
  • USPS verification flag
  • Algorithm used (for transparency)

Performance & Rate Limiting

To ensure optimal performance and fair usage:

  • Batch Processing: Verify up to 10 addresses per request with automatic parallel processing
  • Concurrency Control: Intelligent throttling prevents overload (max 10 concurrent operations)
  • Timeout Protection: 120-second timeout ensures requests don't hang indefinitely
  • Rate Limiting: Per-user rate limits ensure fair access for all customers
Result: You get USPS-standardized, geocoded addresses with 95%+ accuracy, even for misspelled or incomplete inputs—all in under 100ms on average.

4. Choosing the Right Address Verification API

Not all address verification APIs are created equal. Here's what to consider when evaluating providers:

Key Selection Criteria

Criteria What to Look For
Data Coverage Does it cover your target countries? How frequently is data updated?
Accuracy CASS certification for US addresses; validation accuracy rate 95%+
Performance API response time under 100ms; high availability SLA (99.9%+)
Pricing Cost per lookup; volume discounts; free tier availability
Features Autocomplete, parsing, geocoding, international support
Developer Experience Clear documentation, SDKs, code samples, support quality
Tip: Start with a provider that offers a generous free tier so you can test thoroughly before committing. Sthan.io offers 100 free lookups per month—perfect for development and testing.

5. Implementation Guide with Code Examples

Let's get practical. Here's how to integrate address verification into your application using popular programming languages.

Getting Started

First, sign up for a free account at Sthan.io. You'll receive a profile name and password that you'll use to obtain authentication tokens.

Free Tier Available: Start with 100 free address verifications per month. No credit card required. Perfect for development, testing, or small-scale applications. Check the pricing page for paid plans with higher volumes.

Step 1: Obtain Authentication Token

Before making any API calls, you need to authenticate and obtain a bearer token. This token will be used for all subsequent API requests.

Get authentication token using fetch API:

async function getAuthToken(profileName, profilePassword) {
  const response = await fetch('https://api.sthan.io/auth/token', {
    method: 'GET',
    headers: {
      'profileName': profileName,
      'profilePassword': profilePassword
    }
  });

  if (!response.ok) {
    throw new Error(`Authentication failed: ${response.status}`);
  }

  const data = await response.json();
  return data.Result.access_token; // Extract token from response
}

// Example usage
const token = await getAuthToken('your-profile-name', 'your-profile-password');
console.log('Authentication token:', token);

// Store token for later use
localStorage.setItem('sthan_auth_token', token); // Browser
// or
process.env.STHAN_TOKEN = token; // Node.js

Get authentication token using requests library:

import requests
import os

def get_auth_token(profile_name, profile_password):
    """
    Obtain authentication token from Sthan.io API

    Args:
        profile_name (str): Your profile name
        profile_password (str): Your profile password

    Returns:
        str: Authentication token
    """
    url = 'https://api.sthan.io/auth/token'
    headers = {
        'profileName': profile_name,
        'profilePassword': profile_password
    }

    response = requests.get(url, headers=headers)
    response.raise_for_status()

    data = response.json()
    return data['Result']['access_token']

# Example usage
profile_name = os.getenv('STHAN_PROFILE_NAME', 'your-profile-name')
profile_password = os.getenv('STHAN_PROFILE_PASSWORD', 'your-profile-password')

token = get_auth_token(profile_name, profile_password)
print(f'Authentication token: {token}')

# Store token as environment variable for later use
os.environ['STHAN_AUTH_TOKEN'] = token

Get authentication token using Spring RestTemplate:

import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;

public class SthanAuthService {
    private final RestTemplate restTemplate;
    private static final String AUTH_URL = "https://api.sthan.io/auth/token";

    public SthanAuthService() {
        this.restTemplate = new RestTemplate();
    }

    public String getAuthToken(String profileName, String profilePassword) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("profileName", profileName);
        headers.set("profilePassword", profilePassword);

        HttpEntity<Void> request = new HttpEntity<>(headers);

        ResponseEntity<JsonNode> response =
            restTemplate.exchange(AUTH_URL, HttpMethod.GET, request, JsonNode.class);

        if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
            JsonNode resultNode = response.getBody().get("Result");
            return resultNode.get("access_token").asText();
        }

        throw new RuntimeException("Authentication failed");
    }
}

// Example usage
SthanAuthService authService = new SthanAuthService();
String token = authService.getAuthToken("your-profile-name", "your-profile-password");
System.out.println("Authentication token: " + token);

// Store token in configuration or cache for reuse

Get authentication token using Go's net/http:

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

type AuthResponse struct {
    ID       string `json:"Id"`
    Result   AuthResult `json:"Result"`
    StatusCode int  `json:"StatusCode"`
    IsError  bool   `json:"IsError"`
}

type AuthResult struct {
    AccessToken string `json:"access_token"`
    Expiration  string `json:"expiration"`
}

func getAuthToken(profileName, profilePassword string) (string, error) {
    url := "https://api.sthan.io/auth/token"

    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return "", err
    }

    req.Header.Set("profileName", profileName)
    req.Header.Set("profilePassword", profilePassword)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }

    var authResp AuthResponse
    err = json.Unmarshal(body, &authResp)
    if err != nil {
        return "", err
    }

    return authResp.Result.AccessToken, nil
}

func main() {
    profileName := os.Getenv("STHAN_PROFILE_NAME")
    profilePassword := os.Getenv("STHAN_PROFILE_PASSWORD")

    token, err := getAuthToken(profileName, profilePassword)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }

    fmt.Printf("Authentication token: %s\n", token)

    // Store token for later use
    os.Setenv("STHAN_AUTH_TOKEN", token)
}

Get authentication token using HttpClient:

using System;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

public class SthanAuthService
{
    private readonly HttpClient _httpClient;
    private const string AuthUrl = "https://api.sthan.io/auth/token";

    public SthanAuthService()
    {
        _httpClient = new HttpClient();
    }

    public async Task<string> GetAuthTokenAsync(string profileName, string profilePassword)
    {
        var request = new HttpRequestMessage(HttpMethod.Get, AuthUrl);
        request.Headers.Add("profileName", profileName);
        request.Headers.Add("profilePassword", profilePassword);

        var response = await _httpClient.SendAsync(request);
        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync();
        var apiResponse = JsonSerializer.Deserialize<AuthApiResponse>(content);

        return apiResponse.Result.AccessToken;
    }
}

public class AuthApiResponse
{
    public string Id { get; set; }
    public AuthResult Result { get; set; }
    public int StatusCode { get; set; }
    public bool IsError { get; set; }
    public string[] Errors { get; set; }
}

public class AuthResult
{
    [JsonPropertyName("access_token")]
    public string AccessToken { get; set; }

    [JsonPropertyName("expiration")]
    public DateTime Expiration { get; set; }
}

// Example usage
var authService = new SthanAuthService();
var token = await authService.GetAuthTokenAsync("your-profile-name", "your-profile-password");
Console.WriteLine($"Authentication token: {token}");

// Store token for later use (e.g., in configuration or cache)

Get authentication token using Laravel HTTP client:

<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;

class SthanAuthService
{
    private const AUTH_URL = 'https://api.sthan.io/auth/token';

    public function getAuthToken(string $profileName, string $profilePassword): string
    {
        $response = Http::withHeaders([
            'profileName' => $profileName,
            'profilePassword' => $profilePassword
        ])->get(self::AUTH_URL);

        if ($response->successful()) {
            $token = $response->json()['Result']['access_token'];

            // Cache token for 15 minutes (token expiration time)
            Cache::put('sthan_auth_token', $token, now()->addMinutes(15));

            return $token;
        }

        throw new \Exception('Authentication failed: ' . $response->status());
    }

    public function getCachedToken(): ?string
    {
        return Cache::get('sthan_auth_token');
    }
}

// Example usage
$authService = new SthanAuthService();
$token = $authService->getAuthToken(
    config('services.sthan.profile_name'),
    config('services.sthan.profile_password')
);

echo "Authentication token: {$token}\n";

Get authentication token using Ruby's Net::HTTP:

require 'net/http'
require 'json'
require 'uri'

class SthanAuthService
  AUTH_URL = 'https://api.sthan.io/auth/token'.freeze

  def initialize(profile_name, profile_password)
    @profile_name = profile_name
    @profile_password = profile_password
  end

  def get_auth_token
    uri = URI(AUTH_URL)
    request = Net::HTTP::Get.new(uri)
    request['profileName'] = @profile_name
    request['profilePassword'] = @profile_password

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    raise "Authentication failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess)

    data = JSON.parse(response.body)
    data['Result']['access_token']
  end
end

# Example usage
profile_name = ENV['STHAN_PROFILE_NAME'] || 'your-profile-name'
profile_password = ENV['STHAN_PROFILE_PASSWORD'] || 'your-profile-password'

auth_service = SthanAuthService.new(profile_name, profile_password)
token = auth_service.get_auth_token

puts "Authentication token: #{token}"

# Store token for later use
ENV['STHAN_AUTH_TOKEN'] = token

Get authentication token using reqwest:

use reqwest;
use serde::{Deserialize, Serialize};
use std::env;

#[derive(Debug, Deserialize)]
struct AuthResponse {
    #[serde(rename = "Id")]
    id: String,
    #[serde(rename = "Result")]
    result: AuthResult,
    #[serde(rename = "StatusCode")]
    status_code: i32,
}

#[derive(Debug, Deserialize)]
struct AuthResult {
    access_token: String,
    expiration: String,
}

async fn get_auth_token(
    profile_name: &str,
    profile_password: &str,
) -> Result> {
    let client = reqwest::Client::new();
    let response = client
        .get("https://api.sthan.io/auth/token")
        .header("profileName", profile_name)
        .header("profilePassword", profile_password)
        .send()
        .await?;

    let auth_response: AuthResponse = response.json().await?;
    Ok(auth_response.result.access_token)
}

#[tokio::main]
async fn main() -> Result<(), Box> {
    let profile_name = env::var("STHAN_PROFILE_NAME")
        .unwrap_or_else(|_| "your-profile-name".to_string());
    let profile_password = env::var("STHAN_PROFILE_PASSWORD")
        .unwrap_or_else(|_| "your-profile-password".to_string());

    let token = get_auth_token(&profile_name, &profile_password).await?;
    println!("Authentication token: {}", token);

    // Store token for later use
    env::set_var("STHAN_AUTH_TOKEN", &token);

    Ok(())
}

Get authentication token using OkHttp:

import okhttp3.OkHttpClient
import okhttp3.Request
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName

data class AuthResponse(
    @SerializedName("Id") val id: String,
    @SerializedName("Result") val result: AuthResult,
    @SerializedName("StatusCode") val statusCode: Int
)

data class AuthResult(
    @SerializedName("access_token") val accessToken: String,
    @SerializedName("expiration") val expiration: String
)

class SthanAuthService {
    private val client = OkHttpClient()
    private val gson = Gson()

    fun getAuthToken(profileName: String, profilePassword: String): String {
        val request = Request.Builder()
            .url("https://api.sthan.io/auth/token")
            .header("profileName", profileName)
            .header("profilePassword", profilePassword)
            .get()
            .build()

        client.newCall(request).execute().use { response ->
            if (!response.isSuccessful) {
                throw Exception("Authentication failed: ${response.code}")
            }

            val body = response.body?.string()
                ?: throw Exception("Empty response body")

            val authResponse = gson.fromJson(body, AuthResponse::class.java)
            return authResponse.result.accessToken
        }
    }
}

// Example usage
fun main() {
    val profileName = System.getenv("STHAN_PROFILE_NAME") ?: "your-profile-name"
    val profilePassword = System.getenv("STHAN_PROFILE_PASSWORD") ?: "your-profile-password"

    val authService = SthanAuthService()
    val token = authService.getAuthToken(profileName, profilePassword)

    println("Authentication token: $token")

    // Store token for later use
    System.setProperty("STHAN_AUTH_TOKEN", token)
}

Get authentication token using HTTPoison:

defmodule SthanAuthService do
  @auth_url "https://api.sthan.io/auth/token"

  def get_auth_token(profile_name, profile_password) do
    headers = [
      {"profileName", profile_name},
      {"profilePassword", profile_password}
    ]

    case HTTPoison.get(@auth_url, headers) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        body
        |> Jason.decode!()
        |> get_in(["Result", "access_token"])

      {:ok, %HTTPoison.Response{status_code: status_code}} ->
        {:error, "Authentication failed with status: #{status_code}"}

      {:error, %HTTPoison.Error{reason: reason}} ->
        {:error, reason}
    end
  end
end

# Example usage
profile_name = System.get_env("STHAN_PROFILE_NAME") || "your-profile-name"
profile_password = System.get_env("STHAN_PROFILE_PASSWORD") || "your-profile-password"

case SthanAuthService.get_auth_token(profile_name, profile_password) do
  {:error, reason} ->
    IO.puts("Error: #{reason}")

  token ->
    IO.puts("Authentication token: #{token}")

    # Store token for later use
    System.put_env("STHAN_AUTH_TOKEN", token)
end

Authentication Response Example

When you successfully authenticate, you'll receive a response like this:

{
  "Id": "4ab926ac-62dd-407b-8be9-5e5ae20c66ab",
  "Result": {
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54...",
    "expiration": "2025-10-27T11:59:19.6097906-07:00"
  },
  "ClientSessionId": null,
  "StatusCode": 200,
  "IsError": false,
  "Errors": []
}

Key fields to note:

  • Result.access_token: The JWT bearer token you'll use for API requests
  • Result.expiration: Token expiration timestamp (15 minutes from issue time)
  • StatusCode: HTTP status code (200 = success)
  • IsError: Boolean flag indicating if the request failed
  • Errors: Array of error messages (empty on success)
Token Management: Authentication tokens expire after 15 minutes. Implement token caching with automatic refresh logic in production applications to avoid unnecessary authentication calls while ensuring you always use valid tokens.

Step 2: Verify Addresses

Once you have your authentication token, you can start verifying addresses. Here's how to make verification requests in different programming languages.

Perfect for frontend address validation or Node.js backends:

// Using the token from Step 1
async function verifyAddress(token, addressString) {
  // URL encode the address
  const encodedAddress = encodeURIComponent(addressString);
  const url = `https://api.sthan.io/AddressVerification/Usa/Single/${encodedAddress}`;

  const response = await fetch(url, {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });

  const data = await response.json();
  return data;
}

// Example usage - use token from Step 1
const token = await getAuthToken('your-profile-name', 'your-profile-password');

// Pass the address as a single string
const result = await verifyAddress(token, '6000 j street sacrmento ca');

console.log(result);
// Output:
// {
//   "Id": "67890abc-def1-2345-6789-0abcdef12345",
//   "Result": {
//     "inputAddress": "6000 j street sacrmento ca",
//     "fullAddress": "6000 J St, Sacramento, CA 95819-2605",
//     "addressLine1": "6000 J St",
//     "addressLine2": "",
//     "city": "Sacramento",
//     "stateCode": "CA",
//     "county": "Sacramento",
//     "zipCode": "95819",
//     "zip4": "2605",
//     "algorithmUsed": "cc1",
//     "isError": false,
//     "errorMessages": []
//   },
//   "StatusCode": 200,
//   "IsError": false,
//   "Errors": []
// }

Ideal for data processing, Django, Flask, or FastAPI applications:

import requests
import json
from urllib.parse import quote

def verify_address(token, address_string):
    """
    Verify an address using Sthan.io API

    Args:
        token (str): Authentication token from Step 1
        address_string (str): Full address as a single string

    Returns:
        dict: Verification result
    """
    # URL encode the address
    encoded_address = quote(address_string)
    url = f'https://api.sthan.io/AddressVerification/Usa/Single/{encoded_address}'

    headers = {
        'Authorization': f'Bearer {token}'
    }

    response = requests.get(url, headers=headers)
    response.raise_for_status()

    return response.json()

# Get token from Step 1
token = get_auth_token('your-profile-name', 'your-profile-password')

# Example usage - pass address as a single string
result = verify_address(token, '1199 west shaw avenue fresno california')
print(json.dumps(result, indent=2))

# The API returns:
# {
#   "Result": {
#     "fullAddress": "1199 W Shaw Ave, Fresno, CA 93711",
#     "addressLine1": "1199 W Shaw Ave",
#     "city": "Fresno",
#     "stateCode": "CA",
#     "zipCode": "93711",
#     ...
#   }
# }

# Batch processing example
addresses = [
    '6000 j street sacrmento ca',
    '3808 avenue faradiya davis ca',
    '543 sanders road cross'
]

verified_addresses = [verify_address(token, addr) for addr in addresses]

For enterprise Java applications:

import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriUtils;
import java.nio.charset.StandardCharsets;

public class AddressVerificationService {
    private final RestTemplate restTemplate;
    private final String authToken;
    private static final String API_BASE_URL = "https://api.sthan.io/AddressVerification/Usa/Single/";

    public AddressVerificationService(String authToken) {
        this.restTemplate = new RestTemplate();
        this.authToken = authToken;
    }

    public AddressVerificationResult verifyAddress(String addressString) {
        // URL encode the address
        String encodedAddress = UriUtils.encode(addressString, StandardCharsets.UTF_8);
        String url = API_BASE_URL + encodedAddress;

        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(authToken);

        HttpEntity<?> request = new HttpEntity<>(headers);

        ResponseEntity<AddressVerificationResult> response =
            restTemplate.exchange(url, HttpMethod.GET, request, AddressVerificationResult.class);

        return response.getBody();
    }
}

// Get auth token from Step 1
SthanAuthService authService = new SthanAuthService();
String token = authService.getAuthToken("your-profile-name", "your-profile-password");

// Usage example with Spring controller
@RestController
@RequestMapping("/api/address")
public class AddressController {

    private final AddressVerificationService verificationService;

    public AddressController() {
        // Get token from auth service and initialize verification service
        SthanAuthService authService = new SthanAuthService();
        String token = authService.getAuthToken(
            System.getenv("STHAN_PROFILE_NAME"),
            System.getenv("STHAN_PROFILE_PASSWORD")
        );
        this.verificationService = new AddressVerificationService(token);
    }

    @GetMapping("/verify")
    public ResponseEntity<AddressVerificationResult> verify(@RequestParam String address) {
        AddressVerificationResult result = verificationService.verifyAddress(address);
        return ResponseEntity.ok(result);
    }
}

For Go applications and microservices:

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
)

type VerificationResponse struct {
    ID       string            `json:"Id"`
    Result   VerificationResult `json:"Result"`
    StatusCode int             `json:"StatusCode"`
    IsError  bool              `json:"IsError"`
}

type VerificationResult struct {
    InputAddress  string `json:"inputAddress"`
    FullAddress   string `json:"fullAddress"`
    AddressLine1  string `json:"addressLine1"`
    City          string `json:"city"`
    StateCode     string `json:"stateCode"`
    County        string `json:"county"`
    ZipCode       string `json:"zipCode"`
    Zip4          string `json:"zip4"`
    AlgorithmUsed string `json:"algorithmUsed"`
    IsError       bool   `json:"isError"`
}

func verifyAddress(token, addressString string) (*VerificationResponse, error) {
    // URL encode the address
    encodedAddress := url.QueryEscape(addressString)
    apiURL := fmt.Sprintf("https://api.sthan.io/AddressVerification/Usa/Single/%s", encodedAddress)

    req, err := http.NewRequest("GET", apiURL, nil)
    if err != nil {
        return nil, err
    }

    req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    var verificationResp VerificationResponse
    err = json.Unmarshal(body, &verificationResp)
    if err != nil {
        return nil, err
    }

    return &verificationResp, nil
}

func main() {
    // Get token from Step 1
    token, err := getAuthToken("your-profile-name", "your-profile-password")
    if err != nil {
        fmt.Printf("Error getting token: %v\n", err)
        return
    }

    // Verify an address
    result, err := verifyAddress(token, "6000 j street sacrmento ca")
    if err != nil {
        fmt.Printf("Error verifying address: %v\n", err)
        return
    }

    fmt.Printf("Input: %s\n", result.Result.InputAddress)
    fmt.Printf("Verified: %s\n", result.Result.FullAddress)
    fmt.Printf("City: %s, State: %s, ZIP: %s-%s\n",
        result.Result.City,
        result.Result.StateCode,
        result.Result.ZipCode,
        result.Result.Zip4)
}

For ASP.NET, Blazor, or .NET Core applications:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;

public class AddressVerificationService
{
    private readonly HttpClient _httpClient;
    private const string ApiBaseUrl = "https://api.sthan.io/AddressVerification/Usa/Single/";

    public AddressVerificationService(string authToken)
    {
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", authToken);
    }

    public async Task<AddressVerificationResult> VerifyAddressAsync(string addressString)
    {
        // URL encode the address
        var encodedAddress = HttpUtility.UrlEncode(addressString);
        var url = $"{ApiBaseUrl}{encodedAddress}";

        var response = await _httpClient.GetAsync(url);
        response.EnsureSuccessStatusCode();

        var responseJson = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<AddressVerificationResult>(responseJson);
    }
}

// Response model classes
public class AddressVerificationResult
{
    public string Id { get; set; }
    public AddressResult Result { get; set; }
    public int StatusCode { get; set; }
    public bool IsError { get; set; }
}

public class AddressResult
{
    public string InputAddress { get; set; }
    public string FullAddress { get; set; }
    public string AddressLine1 { get; set; }
    public string City { get; set; }
    public string StateCode { get; set; }
    public string ZipCode { get; set; }
    public string Zip4 { get; set; }
}

// Get auth token from Step 1
var authService = new SthanAuthService();
var token = await authService.GetAuthTokenAsync("your-profile-name", "your-profile-password");

// Use token for address verification - pass address as single string
var service = new AddressVerificationService(token);
var result = await service.VerifyAddressAsync("3808 avenue faradiya davis ca");

Console.WriteLine($"Input: {result.Result.InputAddress}");
Console.WriteLine($"Verified: {result.Result.FullAddress}");

For WordPress, Laravel, or PHP applications:

<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;

class AddressVerificationService
{
    private $authToken;
    private $apiBaseUrl = 'https://api.sthan.io/AddressVerification/Usa/Single/';

    public function __construct(string $authToken)
    {
        $this->authToken = $authToken;
    }

    public function verifyAddress(string $addressString)
    {
        // URL encode the address
        $encodedAddress = urlencode($addressString);
        $url = $this->apiBaseUrl . $encodedAddress;

        $response = Http::withHeaders([
            'Authorization' => 'Bearer ' . $this->authToken
        ])->get($url);

        if ($response->successful()) {
            return $response->json();
        }

        throw new \Exception('Address verification failed: ' . $response->body());
    }
}

// Get auth token from Step 1
$authService = new SthanAuthService();
$token = $authService->getAuthToken(
    config('services.sthan.profile_name'),
    config('services.sthan.profile_password')
);

// Usage in controller
use App\Services\{AddressVerificationService, SthanAuthService};

class CheckoutController extends Controller
{
    public function validateAddress(Request $request)
    {
        // Get auth token
        $authService = new SthanAuthService();
        $token = $authService->getCachedToken() ?? $authService->getAuthToken(
            config('services.sthan.profile_name'),
            config('services.sthan.profile_password')
        );

        $service = new AddressVerificationService($token);

        // Combine address components into a single string
        $addressString = sprintf(
            '%s %s %s %s',
            $request->input('street'),
            $request->input('city'),
            $request->input('state'),
            $request->input('zip')
        );

        $result = $service->verifyAddress($addressString);

        return response()->json($result);
    }
}

For Ruby on Rails or Sinatra applications:

require 'net/http'
require 'json'
require 'uri'

class AddressVerificationService
  API_BASE_URL = 'https://api.sthan.io/AddressVerification/Usa/Single/'.freeze

  def initialize(auth_token)
    @auth_token = auth_token
  end

  def verify_address(address_string)
    # URL encode the address
    encoded_address = URI.encode_www_form_component(address_string)
    uri = URI("#{API_BASE_URL}#{encoded_address}")

    request = Net::HTTP::Get.new(uri)
    request['Authorization'] = "Bearer #{@auth_token}"

    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
      http.request(request)
    end

    raise "Address verification failed: #{response.code}" unless response.is_a?(Net::HTTPSuccess)

    JSON.parse(response.body)
  end
end

# Get auth token from Step 1
auth_service = SthanAuthService.new(
  ENV['STHAN_PROFILE_NAME'] || 'your-profile-name',
  ENV['STHAN_PROFILE_PASSWORD'] || 'your-profile-password'
)
token = auth_service.get_auth_token

# Verify an address
verification_service = AddressVerificationService.new(token)
result = verification_service.verify_address('6000 j street sacrmento ca')

puts "Input: #{result['Result']['inputAddress']}"
puts "Verified: #{result['Result']['fullAddress']}"
puts "City: #{result['Result']['city']}, State: #{result['Result']['stateCode']}"
puts "ZIP: #{result['Result']['zipCode']}-#{result['Result']['zip4']}"

For high-performance Rust applications:

use reqwest;
use serde::{Deserialize, Serialize};
use urlencoding::encode;

#[derive(Debug, Deserialize)]
struct VerificationResponse {
    #[serde(rename = "Id")]
    id: String,
    #[serde(rename = "Result")]
    result: VerificationResult,
    #[serde(rename = "StatusCode")]
    status_code: i32,
}

#[derive(Debug, Deserialize)]
struct VerificationResult {
    #[serde(rename = "inputAddress")]
    input_address: String,
    #[serde(rename = "fullAddress")]
    full_address: String,
    #[serde(rename = "addressLine1")]
    address_line1: String,
    city: String,
    #[serde(rename = "stateCode")]
    state_code: String,
    #[serde(rename = "zipCode")]
    zip_code: String,
    zip4: String,
}

async fn verify_address(
    token: &str,
    address_string: &str,
) -> Result> {
    let encoded_address = encode(address_string);
    let url = format!("https://api.sthan.io/AddressVerification/Usa/Single/{}", encoded_address);

    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await?;

    let verification_response: VerificationResponse = response.json().await?;
    Ok(verification_response)
}

#[tokio::main]
async fn main() -> Result<(), Box> {
    // Get token from Step 1
    let token = get_auth_token("your-profile-name", "your-profile-password").await?;

    // Verify an address
    let result = verify_address(&token, "6000 j street sacrmento ca").await?;

    println!("Input: {}", result.result.input_address);
    println!("Verified: {}", result.result.full_address);
    println!("City: {}, State: {}", result.result.city, result.result.state_code);
    println!("ZIP: {}-{}", result.result.zip_code, result.result.zip4);

    Ok(())
}

For Android or Kotlin applications:

import okhttp3.OkHttpClient
import okhttp3.Request
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import java.net.URLEncoder
import java.nio.charset.StandardCharsets

data class VerificationResponse(
    @SerializedName("Id") val id: String,
    @SerializedName("Result") val result: VerificationResult,
    @SerializedName("StatusCode") val statusCode: Int
)

data class VerificationResult(
    @SerializedName("inputAddress") val inputAddress: String,
    @SerializedName("fullAddress") val fullAddress: String,
    @SerializedName("addressLine1") val addressLine1: String,
    @SerializedName("city") val city: String,
    @SerializedName("stateCode") val stateCode: String,
    @SerializedName("zipCode") val zipCode: String,
    @SerializedName("zip4") val zip4: String,
    @SerializedName("algorithmUsed") val algorithmUsed: String
)

class AddressVerificationService(private val authToken: String) {
    private val client = OkHttpClient()
    private val gson = Gson()
    private val apiBaseUrl = "https://api.sthan.io/AddressVerification/Usa/Single/"

    fun verifyAddress(addressString: String): VerificationResponse {
        // URL encode the address
        val encodedAddress = URLEncoder.encode(addressString, StandardCharsets.UTF_8.toString())
        val url = "$apiBaseUrl$encodedAddress"

        val request = Request.Builder()
            .url(url)
            .header("Authorization", "Bearer $authToken")
            .get()
            .build()

        client.newCall(request).execute().use { response ->
            if (!response.isSuccessful) {
                throw Exception("Verification failed: ${response.code}")
            }

            val body = response.body?.string()
                ?: throw Exception("Empty response body")

            return gson.fromJson(body, VerificationResponse::class.java)
        }
    }
}

// Example usage
fun main() {
    // Get token from Step 1
    val authService = SthanAuthService()
    val token = authService.getAuthToken(
        System.getenv("STHAN_PROFILE_NAME") ?: "your-profile-name",
        System.getenv("STHAN_PROFILE_PASSWORD") ?: "your-profile-password"
    )

    // Verify an address
    val verificationService = AddressVerificationService(token)
    val result = verificationService.verifyAddress("6000 j street sacrmento ca")

    println("Input: ${result.result.inputAddress}")
    println("Verified: ${result.result.fullAddress}")
    println("City: ${result.result.city}, State: ${result.result.stateCode}")
    println("ZIP: ${result.result.zipCode}-${result.result.zip4}")
}

For Phoenix or Elixir applications:

defmodule AddressVerificationService do
  @api_base_url "https://api.sthan.io/AddressVerification/Usa/Single/"

  def verify_address(auth_token, address_string) do
    # URL encode the address
    encoded_address = URI.encode(address_string)
    url = @api_base_url <> encoded_address

    headers = [
      {"Authorization", "Bearer #{auth_token}"}
    ]

    case HTTPoison.get(url, headers) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        Jason.decode!(body)

      {:ok, %HTTPoison.Response{status_code: status_code}} ->
        {:error, "Verification failed with status: #{status_code}"}

      {:error, %HTTPoison.Error{reason: reason}} ->
        {:error, reason}
    end
  end
end

# Example usage
# Get auth token from Step 1
profile_name = System.get_env("STHAN_PROFILE_NAME") || "your-profile-name"
profile_password = System.get_env("STHAN_PROFILE_PASSWORD") || "your-profile-password"

token = case SthanAuthService.get_auth_token(profile_name, profile_password) do
  {:error, reason} ->
    IO.puts("Auth error: #{reason}")
    System.halt(1)

  token ->
    token
end

# Verify an address
case AddressVerificationService.verify_address(token, "6000 j street sacrmento ca") do
  {:error, reason} ->
    IO.puts("Error: #{reason}")

  result ->
    verification_result = result["Result"]
    IO.puts("Input: #{verification_result["inputAddress"]}")
    IO.puts("Verified: #{verification_result["fullAddress"]}")
    IO.puts("City: #{verification_result["city"]}, State: #{verification_result["stateCode"]}")
    IO.puts("ZIP: #{verification_result["zipCode"]}-#{verification_result["zip4"]}")
end
Security Note: Never expose your API key in frontend code. Always make API calls from your backend server and use environment variables to store your API key.

Understanding the API Response

The API returns a structured JSON response containing the verification results and standardized address components:

{
  "Id": "67890abc-def1-2345-6789-0abcdef12345",
  "Result": {
    "inputAddress": "6000 j street sacrmento ca",
    "fullAddress": "6000 J St, Sacramento, CA 95819-2605",
    "addressLine1": "6000 J St",
    "addressLine2": "",
    "city": "Sacramento",
    "stateCode": "CA",
    "county": "Sacramento",
    "zipCode": "95819",
    "zip4": "2605",
    "algorithmUsed": "cc1",
    "isError": false,
    "errorMessages": []
  },
  "StatusCode": 200,
  "IsError": false,
  "Errors": []
}

Key Response Fields

  • inputAddress: The original address string you submitted
  • fullAddress: The complete, USPS-standardized address
  • addressLine1: Street number and street name
  • city: City name (standardized)
  • stateCode: Two-letter state abbreviation
  • county: County name
  • zipCode: 5-digit ZIP code
  • zip4: ZIP+4 extension for precise delivery
  • algorithmUsed: Which verification algorithm was used (e.g., "cc1", "TryGetExactMatch", "fuzzy-auto-poc")
  • isError: Whether the verification encountered an error
Algorithm Types: The API uses multiple verification algorithms including exact matching, fuzzy matching with Levenshtein distance, and custom algorithms to handle various address formats and errors.

6. Best Practices for Address Verification

✅ Verify During Data Entry, Not After

Validate addresses in real-time as users type or immediately after form submission. This provides instant feedback and prevents invalid data from entering your system.

✅ Use Address Autocomplete First

Combine address autocomplete with verification. Let users select from suggested addresses as they type, then verify the final selection. This reduces errors and speeds up data entry. Check out our complete guide to implementing address autocomplete in React, Vue, and Angular.

✅ Handle Edge Cases Gracefully

Not every address will validate perfectly. Implement logic to handle:

  • New addresses not yet in databases
  • Rural routes and PO boxes
  • Military addresses (APO/FPO)
  • International addresses with varying formats

✅ Cache Results Appropriately

Cache verified addresses to reduce API calls and improve performance. But remember: address data changes over time, so set reasonable TTLs (30-90 days).

✅ Provide Clear User Feedback

When an address can't be verified, explain why and suggest corrections. Don't just show a generic error message.

// Good: Helpful feedback
if (!result.valid) {
  if (result.suggestions && result.suggestions.length > 0) {
    showMessage('We found a similar address. Did you mean: ' +
                result.suggestions[0].formatted);
  } else {
    showMessage('We couldn\'t verify this address. Please double-check ' +
                'the street name and number.');
  }
}

✅ Monitor API Usage and Errors

Track your API usage, error rates, and response times. Set up alerts for unusual patterns that might indicate issues.

7. Common Pitfalls to Avoid

❌ Blocking Form Submission on Verification Failure

Don't prevent users from submitting a form if verification fails. Some addresses legitimately won't verify (new construction, etc.). Instead, show a warning and let them confirm their entry.

❌ Over-Relying on Client-Side Validation

Always verify addresses server-side. Client-side validation can be bypassed, and you don't want to expose your API key in frontend code.

❌ Ignoring International Address Formats

If you serve international customers, ensure your validation supports their address formats. A US-only validator will fail for international addresses.

❌ Not Testing with Real-World Data

Test with various address formats: apartments, rural routes, PO boxes, military addresses, and edge cases. Don't just test with clean, perfect addresses.

❌ Forgetting About Performance

API calls add latency. Implement timeouts, handle failures gracefully, and consider async verification for non-critical workflows to avoid blocking users.

Key Takeaways

  • Address verification reduces shipping costs, improves conversion rates, and enhances data quality
  • Choose an API provider based on coverage, accuracy, performance, and pricing that fits your needs
  • Implement verification server-side with proper error handling and user feedback
  • Combine autocomplete with verification for the best user experience
  • Test thoroughly with real-world address scenarios including edge cases
  • Monitor usage and continuously optimize your implementation

8. Conclusion

Address verification is no longer optional—it's a competitive necessity. Whether you're reducing shipping costs, improving conversion rates, or enhancing data quality, a solid address verification strategy delivers measurable ROI.

The implementation examples in this guide give you a head start, but remember: the best verification system is one that fits seamlessly into your user experience while maintaining data accuracy.

Ready to get started? Sign up for Sthan.io and get 100 free address verifications per month—no credit card required. Check out our flexible pricing plans for higher volumes.

Frequently Asked Questions

An address verification API is a service that validates, standardizes, and enriches address data in real-time. It checks whether a given address exists and is deliverable by comparing it against authoritative databases maintained by postal services like USPS. The process involves parsing the address into components, validating against databases, and standardizing the format according to postal service standards.

Address verification API pricing varies by provider. Sthan.io offers 100 free address verifications per month with no credit card required, making it perfect for development and testing. Paid plans are available for higher volumes. The cost per lookup typically decreases with volume, and most providers offer tiered pricing based on monthly usage.

Address verification APIs are language-agnostic and work with any programming language that can make HTTP requests. Common implementations include JavaScript/Node.js, Python, Java, Go, C#/.NET, PHP, Ruby, Rust, Kotlin, and Elixir. Most providers offer RESTful APIs with JSON responses that integrate easily with any technology stack.

Modern address verification APIs typically achieve 95%+ accuracy rates, even for misspelled or incomplete addresses. The accuracy depends on the data sources used - APIs that combine multiple sources like USPS databases, Census GeoCoding, and proprietary databases with fuzzy matching algorithms provide the highest accuracy. CASS-certified providers meet USPS standards for accuracy.

CASS (Coding Accuracy Support System) certification is a USPS standard that ensures address validation software meets specific accuracy requirements. If you need to qualify for postal discounts or require the highest level of USPS-compliant address standardization, you should use a CASS-certified API. For general address validation and standardization, CASS certification is beneficial but not always required.

The ability to verify international addresses depends on the API provider. Some providers specialize in US addresses only (using USPS data), while others offer global coverage. When evaluating providers, check their data coverage for your target countries and ensure they support the specific address formats and postal standards for those regions. Sthan.io currently focuses on US address verification with plans to expand internationally.

Integrating address verification typically involves two steps: 1) Obtain an authentication token using your API credentials, and 2) Make verification requests by sending addresses to the API endpoint. Most APIs use REST architecture with JSON responses. You can implement verification during form submission (real-time validation) or batch process existing addresses. The integration usually takes less than an hour for basic implementation.

Address validation and verification are often used interchangeably, but technically: validation checks if an address follows the correct format and structure, while verification confirms the address actually exists and is deliverable by checking against authoritative postal databases. Modern APIs typically do both - they validate the format AND verify deliverability in a single operation.

Always verify addresses server-side for security and accuracy. Client-side validation can be used for basic format checking and improved user experience, but should never replace server-side verification. Server-side verification protects your API keys, prevents tampering, ensures data integrity, and provides authoritative validation. A hybrid approach using client-side autocomplete with server-side verification offers the best user experience and data quality.

Address verification reduces shipping costs by preventing failed deliveries and returns. Each failed delivery can cost $10-$30 in re-shipping fees and customer service overhead. By ensuring addresses are valid and deliverable before shipping, businesses can reduce failed delivery rates from 3-5% to under 1%. For a business shipping 50,000 orders monthly, this can result in annual savings of $180,000 or more.

Ready to Implement Address Verification?

Start verifying addresses with our powerful API. Get 100 free lookups every month.

Related Articles

Technical Guide

How to Integrate Address Autocomplete in React, Vue, and Angular

Learn how to implement address autocomplete in React, Vue, and Angular with complete code examples.

Coming Soon

Reducing Cart Abandonment with Address Validation

Discover how address validation improves e-commerce conversion rates.

Coming Soon

USPS Address Standards Explained

Understanding address formats, abbreviations, and standardization rules.