Address Validator
Language: EN
Create an account

DOCSIntegrate the Address Validator API

Integration guide

From API key to production: authentication, contract, return codes, cache and quotas. Then four practical cases: direct call, CSV import, Shopify and WooCommerce.

  • POST/validate
  • X-API-Key
  • X-PAYMENT · 0.02 USDC
  • application/json

Getting started

In one minute

  1. Get a key. Create an account: 14 days and 100 free validations, no credit card. The key (sk_live_…) is shown only once. After that, it can be regenerated from your dashboard.
  2. Call POST /validate with the key in the X-API-Key header.
  3. Read the result: standardized address, deliverability, coordinates, and cache_key.
POST/validatecurl
curl -sS https://kder-ai.com/validate \
  -H "X-API-Key: sk_live_YOUR_KEY" \
  -H "content-type: application/json" \
  -d '{"address": {"street": "1 Rue de Paris", "city": "Saint-Denis", "state": "RE", "zip": "97400", "country": "RE"}}'

In every example, replace sk_live_YOUR_KEY with your key.

No account yet? Test an address for free: the page stores nothing and shows you exactly what the API returns.

Access

Authentication

Two ways in, one response. Only the header you send and the payment differ.

Subscription

An online store

You send
X-API-Key: sk_live_…, the key shown at signup, or regenerated in your dashboard.
You pay
The monthly quota of your plan. Nothing is charged per call.

Pay per call · x402

An autonomous agent

You send
X-PAYMENT: a signed EIP-3009 authorization, built from the body of the 402 response (accepts field).
You pay
0.02 USDC per validation charged.
The key takes priority. If both headers are sent together, the subscription wins: a call covered by an account is never charged in USDC. Each response says how it was paid, in X-Payment-Mode.

Never put the key in a git repository, a shared file or a URL. It lives in an environment variable or a secrets manager, and only travels over https. A leaked key is regenerated in one click: the old one stops working immediately.

Reference

The contract

Request

POST https://kder-ai.com/validate, JSON body, content-type: application/json header.

Request bodyJSON
{"address": {
   "street": "1 Rue de Paris",   // required
   "city": "Saint-Denis",        // required
   "state": "RE",                // required (state or region)
   "zip": "97400",               // required
   "country": "RE"               // optional, "US" by default (ISO 2 letters)
}}

Response 200

200 OKJSON
{
  "result": {
    "validated": true,
    "standardized": {"line1": "1 RUE DE PARIS", "line2": "", "city": "SAINT-DENIS",
                     "state": "RE", "zip": "97400", "country": "RE"},
    "latitude": -20.8823, "longitude": 55.4504,
    "deliverability": "residential",
    "confidence": 0.99,
    "degraded": false,
    "cache_key": "9b7a..."
  },
  "price_paid": 0.0,
  "transaction_id": null
}
validated
The provider recognized the address. false is not always a verdict: read degraded before drawing a conclusion.
deliverability
residential, commercial, unknown or invalid: the provider's code, translated into plain words.
confidence
A score between 0 and 1: a score, not a guarantee. Choose your threshold based on the return rate you accept.
degraded
true: the provider did not answer (outage, timeout, open circuit breaker). The response says nothing about the address. Never record it as an "invalid address".
price_paid
0.0 for a call covered by a subscription; the price in USDC for a paid call.
cache_key
Pseudonym of the address (SHA-256). It is used for the cache and for any deletion request. Keep it: it does not contain the address.

Response headers

These are the headers the route actually sets. No others are promised.

X-Payment-Mode
api_key or x402: how the call was paid.
X-Plan
trial, starter, pro or enterprise. Key mode only.
X-Quota-Remaining
Validations remaining this month. Absent: unlimited plan, the absence means "no cap".

Reference

Return codes, and what to do

Eight possible codes on /validate. The three key-mode rejections (401, 402, 403) are distinct on purpose: handle each in its own way.

Served

200

Read the result.

Fix on your side

401403422

Retrying without changing anything is pointless.

Limit or payment

402429

Not an outage: the body or Retry-After says what to do.

Service-side incident

500503

Try again. Never a payment request.

  1. 200Validation served

    Success

    Read result. Keep cache_key: it is your pseudonym for this address and is used to request deletion.

    Nothing to retry

  2. 401Unknown API key

    Fix on your side

    The key is incomplete, revoked (regenerated) or meant for another service. Fix the environment variable. Do not retry in a loop.

    No: fix the key

  3. 402Monthly quota used up, or no valid payment

    Limit or payment

    Read the response body: it says which of the two cases applies. A quota 402 is not an outage.

    Depends on the body

    {"reason": "quota_atteint"}Usage cap reached. This is not an outage. The counter resets on the 1st of the month (UTC).
    {"x402Version": 1}Payment is missing for this call. The price is in the accepts field.
  4. 403Account expired

    Fix on your side

    The free trial is over and the account is still on the trial plan. Move to a paid plan: nothing is charged automatically.

    No: change plan

  5. 422Malformed request

    Fix on your side

    A field is missing or empty. street, city, state and zip are required; country defaults to US.

    After fixing it

  6. 429Free-trial cap reached

    Limit

    Applies to /validate-form, the trial without an account, not the paid API. Wait for the delay given in Retry-After, or create an account.

    After Retry-After

  7. 500Payment collected, but ledger unreachable

    Service-side incident

    The service refuses to serve a validation it cannot record. Try again. If the call was paid, quote transaction_id: reconciliation is done by hand.

    Yes, keep transaction_id

  8. 503Customer accounts or database unreachable

    Service-side incident

    The key could not be verified (this is not a rejection), or the database is not responding. Try again in a few minutes: nothing was consumed.

    A 503 is never a payment request.

    Yes, in a few minutes

Billing

What is not paid twice, and what is capped

Cache (verdict)
The same normalized address does not query the provider again for 30 days. cache_key guarantees this.
To doCall without fear of paying twice for the same question. To force a new call, change the address: the API does not guess.
Cache (degraded)
A degraded result is kept for 5 minutes, not 30 days: "the provider did not answer" is not a verdict.
To doTreat degraded: true as "try again later". After this short window, the same call gets a real answer.
Quota
Monthly, on the UTC calendar month: it resets on the 1st of the month, not 30 days after your first call.
To doWatch X-Quota-Remaining. Write to us before the limit rather than after.
Price per call
0.02 USDC per validation charged (x402 mode). A call covered by a subscription reports price_paid: 0.0.
To doA paying agent reads the 402 body (accepts): the price is there, never to be guessed.
Trial without an account
10 validations per day per visitor on /validate-form.
To doFor regular use, create an account: 100 free validations, then a monthly plan.

Examples

Practical cases

Four ways to plug in the API. Each example reads the key from an environment variable.

1. Direct call (PowerShell)

POST/validatePowerShell
$cle = $env:ADDRESS_VALIDATOR_KEY
$corps = '{"address":{"street":"1 Rue de Paris","city":"Saint-Denis","state":"RE","zip":"97400","country":"RE"}}'
$r = Invoke-WebRequest -Uri https://kder-ai.com/validate -Method POST `
     -Headers @{ "X-API-Key" = $cle } -ContentType "application/json" -Body $corps
$r.StatusCode
$r.Headers["X-Payment-Mode"], $r.Headers["X-Plan"], $r.Headers["X-Quota-Remaining"]
($r.Content | ConvertFrom-Json).result

2. CSV import (Python)

verifier_colis.pyPython
"""Checks a CSV of addresses and writes the verdict next to each one.

The provider is queried only once per address: the response is cached
(30 days for a verdict), so a second run on the same file
costs nothing more.
"""
import csv
import os
import time

import requests

CLE = os.environ["ADDRESS_VALIDATOR_KEY"]  # never in the code, never in git
API = "https://kder-ai.com/validate"

with open("colis.csv", newline="", encoding="utf-8") as source, open(
    "colis-verifies.csv", "w", newline="", encoding="utf-8"
) as cible:
    lecteur = csv.DictReader(source)
    champs = list(lecteur.fieldnames) + [
        "validated", "deliverability", "degraded", "note",
    ]
    sortie = csv.DictWriter(cible, fieldnames=champs)
    sortie.writeheader()
    for ligne in lecteur:
        reponse = requests.post(
            API,
            headers={"X-API-Key": CLE},
            timeout=30,
            json={
                "address": {
                    "street": ligne["adresse"],
                    "city": ligne["ville"],
                    "state": ligne["region"],
                    "zip": ligne["code_postal"],
                    "country": ligne.get("pays") or "US",
                }
            },
        )
        if reponse.status_code in (500, 503):
            time.sleep(5)  # service-side outage: nothing was consumed
            continue
        if reponse.status_code == 429:
            time.sleep(int(reponse.headers.get("Retry-After", "60")))
            continue
        if reponse.status_code != 200:
            sortie.writerow({**ligne, "note": f"rejected {reponse.status_code}"})
            continue
        resultat = reponse.json()["result"]
        sortie.writerow(
            {
                **ligne,
                "validated": resultat["validated"],
                "deliverability": resultat["deliverability"],
                "degraded": resultat["degraded"],
                "note": "to re-check (provider unreachable)"
                if resultat["degraded"]
                else "",
            }
        )

What the example shows, and nothing else: the key comes from an environment variable, the 429 is respected (the delay is read, not guessed), and a degraded result is marked "to re-check" instead of being written as an invalid address.

No Shopify app, no WooCommerce extension. There is nothing to install in your store: the API is the contract, and the call is made from your service. That is what lets you choose your own confidence threshold.

3. Shopify

Trigger
An orders/create webhook (or your package-preparation step) calls your service, which queries /validate with the order's shipping address.
Field mapping
  • shipping_address.address1 → street (add address2 to line2 on the result side)
  • city → city
  • province_code → state
  • zip → zip
  • country_code → country
Decision
validated: false: flag the order in your back office before shipping. degraded: true: block nothing, schedule another check.
What we do not provide
No ready-to-install Shopify app: the call comes from your service.

4. WooCommerce

Trigger
The woocommerce_checkout_order_processed action (order paid), or a cron job that goes through orders being prepared (wc_get_orders( ['status' => 'processing'] )).
Field mapping
  • $order->get_shipping_address_1() → street
  • $order->get_shipping_city() → city
  • $order->get_shipping_state() → state
  • $order->get_shipping_postcode() → zip
  • $order->get_shipping_country() → country
Traceability
Store cache_key with the order: it is the only address identifier valid for a deletion request.
What we do not provide
No WooCommerce extension: the call comes from your service.

In every case: one call per address and per order, never in a loop that ignores the return codes. A quota 402 is not an outage: it is a limit, and it is read from the response.

Developers and AI agents

Pay per call, no account

An autonomous agent needs neither an account nor a key. Each validation charged costs 0.02 USDC, paid with x402 on the Base network.

  1. 1Call POST /validate with the usual body. Without payment, the response is a 402 whose body carries {"x402Version": 1} and the accepts field: the price is there.
  2. 2Sign the authorization. Build an EIP-3009 authorization from accepts, and resend the request with the X-PAYMENT header.
  3. 3Read the response. X-Payment-Mode is x402, price_paid gives the price in USDC, and transaction_id is the reference to quote in case of a 500.

For your agents, everything can be discovered without paying:

Good to know before you integrate

Limits

United States Optimized
The provider is optimized for United States addresses: standardized address, coordinates, deliverability and confidence score.
France and overseas Verified (BAN)
French and overseas addresses are checked against the Base Adresse Nationale: score, postcode and city must match. Other countries remain partially verified.
degraded: true Re-check
The provider did not answer. The response says nothing about the address: it is not a rejection. Run the check again later.
Integrations
No Shopify app, no WooCommerce extension: the call is made from your own service.
What we keep, and what we do not. For a paid or subscribed call: a fingerprint of the address (cache_key) and the outcome, never the address itself. For the free trial without an account: nothing, only an anonymous counter that enforces the cap. A deletion request is made with the cache_key.