# 🏠 Address Normalizer - Parse, Canonicalise & Dedupe Addresses (`that_red_bird/address-normalizer`) Actor

⚡ Parse freeform addresses into unit, house number, street name/type, city, region, postcode and country — for US, UK, DE and FR patterns.

- **URL**: https://apify.com/that\_red\_bird/address-normalizer.md
- **Developed by:** [mohamed alaya](https://apify.com/that_red_bird) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Address Normalizer

Parse freeform address strings into components, canonicalise them, and flag the addresses that
are the same place written differently. There are zero actors in the Apify store for address
normalization — the reason is that it is genuinely fiddly (four countries, four different
grammars, no shared postal standard), which is exactly why it's worth having.

### What it actually does

**1. Parse.** Splits a freeform string into unit/apartment, house number, street name, street
type, city, region/state, postcode and country. Handles the very different shapes of US
(`123 N Main St, Apt 4, Springfield, IL 62701`), UK (`10 Downing Street, London SW1A 2AA`), DE
(`Musterstraße 1, 10117 Berlin` — street name first, number last) and FR
(`12 Rue de la Paix, 75002 Paris` — number then street *type* then name) addresses.

**2. Canonicalise.** Street types and unit markers are expanded and abbreviated both ways (`St`
↔ `Street`, `Rd` ↔ `Road`, `Apt`/`Suite`/`Unit`, directionals like `N` ↔ `North`), casing is
fixed, and postcodes are normalised per country: UK `sw1a1aa` → `SW1A 1AA`, US ZIP+4
`123456789` → `12345-6789`.

**3. Confidence.** Every parse reports a 0–1 confidence score plus which components were
actually **found** (matched a real pattern) versus **guessed** (fallback heuristic, e.g. "last
comma segment is probably the city").

**4. Dedupe.** Reuses the same fuzzy-matching core as entity-resolver and contact-deduplicator
(`packages/monitor-core`: normalisation, Jaro-Winkler, token-set and trigram similarity,
Soundex blocking, union-find clustering) scored on address-shaped fields, so `221B Baker St` and
`221b baker street, apt B` are flagged as one duplicate group even though the unit is written as
a house-number suffix in one and an explicit `Apt B` in the other.

**5. Optional geocoding.** Off by default. When turned on, parsed addresses are looked up
against the free OpenStreetMap Nominatim geocoder for lat/lon. This sends a descriptive
`User-Agent` and enforces Nominatim's usage policy of **no more than 1 request/second** in-process
(hard floor, regardless of the configured delay), plus a per-run request cap. If geocoding is off,
fails, or a query can't be built, the parsed result is still returned along with a
`geocodeSkippedReason` explaining why — a geocoding failure never blocks the parse output.

### Honest limitations

- **Coverage is pattern-based, per country**, not a universal postal grammar. US and UK are the
  most solid (mature, well-documented formats). DE and FR are best-effort: German compound
  street names (`Bahnhofstraße`) and French `bis`/`ter` suffixes are handled, but rarer regional
  conventions are not.
- Country detection falls back to postcode shape when no country name is present. A bare 5-digit
  code that isn't clearly a ZIP+4 or state+ZIP is genuinely ambiguous between US/DE/FR — it's
  resolved by street-type keywords when possible, and documented as "guessed" in the output
  otherwise.
- Junk input (empty strings, no recognisable postcode or house number) never crashes the run — it
  returns a low-confidence, mostly-guessed parse with `warnings` explaining what wasn't found.
- Nominatim is a shared free public service. Respect the 1 req/sec limit (enforced here) and set a
  real `geocodeContact` — anonymous or abusive traffic gets blocked at their end, not just yours.
- Capped at 50,000 addresses per run.

# Actor input Schema

## `addresses` (type: `array`):

The addresses to parse, as an array of freeform strings, and/or objects that carry the address text in "addressField" plus any other columns you want passed through unchanged. Combine freely with sourceDatasetIds.

## `sourceDatasetIds` (type: `array`):

Apify dataset IDs to pull additional address rows from (e.g. the output of a scraper actor). Items are read the same way as "addresses".

## `addressField` (type: `string`):

When an item in "addresses" is an object rather than a plain string, this is the property that holds the freeform address text.

## `defaultCountry` (type: `string`):

Country to assume when one can't be detected from the text itself (no country name and an ambiguous or missing postcode). "Auto-detect" relies purely on postcode shape and address wording.

## `dedupeThreshold` (type: `integer`):

Two parsed addresses scoring at or above this are flagged as the same place. Higher = fewer false matches, lower = catches more loosely-written duplicates. Expressed 0-100; 88 means 0.88.

## `includeAllPairs` (type: `boolean`):

Emit every address pair that was compared (not just matches), with per-field similarity. Useful for tuning dedupeThreshold; verbose on large inputs.

## `enableGeocoding` (type: `boolean`):

Off by default. When on, each parsed address is looked up against the free OpenStreetMap Nominatim geocoder to attach latitude/longitude. Requires "Geocoding contact info" and is rate-limited to 1 request/second per Nominatim's usage policy, so large batches take a while.

## `geocodeContact` (type: `string`):

An app name, URL or email included in the User-Agent sent to Nominatim, as their usage policy requires so they can contact you about your traffic. Only used when "Enable geocoding" is on.

## `geocodeDelayMs` (type: `integer`):

Milliseconds to wait between geocoding requests. Always enforced at 1000ms minimum regardless of this value, per Nominatim's 1 request/second limit.

## `maxGeocodeRequests` (type: `integer`):

Hard cap on how many addresses get geocoded in one run, so a large input with geocoding on can't turn into an hours-long run. Addresses beyond the cap are still parsed and deduped, just not geocoded.

## Actor input object example

```json
{
  "addresses": [
    "221B Baker St, Apt 3B, Springfield, IL 62701",
    "221b baker street, apt B, springfield il",
    "10 Downing Street, London SW1A 2AA",
    "sw1a 2aa, 10 downing st, london",
    "Musterstraße 1, 10117 Berlin",
    "12 Rue de la Paix, 75002 Paris"
  ],
  "addressField": "address",
  "defaultCountry": "auto",
  "dedupeThreshold": 88,
  "includeAllPairs": false,
  "enableGeocoding": false,
  "geocodeDelayMs": 1000,
  "maxGeocodeRequests": 100
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `downloadCsv` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `count` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "addresses": [
        "221B Baker St, Apt 3B, Springfield, IL 62701",
        "221b baker street, apt B, springfield il",
        "10 Downing Street, London SW1A 2AA",
        "sw1a 2aa, 10 downing st, london",
        "Musterstraße 1, 10117 Berlin",
        "12 Rue de la Paix, 75002 Paris"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("that_red_bird/address-normalizer").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "addresses": [
        "221B Baker St, Apt 3B, Springfield, IL 62701",
        "221b baker street, apt B, springfield il",
        "10 Downing Street, London SW1A 2AA",
        "sw1a 2aa, 10 downing st, london",
        "Musterstraße 1, 10117 Berlin",
        "12 Rue de la Paix, 75002 Paris",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("that_red_bird/address-normalizer").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "addresses": [
    "221B Baker St, Apt 3B, Springfield, IL 62701",
    "221b baker street, apt B, springfield il",
    "10 Downing Street, London SW1A 2AA",
    "sw1a 2aa, 10 downing st, london",
    "Musterstraße 1, 10117 Berlin",
    "12 Rue de la Paix, 75002 Paris"
  ]
}' |
apify call that_red_bird/address-normalizer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,that_red_bird/address-normalizer"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/tE3w5pyLQepEpB9Me/builds/ngOLJ5v7ByxceGV6o/openapi.json
