# Nominatim Address Geocoder (`ryanclinton/nominatim-geocoder`) Actor

Batch geocode addresses to GPS coordinates (forward geocoding) or convert latitude/longitude coordinates back to street addresses (reverse geocoding) using the OpenStreetMap Nominatim API. Powered by the world's largest open geographic database with global coverage across 200+ countries.

- **URL**: https://apify.com/ryanclinton/nominatim-geocoder.md
- **Developed by:** [Ryan Clinton](https://apify.com/ryanclinton) (community)
- **Categories:** AI, Developer tools
- **Stats:** 54 total users, 6 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 location geocodeds

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/actors/running/actors-in-store.md#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

## Nominatim Address Geocoder

![Turn messy addresses into decisions — audit, validate, repair, deduplicate and enrich address data on OpenStreetMap](https://apifyforge.com/readme-assets/ryanclinton-nominatim-geocoder/hero.png)

> Raw geocoding APIs tell you *where* an address is. This actor tells you whether you should **trust it, repair it, route it, or reject it**.

Audit, validate, repair, deduplicate, and enrich address datasets at scale on OpenStreetMap. **The output is not a coordinate. It's an action.**

**Input:** 50,000 messy addresses
**Output:** 42,108 `use` • 5,124 `repair` • 1,932 `verify` • 836 `reject`

…without reading a single address manually. No API key, 200+ countries, automatic rate-limit compliance.

***

### The difference, in one picture

```
   A raw geocoder                 This actor
   ─────────────                  ──────────

      Address                       Address
         │                             │
         ▼                             ▼   quality + normalization
     Geocoder                      Geocoding
         │                             │
         ▼                             ▼   validation
    Coordinates                     Repair
       (done)                          │
                                       ▼   deduplication
                                  Risk analysis
                                       │
                                       ▼   deliverability + territory
                              ┌─────────────────┐
                              │    DECISION     │
                              │ use · repair ·  │
                              │ verify · reject │
                              └─────────────────┘
```

***

### Why address data fails

Bad address data quietly creates duplicate CRM records, failed deliveries, incorrect territory assignment, broken lead routing, wasted sales outreach, and inaccurate reporting. Raw geocoding APIs make this worse: they return coordinates for almost anything, even a vague, misspelled, or duplicate address, with no signal that something is wrong.

**Raw geocoding APIs return coordinates. This actor tells you whether you should trust them.**

***

### Most geocoders stop here

```json
{ "lat": 38.8977, "lon": -77.0365 }
```

Your workflow still doesn't know:

- ❌ Can I trust it?
- ❌ Can I deliver to it?
- ❌ Should I repair it?
- ❌ Is it a duplicate?
- ❌ Should I reject it?

### This actor ends here

```json
{
  "recommendedAction": "repair",
  "addressPersona": "repairable",
  "riskFlags": ["postcode_mismatch"],
  "repairSuggestion": "1600 Pennsylvania Avenue NW, Washington, DC 20500",
  "territory": "Washington DC",
  "explanation": ["address resolved at street-level", "source address is incomplete (no postcode)", "a clean canonical address is available", "recommended action: repair"]
}
```

Your workflow knows exactly what to do next.

***

### The most important field

```json
{ "recommendedAction": "repair" }
```

Possible values: **`use`** (trustworthy, act on it) · **`repair`** (resolved, but clean the source address first) · **`verify`** (needs a human look) · **`reject`** (unusable).

Not the latitude. Not the longitude. Not the confidence score. **Everything else in this actor — normalization, validation, risk scoring, deliverability, territory assignment — exists to produce this one decision**, so your CRM, dialler, routing engine, or ETL job can act on it automatically.

***

### This isn't one address. It's your whole dataset.

Point the actor at 50,000 CRM records (via `queries`, `structuredAddresses`, or `inputDatasetId`) and the run-ending summary classifies every one and tells you what to do — without reading a single address manually:

```json
{
  "recordType": "summary",
  "auditReport": { "valid": 42108, "needsReview": 1932, "duplicates": 1056, "countryConflicts": 318, "poorQuality": 836 },
  "datasetRecommendations": [
    { "action": "repair", "count": 5124, "reason": "postcode_added_from_match" },
    { "action": "review", "count": 1932, "reason": "ambiguous" },
    { "action": "reject", "count": 836, "reason": "too_vague" }
  ],
  "stewardReport": { "highestPriorityIssue": "postcode_added_from_match", "recordsAffected": 5124, "estimatedQualityGain": 10 }
}
```

**Without this actor:** someone manually reviews 50,000 rows to find the 7,892 that are broken.
**With this actor:** every record is classified automatically, and `datasetRecommendations` hands you a ranked fix-list. `stewardReport` (use `workflow: "steward"`) names the single highest-priority issue and the quality points you'd recover by fixing it.

***

### Why not just use Google, HERE, Mapbox, or OpenCage?

| Question | Raw geocoder | This actor |
|----------|:------------:|:----------:|
| Where is it? | ✓ | ✓ |
| Can I trust it? | ✗ | ✓ |
| Should I repair it? | ✗ | ✓ |
| Is it a duplicate? | ✗ | ✓ |
| Is it deliverable? | ✗ | ✓ |
| Which territory owns it? | ✗ | ✓ |
| What should I do next? | ✗ | ✓ |

***

### At a glance

| Capability | Raw geocoding API | This actor |
|------------|:-----------------:|:----------:|
| Coordinates + address breakdown | ✓ | ✓ |
| Validation (country / boundary / postcode) | ✗ | ✓ |
| Repair suggestions for messy addresses | ✗ | ✓ |
| Deduplication + entity resolution | ✗ | ✓ |
| Deliverability scoring (logistics) | ✗ | ✓ |
| Sales-territory assignment | ✗ | ✓ |
| Whole-dataset audit reports | ✗ | ✓ |
| A `use` / `repair` / `verify` / `reject` decision | ✗ | ✓ |

(See [Key features](#key-features) for the full list — enrichment, geofencing, caching, urbanicity, and more.)

***

### What problem are you solving?

Pick the row that matches your pain and the actor configures itself:

| If you have… | Use workflow |
|--------------|--------------|
| Dirty CRM addresses | `crm-cleanse` |
| Duplicate customer records | `deduplicate` |
| Failed deliveries | `delivery-validation` |
| Bad data imports | `audit` |
| Territory / lead assignment | `lead-routing` |
| Addresses needing enrichment | `enrich` |
| "Find everything wrong with my dataset" | `steward` |

In outcomes: prevent duplicate CRM records, reject bad delivery locations automatically, fix messy customer addresses, route leads to the correct territory, and audit an entire address dataset in a single run — for CRM teams, logistics teams, revenue operations, and data engineers.

#### Who this is *not* for

If you only need raw latitude/longitude, basic reverse geocoding, or a one-off single-address lookup, a plain geocoder is simpler and cheaper — you don't need this. Reach for this actor when you're processing a **dataset** and need validation, repair, deduplication, deliverability, territory assignment, or a quality audit — decisions, not just coordinates.

***

### Choose your workflow

Set `workflow` to match your job — it composes with `mode` (forward/reverse) and tunes the output for your use case. The full decision layer is always computed.

| Workflow | Use case | Example |
|----------|----------|---------|
| `audit` | Grade a CRM/dataset's address quality | `{ "workflow": "audit", "queries": [...] }` |
| `validate` | Confirm addresses resolve in the right country/boundary | `{ "workflow": "validate", "expectedCountry": "GB", "queries": [...] }` |
| `enrich` | Add full location intelligence (timezone, Plus Code, category) | `{ "workflow": "enrich", "queries": [...] }` |
| `deduplicate` | Collapse addresses that point to the same place | `{ "workflow": "deduplicate", "queries": [...] }` |
| `delivery` | Score logistics serviceability for each location | `{ "workflow": "delivery", "queries": [...] }` |

(Customer-named aliases also work: `crm-cleanse`, `lead-routing`, `delivery-validation`.)

***

### How the decision is made

![What this actor adds on top of raw geocoding: input quality, geocoding, normalization and repair, validation, risk and deduplication, deliverability and territory, then a decision](https://apifyforge.com/readme-assets/ryanclinton-nominatim-geocoder/intelligence-layers.png)

This actor is an **Address Intelligence Engine**: five independent signals feed one decision, and you can see every one of them.

```
                 recommendedAction  (the decision)
                          ▲
        ┌─────────┬───────┼───────┬──────────────┐
   confidence  inputQuality  validation  risk  deliverability
```

Every record runs through a deterministic pipeline — no AI, no black box. Each score, flag, and recommendation traces back to a documented rule, and every result carries an `explanation[]` array spelling out the reasoning in plain English.

```
INPUT (address or coordinate)
   │
   ▼  Input quality scored (structure, completeness)  →  inputQuality
   │
   ▼  Normalization (clean address components)         →  normalizedAddress, repairSuggestion
   │
   ▼  Geocoding (OpenStreetMap Nominatim)
   │
   ▼  Match classification (precision + match type)    →  precision, matchType, candidateCount
   │
   ▼  Confidence scoring (transparent components)      →  confidenceScore, confidenceBreakdown
   │
   ▼  Validation (country, boundary, postcode)         →  validation, geofencing, territory
   │
   ▼  Risk assessment (consolidated signals)           →  riskScore, riskFlags, reviewReasons
   │
   ▼  Deliverability + serviceability (precision rule) →  deliverySuitability, serviceability
   │
   ▼  DECISION                                         →  recommendedAction + explanation
```

**How a few of the derived signals are calculated** (the full rules are in the dataset schema field descriptions):

- **`confidenceScore`** = a base from the precision tier (rooftop 90 → country 20) plus small documented bonuses for a resolved house number (+4), postcode (+3), country-filter match (+3), and a single candidate (+3). The `confidenceBreakdown` object shows each component.
- **`deliverySuitability` / `serviceability.deliverable`** = precision-driven: `rooftop`/`street` precision is deliverable; `city`-level or coarser is not precise enough for last-mile delivery. The `reason` field states which rule applied.
- **`recommendedAction`** = `reject` if no match; `verify` if ambiguous or a boundary violation; `repair` if it resolved but the source address is messy; `use` if high-confidence and unambiguous. The `explanation[]` and `reviewReasons[]` show exactly which conditions fired.
- **`riskScore`** = a weighted sum of the risk flags present (e.g. country mismatch +30, ambiguous +20, postcode mismatch +15); the `riskFlags[]` array lists every contributor.

**The output is not a coordinate. The output is an action.**

***

### Example automation rules

Because the output is a decision, you wire it straight into your tools — no parsing, no scripting. Branch on `recommendedAction`:

| `recommendedAction` | What your automation does |
|---------------------|---------------------------|
| `use` | Write the record straight to your CRM / database / map layer |
| `repair` | Update the source record with `repairSuggestion`, then re-import |
| `verify` | Send to a human review queue (with `reviewReasons` attached) |
| `reject` | Block the import / flag for correction |

In Zapier, Make, n8n, or Dify, that's a single if/else node on one field. This actor isn't returning data — it's returning workflow decisions.

***

![Four features: returns a decision, repairs dirty addresses, audits whole datasets, 100% deterministic](https://apifyforge.com/readme-assets/ryanclinton-nominatim-geocoder/feature-callouts.png)

### Why use this actor?

- **Two-way address resolution** -- forward geocode addresses to coordinates or reverse geocode coordinates to addresses in a single actor, eliminating the need for separate tools
- **Batch processing built in** -- submit hundreds of addresses or coordinate pairs at once instead of writing custom scripts to loop through individual API calls
- **Automatic rate limit compliance** -- Nominatim enforces a strict 1 request/second policy and will block violators; this actor spaces requests at 1.1 seconds automatically so you never get banned
- **Structured, consistent output** -- every result returns the same fields with null-safe handling for failed lookups, making downstream data processing predictable and reliable
- **Cloud-native scheduling** -- run geocoding jobs on recurring schedules via Apify, trigger runs via webhooks, and pipe results directly into Google Sheets, CRMs, or databases without local infrastructure

***

### Key features

- **Forward geocoding** -- convert street addresses, landmarks, city names, or any place description into precise latitude/longitude GPS coordinates
- **Reverse geocoding** -- convert GPS coordinate pairs back into fully structured street addresses with city, state, country, and postcode
- **Batch processing** -- geocode hundreds of addresses or coordinate pairs in a single actor run with automatic queuing
- **Country code filtering** -- restrict forward geocoding results to a specific country using ISO 3166-1 alpha-2 codes (e.g., US, GB, DE, JP) for higher accuracy
- **Multi-language results** -- retrieve address components in English, German, French, Spanish, Japanese, or any language supported by OpenStreetMap
- **Full address decomposition** -- every result includes house number, road, city, state, country, and postcode as separate fields
- **OpenStreetMap metadata** -- includes OSM type, OSM ID, place type classification, importance ranking score, and geographic bounding box
- **Match-quality decision layer** -- every result is classified by `matchType`, `precision`, and `confidenceScore`, then given a `recommendedAction` (`use` / `verify` / `reject`) your workflow can branch on directly
- **Address standardization** -- a `normalizedAddress` object with clean house number, street, city, county, state, postcode, and country, plus an `administrativeHierarchy` from broad to narrow
- **Input quality scoring** -- each address gets an `inputQuality` score so you can tell garbage source data from good data before trusting the geocode
- **Ambiguity & duplicate detection** -- flags vague queries with multiple candidates (`ambiguous`, `candidateCount`) and marks repeat places in a batch (`isDuplicate`, `duplicateOf`)
- **Country validation** -- set `expectedCountry` to verify every result landed in the country you expected (`countryMatched`)
- **Spatial enrichment** -- every resolved coordinate carries its Plus Code, IANA timezone, current UTC offset, continent, and hemisphere, computed offline at no extra cost
- **Batch analytics** -- a per-run summary record with `dataQualityScore`, duplicates found, countries detected, ambiguous count, and the top failure reason
- **Dataset chaining** -- read addresses straight from another Apify dataset with `inputDatasetId` instead of re-pasting them
- **Structured address input** -- feed clean, field-separated addresses (`{ houseNumber, street, city, state, postcode, country }`) for higher-precision matching, perfect for CRM, Salesforce, HubSpot, and ERP exports
- **Location categories** -- every place is classified `government`, `commercial`, `residential`, `landmark`, `transport`, and more, from OpenStreetMap tags
- **Boundary & postcode validation** -- check results against `allowedCountries` / `allowedStates` and verify the input postcode resolved correctly
- **Distance & bearing** -- set a reference point and every result reports its distance (km and miles) and compass bearing, with optional radius geofencing
- **Cross-run cache** -- name a cache and repeated addresses are served instantly without re-calling Nominatim and without being charged again
- **Risk flags & levels** -- every record carries a `riskLevel` (none/low/medium/high) and stable `riskFlags` (ambiguous, country mismatch, boundary violation, low confidence, duplicate, poor input) so you can auto-reject or route bad data
- **Deliverability scoring** -- a logistics `deliverySuitability` score answers "can a parcel actually reach this point?", distinct from match confidence
- **Address repair suggestions** -- when input is messy but resolves, the actor suggests the clean canonical address, with concrete `inputQuality` fix recommendations
- **Entity resolution** -- a stable cross-run `entity` id means "IBM HQ", "IBM Headquarters", and "1 New Orchard Road" all collapse to the same entity for deduplication
- **Geofencing** -- pass circular or polygon geofences (delivery zones, territories) and every result reports which it falls inside
- **Dataset audit & diagnosis** -- every run produces an `auditReport` (valid / needs-review / duplicates / country-conflicts / poor-quality counts) and a graded `datasetDiagnosis` with per-dimension risk levels
- **Job workflows** -- pick `audit`, `validate`, `enrich`, `deduplicate`, or `delivery` and the actor tunes its output for your use case (composes with forward/reverse mode)
- **Repair decisions** -- messy-but-resolvable addresses route to a `repair` action with a `repairConfidence`, so you clean source data instead of just flagging it
- **Logistics serviceability** -- boolean `serviceability` (`deliverable` / `urban` / `rural`) drops straight into delivery-routing automation rules
- **Sales territory assignment** -- define geographic or administrative `salesTerritories` and every result is tagged with its `territory` for CRM routing and ownership
- **Automatic rate limiting** -- built-in 1.1-second delay between requests keeps you compliant with Nominatim usage policy without manual throttling
- **Null-safe failure handling** -- when an address cannot be resolved, the actor returns a record with the original query preserved and all location fields set to null
- **Zero configuration** -- no API keys, no account registration, no billing setup; just provide addresses and run

***

### How to use Nominatim Address Geocoder

#### Using the Apify Console

1. Navigate to the [Nominatim Address Geocoder](https://apify.com/ryanclinton/nominatim-geocoder) actor page on Apify Store.
2. Click **Try for free** to open the actor in the Apify Console.
3. Select the **Geocoding Mode** -- choose "Forward" to convert addresses to coordinates, or "Reverse" to convert coordinates to addresses.
4. For forward mode, enter your addresses in the **Addresses to Geocode** field, one per line. Optionally set a **Country Code** to restrict results.
5. For reverse mode, enter matching lists of **Latitudes** and **Longitudes**, one value per line. Both lists must have the same length.
6. Optionally change the **Language** from the default "en" to receive results in another language.
7. Click **Start** and wait for the run to complete.
8. View results in the **Dataset** tab, or export to JSON, CSV, Excel, or other formats.

#### Using the Apify API or CLI

```bash
apify call ryanclinton/nominatim-geocoder \
  --input='{"mode":"forward","queries":["1600 Pennsylvania Ave, Washington DC","Eiffel Tower, Paris","Big Ben, London"],"countryCode":"","language":"en"}'
```

***

### Input parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `mode` | Select | No | `forward` | Geocoding direction. "forward" converts addresses to coordinates. "reverse" converts coordinates to addresses. |
| `workflow` | Select | No | `standard` | Job preset (composes with `mode`): `audit`, `validate`, `enrich`, `deduplicate`, `delivery`. Tunes default output detail; the full decision layer always computes. |
| `queries` | String list | No | -- | List of addresses, place names, or landmarks to geocode. Used in forward mode only. One address per line. |
| `latitudes` | String list | No | -- | List of latitude values for reverse geocoding. Must match the length of the longitudes list. |
| `longitudes` | String list | No | -- | List of longitude values for reverse geocoding. Must match the length of the latitudes list. |
| `countryCode` | String | No | -- | Restrict forward geocoding to a specific country. Use ISO 3166-1 alpha-2 codes such as "US", "GB", "DE", "FR", "JP". |
| `language` | String | No | `en` | Preferred language for results. Use ISO 639-1 codes: "en", "de", "fr", "es", "ja", "zh", "ar", etc. |
| `maxAlternatives` | Integer | No | `0` | Forward mode only. When greater than 0, also return up to N alternative candidate matches per address in an `alternatives` array, so you can disambiguate when the top match might be wrong. |
| `minConfidence` | Integer | No | `0` | Quality gate (0-100). Matches scoring below this confidence still appear in the dataset flagged `suppressed: true`, but are not charged. |
| `outputProfile` | Select | No | `standard` | How many fields each result carries. `minimal` = coordinates plus the decision layer. `standard` = full address plus the decision layer. `full` = everything including OSM internals, bounding box, decision trail, and next-actor suggestions. |
| `structuredAddresses` | Array | No | -- | Forward mode: addresses as structured objects (`{ houseNumber, street, city, county, state, postcode, country }`) for higher-precision matching. Overrides `queries` when set. Ideal for CRM/ERP exports. |
| `expectedCountry` | String | No | -- | ISO 3166-1 alpha-2 code. When set, `validation.countryMatched` verifies each result resolved to this country. |
| `allowedCountries` | String list | No | -- | Boundary validation: ISO alpha-2 codes a result must fall within. Outside results get `validation.boundaryViolation: true`. |
| `allowedStates` | String list | No | -- | Boundary validation: state/region names a result must fall within (case-insensitive, e.g. "England", "California"). |
| `referenceLat` / `referenceLon` | Number | No | -- | A reference point. When both are set, every result reports `geospatial.distanceKm`, `distanceMiles`, and `bearing` from it. |
| `maxRadiusKm` | Number | No | -- | With a reference point set, results within this many km are flagged `geospatial.insideRadius: true`. |
| `geofences` | Array | No | -- | Circular (`{name, lat, lon, radiusKm}`) or polygon (`{name, polygon:[[lat,lon],…]}`) geofences. Each result reports which it falls inside. Ideal for delivery zones. |
| `salesTerritories` | Array | No | -- | Named territories — geographic (`{name, polygon}` / `{name, lat, lon, radiusKm}`) or administrative (`{name, countries:["GB"], states:["England"]}`). Each result is assigned the first matching `territory`. |
| `cacheName` | String | No | -- | Opt-in cross-run cache. When set, repeated addresses are served instantly from a named store (no re-fetch, no charge). Leave blank to disable. |
| `cacheMaxAgeDays` | Integer | No | `30` | How long a cached result stays valid before being re-fetched. |
| `inputDatasetId` | String | No | -- | Read forward-mode addresses from another Apify dataset (e.g. a previous actor's output) instead of, or in addition to, the `queries` field. |
| `addressField` | String | No | `address` | The field name to read the address from when `inputDatasetId` is set. |

#### Forward geocoding input example

```json
{
    "mode": "forward",
    "queries": [
        "1600 Pennsylvania Ave, Washington DC",
        "Eiffel Tower, Paris, France",
        "Shibuya Crossing, Tokyo",
        "Colosseum, Rome, Italy"
    ],
    "countryCode": "",
    "language": "en"
}
```

#### Reverse geocoding input example

```json
{
    "mode": "reverse",
    "latitudes": ["48.8584", "40.7484", "51.5014"],
    "longitudes": ["2.2945", "-73.9857", "-0.1419"],
    "language": "en"
}
```

#### Tips for input

- For forward geocoding, include as much detail as possible -- street number, street name, city, state, and country yield the best results.
- Always set the `countryCode` when you know the target country. This prevents the geocoder from returning matches in the wrong country (e.g., "Springfield" exists in 30+ US states and in other countries).
- For reverse geocoding, use at least 4 decimal places of precision for street-level accuracy.
- The latitudes and longitudes lists must be the same length -- each latitude at index N is paired with the longitude at index N.

***

### Output

![Sample output table: query, recommended action, address persona, confidence grade, precision, risk level and territory per row](https://apifyforge.com/readme-assets/ryanclinton-nominatim-geocoder/output-table.png)

Each geocoding result pairs a deterministic decision layer with a standardized address and spatial enrichment. Here is an example of a forward geocoding result (`standard` profile):

```json
{
    "recordType": "result",
    "schemaVersion": "2.6.0",
    "query": "1600 Pennsylvania Ave, Washington DC",
    "matched": true,
    "matchType": "exact",
    "precision": "rooftop",
    "confidenceScore": 100,
    "confidenceLevel": "high",
    "confidenceGrade": "A",
    "confidenceBreakdown": { "base": 90, "importance": 8, "houseNumber": 4, "postcode": 3, "countryFilter": 0, "singleCandidate": 3 },
    "recommendedAction": "use",
    "addressPersona": "trusted",
    "explanation": ["address resolved at rooftop-level (exact match)", "confidence 100/100 (high)", "recommended action: use"],
    "reviewReasons": [],
    "riskScore": 0,
    "riskLevel": "none",
    "riskFlags": [],
    "deliverySuitability": { "score": 100, "level": "high", "reason": "rooftop precision with postcode — deliverable" },
    "serviceability": { "deliverable": true, "urban": true, "rural": false, "reason": "rooftop precision with postcode — deliverable" },
    "summary": "\"1600 Pennsylvania Ave, Washington DC\" geocoded to rooftop-level (exact match, confidence 100/100) — use.",
    "candidateCount": 1,
    "ambiguous": false,
    "isDuplicate": false,
    "duplicateOf": null,
    "duplicateCluster": 0,
    "entity": { "id": "osm_way_238241022", "confidence": "high" },
    "territory": null,
    "recommendedOwner": null,
    "failureReason": null,
    "repairSuggestion": null,
    "repairConfidence": null,
    "repairReasons": [],
    "inputQuality": { "score": 70, "level": "rich", "signals": ["contains a street/house number", "comma-delimited components"], "issues": ["no postcode"], "recommendation": "Add a postcode to confirm the exact location." },
    "validation": { "countryMatched": null, "matchedCountry": "United States", "insideAllowedBoundary": null, "boundaryViolation": null, "postcodePresent": false, "postcodeMatched": null },
    "geofencing": null,
    "geospatial": null,
    "latitude": 38.8976763,
    "longitude": -77.0365298,
    "displayName": "White House, 1600, Pennsylvania Avenue Northwest, Washington, District of Columbia, 20500, United States",
    "normalizedAddress": {
        "houseNumber": "1600", "street": "Pennsylvania Avenue Northwest", "city": "Washington",
        "county": null, "state": "District of Columbia", "postcode": "20500", "country": "United States", "countryCode": "US"
    },
    "administrativeHierarchy": { "country": "United States", "state": "District of Columbia", "city": "Washington" },
    "locationCategory": "government",
    "addressCompleteness": 83,
    "enrichment": { "plusCode": "87C4VXX7+39", "hemisphere": "NW", "continent": "North America", "urbanicity": "urban", "timezone": "America/New_York", "utcOffset": "-04:00" },
    "placeType": "house",
    "importance": 0.782,
    "extractedAt": "2025-01-15T14:32:08.123Z"
}
```

#### Decision & quality fields

| Field | Type | Description |
|-------|------|-------------|
| `matched` | Boolean | `true` when the query resolved to a location |
| `matchType` | String | How it matched: `exact`, `normalized`, `partial`, `ambiguous`, `fallback`, or `none` |
| `precision` | String | Precision tier: `rooftop`, `street`, `neighborhood`, `city`, `region`, `country`, `unknown` |
| `confidenceScore` | Number | Deterministic 0-100 score from precision, importance, and match completeness |
| `confidenceLevel` | String | Stable band: `high` (>=75), `medium` (>=45), `low` (>0), `none` (0) |
| `confidenceFactors` | Array | Plain-English drivers behind the score, for auditability |
| `confidenceBreakdown` | Object | Numeric point contributions: `{ base, importance, houseNumber, postcode, countryFilter, singleCandidate }` |
| `confidenceGrade` | String | A–F letter grade on the confidence score |
| `recommendedAction` | String | The decision to branch on: `use` (high & unambiguous), `verify` (ambiguous/boundary), `repair` (resolved but the source address is messy), `reject` (no match) |
| `addressPersona` | String | One-word dashboard label: `trusted`, `repairable`, `ambiguous`, `unusable`, `duplicate` |
| `explanation` | Array | Plain-English account of why this decision was reached (what resolved, what's wrong, confidence, action) |
| `reviewReasons` | Array | Action-scoped codes for why the record needs attention — empty when the action is `use` |
| `riskScore` / `riskLevel` / `riskFlags` | Number / String / Array | Numeric 0-100 risk, the `none`/`low`/`medium`/`high` band, and the stable flag codes behind it |
| `deliverySuitability` | Object / null | Logistics deliverability `{ score, level, reason }` — can a parcel reach this point? |
| `serviceability` | Object / null | Boolean logistics projection `{ deliverable, urban, rural, reason }` for if/then automation |
| `entity` | Object | Stable cross-run `{ id, confidence }` for deduplicating addresses that resolve to the same place |
| `territory` / `recommendedOwner` | String / null | The first matching `salesTerritories` name and its owner, for CRM lead routing |
| `repairSuggestion` / `repairConfidence` / `repairReasons` | String / Number / Array | A clean canonical address, a 0-100 confidence, and what it fixed (e.g. `street_name_expanded`, `postcode_added_from_match`) |
| `summary` | String | One-line plain-English verdict an LLM or agent can quote directly |
| `candidateCount` | Number | How many candidate matches the geocoder returned |
| `ambiguous` / `needsDisambiguation` | Boolean | `true` when multiple plausible candidates exist at a coarse precision |
| `isDuplicate` / `duplicateOf` / `duplicateCluster` | Boolean / Number | Duplicate flag, the first index, and a stable cluster id shared by all members of the same place |
| `failureReason` | String / null | When unmatched: `not_found`, `too_vague`, `country_conflict`, or `invalid_coordinates` |
| `inputQuality` | Object / null | `{ score, level, signals[], issues[], recommendation }` rating the input address string and how to improve it (null in reverse mode) |
| `validation` | Object | `{ countryMatched, matchedCountry, insideAllowedBoundary, boundaryViolation, postcodePresent, postcodeMatched }` from `expectedCountry` / `allowedCountries` / `allowedStates` / input postcode |
| `geofencing` | Object / null | When `geofences` are provided: `{ insideGeofence, matched[], results[] }` |
| `geospatial` | Object / null | When a reference point is set: `{ distanceKm, distanceMiles, bearing, insideRadius }` |
| `cacheHit` / `cacheSource` / `cacheAgeDays` | Boolean / String / Number | Whether the result came from the cross-run cache, and how old it was |
| `decisionTrail` | Array | Step-by-step processing audit (`full` profile) |

#### Address & enrichment fields

| Field | Type | Description |
|-------|------|-------------|
| `latitude` / `longitude` | Number / null | GPS coordinates of the resolved location |
| `displayName` | String / null | Full formatted address as returned by Nominatim |
| `normalizedAddress` | Object / null | Standardized components: `houseNumber`, `street`, `city`, `county`, `state`, `postcode`, `country`, `countryCode` |
| `administrativeHierarchy` | Object / null | Populated administrative levels broad → narrow (`country`, `state`, `county`, `district`, `city`) |
| `locationCategory` | String / null | Broad place category from OSM tags: `government`, `commercial`, `residential`, `landmark`, `education`, `healthcare`, `transport`, `area`, `other` |
| `addressCompleteness` | Number / null | 0-100 fraction of the core address components the geocoder populated |
| `enrichment` | Object / null | `plusCode` (Open Location Code), `hemisphere`, `continent`, `urbanicity` (urban/suburban/rural), `timezone` (IANA), `utcOffset` |
| `placeType` | String / null | Place classification such as "house", "street", "city", "country" |
| `placeRank` | Number / null | Nominatim place\_rank (0-30); higher is more specific (`full` profile) |
| `importance` | Number / null | Nominatim importance ranking score between 0 and 1 |
| `osmType` / `osmId` | String / Number | OpenStreetMap object type and identifier (`full` profile) |
| `boundingBox` | Array / null | Geographic bounding box as \[south lat, north lat, west lon, east lon] (`full` profile) |
| `alternatives` | Array | Candidate matches when `maxAlternatives` > 0, each with coordinates, display name, and importance |
| `extractedAt` | String | ISO 8601 timestamp of when the result was extracted |

When a query cannot be resolved, `matched` is `false`, `recommendedAction` is `reject`, `failureReason` explains why, and the location fields are `null` while `query`, `summary`, `inputQuality`, and `extractedAt` stay populated. Filter on `recommendedAction`, `matchType`, or `confidenceLevel` to separate clean matches from ones that need review.

#### Coverage summary record

After the result records, the actor pushes one `recordType: "summary"` record so you can read the health of the whole batch at a glance:

```json
{
    "recordType": "summary",
    "schemaVersion": "2.6.0",
    "mode": "forward",
    "workflow": "audit",
    "coverage": { "requested": 100, "succeeded": 92, "failed": 8, "successRate": 0.92 },
    "dataQualityScore": 89,
    "healthGrade": "B",
    "duplicatesFound": 6,
    "duplicateClusters": 3,
    "duplicateRate": 0.06,
    "ambiguityRate": 0.04,
    "countryMismatchRate": 0.01,
    "countriesDetected": 3,
    "ambiguousQueries": 4,
    "topFailureReason": "too_vague",
    "precisionBreakdown": { "rooftop": 61, "street": 24, "neighborhood": 0, "city": 7, "region": 0, "country": 0, "unknown": 8 },
    "matchTypeBreakdown": { "exact": 58, "normalized": 27, "partial": 3, "ambiguous": 4, "fallback": 0, "none": 8 },
    "auditReport": { "valid": 85, "needsReview": 7, "duplicates": 6, "countryConflicts": 1, "poorQuality": 11 },
    "datasetDiagnosis": { "qualityGrade": "B", "duplicateRisk": "medium", "ambiguityRisk": "low", "deliverabilityRisk": "low", "countryConflictRisk": "low" },
    "rateLimited": false,
    "summary": "92/100 geocoded. Grade B (quality 89/100). 85 ready, 7 to review, 8 unresolved. 6 duplicate(s) in 3 cluster(s), 4 ambiguous, 3 countries.",
    "timestamp": "2025-01-15T14:34:01.500Z"
}
```

The `auditReport` is your address-quality audit in one object (valid / needs-review / duplicates / country-conflicts / poor-quality counts); the `datasetDiagnosis` grades the batch and assigns a risk level to each quality dimension; `datasetRecommendations` ranks what to fix next; and `stewardReport` names the single highest-priority issue and the quality points recoverable by fixing it. The per-dimension rates feed automation thresholds.

***

### Use cases

- **Real estate analysis** -- geocode property addresses to plot them on maps, calculate distances between listings, or enrich property datasets with GPS coordinates
- **Logistics and delivery routing** -- convert customer delivery addresses into coordinates for route optimization and distance calculations
- **Lead enrichment** -- add geographic coordinates to business contact lists for proximity-based sales targeting and territory mapping
- **Data cleaning and normalization** -- standardize messy address data by geocoding free-text addresses and extracting structured components (city, state, postcode)
- **Store locator databases** -- build store finder features by geocoding retail locations and storing their coordinates for nearest-store lookups
- **Academic research** -- geocode survey respondent locations, historical site addresses, or field study coordinates for spatial analysis and GIS mapping
- **Event planning** -- convert venue addresses to coordinates for generating maps, calculating travel times, and finding nearby hotels or restaurants
- **IoT and sensor data** -- reverse geocode GPS coordinates from tracking devices, fleet vehicles, or mobile apps into human-readable addresses
- **Market research** -- map competitor locations, analyze geographic distribution of customers, or identify underserved areas by geocoding address datasets

***

### API & integrations

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("i4BCLB2eHbePe78aj").call(run_input={
    "mode": "forward",
    "queries": [
        "1600 Pennsylvania Ave, Washington DC",
        "221B Baker Street, London",
        "Champs-Elysees, Paris"
    ],
    "language": "en"
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"{item['query']} -> {item['latitude']}, {item['longitude']}")
```

#### JavaScript

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

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });

const run = await client.actor('i4BCLB2eHbePe78aj').call({
    mode: 'forward',
    queries: [
        '1600 Pennsylvania Ave, Washington DC',
        '221B Baker Street, London',
        'Champs-Elysees, Paris',
    ],
    language: 'en',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.log(`${item.query} -> ${item.latitude}, ${item.longitude}`);
});
```

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/i4BCLB2eHbePe78aj/runs?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "forward",
    "queries": ["1600 Pennsylvania Ave, Washington DC", "Big Ben, London"],
    "language": "en"
  }'
```

#### Integration platforms

- **Google Sheets** -- export geocoded results directly to spreadsheets for mapping and visualization
- **Zapier** -- trigger geocoding runs and route results to 5,000+ connected apps
- **Make (Integromat)** -- build multi-step automation workflows with geocoded address data
- **Webhooks** -- receive HTTP notifications when geocoding runs complete for event-driven pipelines
- **Slack / Email** -- get notified of completed runs or failed lookups automatically
- **Apify datasets API** -- fetch results programmatically in JSON, CSV, XML, or Excel format from any language

***

### Use in Dify

Drop this actor into [Dify](https://docs.apify.com/platform/integrations/dify) workflows via the Apify plugin's Run Actor node. Each address returns geocoded, scored, and classified as structured JSON — `use` / `verify` / `reject` plus the `precision` tier and `confidenceScore` your downstream node branches on. A raw geocoding API pointed at the same address returns a coordinate with no quality signal; this returns a decision about whether that coordinate is trustworthy.

- **Actor ID:** `ryanclinton/nominatim-geocoder`
- **Sample input** (geocode a batch of customer addresses and only auto-accept the clean ones):

```json
{
    "mode": "forward",
    "queries": [
        "1600 Pennsylvania Ave, Washington DC",
        "Eiffel Tower, Paris",
        "221B Baker Street, London"
    ],
    "minConfidence": 75,
    "outputProfile": "standard"
}
```

**Branching on the decision.** Wire a Dify if/else node to the `recommendedAction` field so each address routes itself without anyone reading the coordinates:

- `recommendedAction == "use"` → write the coordinates straight to your database / CRM / map layer
- `recommendedAction == "verify"` → send to a human-review queue (medium/low confidence — the match resolved but may be coarse)
- `recommendedAction == "reject"` → flag the address as undeliverable / unresolvable and request a correction

For tighter control, branch on `precision` (e.g. only accept `rooftop` or `street` for last-mile delivery, allow `city` for analytics) or threshold on the numeric `confidenceScore`. The `summary` field is a plain-English one-liner an LLM node can quote verbatim into a notification.

**Opt-in modes Dify workflows can leverage:** set `minConfidence` to have the actor pre-filter low-quality matches (they still appear flagged, but routing can skip them); set `maxAlternatives` to receive candidate matches in an `alternatives` array for a disambiguation step; set `outputProfile: "minimal"` to keep the Dify variable payload lean. The end-of-run `recordType: "summary"` record gives a single node the batch coverage (`successRate`, precision/confidence distributions) without aggregating per-row.

***

### How it works

The actor processes geocoding requests through the following pipeline:

1. **Input validation** -- the actor reads the input configuration and validates the selected mode, checking that queries are provided for forward mode or that matching latitude/longitude arrays exist for reverse mode.
2. **Request construction** -- for each item in the batch, the actor builds a Nominatim API request URL with the appropriate parameters (`/search` for forward, `/reverse` for reverse) including format, address detail, country code, and language settings.
3. **Rate-limited execution** -- requests are sent sequentially with a 1.1-second delay between each call. A custom User-Agent header identifies the actor to comply with Nominatim's terms of service.
4. **Response parsing** -- the Nominatim JSON response is parsed and mapped into a standardized 16-field output structure. The city field is intelligently resolved from city, town, village, or municipality depending on what Nominatim returns.
5. **Null-safe fallback** -- if a query returns no results or encounters an error, a result record is still created with the original query and all location fields set to null.
6. **Dataset push** -- all results are pushed to the Apify dataset in a single batch for efficient storage and retrieval.

```
Input Addresses          Nominatim API            Structured Output
+------------------+     +------------------+     +------------------+
| "Eiffel Tower"   | --> | /search?q=...    | --> | lat: 48.8584     |
| "Big Ben"        |     | format=jsonv2    |     | lon: 2.2945      |
| "Colosseum"      |     | addressdetails=1 |     | city: Paris      |
+------------------+     +------------------+     | country: France  |
        |                   1.1s delay              +------------------+
        |                 between each                      |
        v                   request                         v
  Batch Queue        Rate-Limited Fetch           Apify Dataset
```

***

### Performance & cost

The actor runs on minimal resources since it only performs lightweight HTTP API calls with no browser rendering or HTML parsing.

| Batch size | Estimated time | Estimated Apify cost | Nominatim API cost |
|------------|---------------|----------------------|--------------------|
| 10 addresses | ~12 seconds | < $0.01 | Free |
| 50 addresses | ~1 minute | < $0.01 | Free |
| 100 addresses | ~2 minutes | $0.01 -- $0.02 | Free |
| 500 addresses | ~9 minutes | $0.05 -- $0.08 | Free |
| 1,000 addresses | ~18 minutes | $0.10 -- $0.15 | Free |

- **Memory usage**: 256 MB (minimum tier) is sufficient for all batch sizes
- **Processing speed**: ~1.1 seconds per address due to Nominatim's mandatory rate limit
- **Cost driver**: Apify compute time is the only cost; the Nominatim API itself is completely free
- **Comparison**: Google Maps Geocoding API charges $5.00 per 1,000 requests; this actor geocodes the same volume for approximately $0.10 in Apify compute

***

### Limitations

- **Rate limit of 1 request per second** -- Nominatim enforces this strictly, so large batches (1,000+ addresses) will take 18+ minutes. This cannot be bypassed without hosting your own Nominatim instance.
- **Single best result by default** -- forward geocoding returns the top-ranked result unless you set `maxAlternatives`, which adds up to 10 candidate matches per address in an `alternatives` array for disambiguation.
- **OpenStreetMap data coverage varies** -- accuracy is excellent in North America, Europe, and major cities worldwide, but may be lower in rural or less-mapped regions of developing countries.
- **Address component language availability** -- multi-language results depend on translations existing in OpenStreetMap. Less common languages may fall back to the local language of the location.
- **Confidence is a deterministic heuristic** -- the `confidenceScore` is derived from the match precision and OpenStreetMap importance, not a probabilistic model. It tells you how specific and prominent the match is, which is the right signal for filtering, but it is not a statistical match probability.
- **Sequential processing only** -- due to the rate limit, requests are processed one at a time. Parallel geocoding is not supported by the public Nominatim instance.

***

### Responsible use

- **Respect Nominatim usage policies** -- this actor complies with the [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) by enforcing rate limits and providing a descriptive User-Agent header. Do not attempt to bypass these limits.
- **Attribute OpenStreetMap** -- Nominatim data comes from OpenStreetMap contributors. If you display geocoded results publicly, include appropriate attribution as required by the [ODbL license](https://www.openstreetmap.org/copyright).
- **Avoid excessive batch sizes** -- while the actor handles rate limiting automatically, submitting extremely large batches (10,000+ addresses) places unnecessary load on the free public Nominatim infrastructure. For very large workloads, consider hosting your own Nominatim instance.
- **Do not use for real-time applications** -- the 1-second rate limit makes this unsuitable for user-facing real-time geocoding. Use it for batch processing and data enrichment workflows instead.
- **Handle personal data responsibly** -- if you are geocoding personal addresses, ensure compliance with applicable data protection regulations (GDPR, CCPA, etc.) regarding the processing and storage of location data.

***

### FAQ

**Q: Is the Nominatim geocoding API free to use?**
**A:** Yes. Nominatim is a free, open-source geocoding service powered by OpenStreetMap data. There are no API keys, subscriptions, or per-request charges. The only cost is Apify compute time for running the actor.

**Q: How accurate is Nominatim compared to Google Maps Geocoding?**
**A:** For well-mapped regions (North America, Europe, Australia, major cities worldwide), Nominatim accuracy is comparable to commercial services. OpenStreetMap data is community-maintained and coverage varies by region. The `importance` field provides a rough indicator of place prominence, though it is not a match confidence score.

**Q: Do I need an API key or account to use this actor?**
**A:** No. The Nominatim API requires no authentication. You only need an Apify account to run the actor. The actor handles all Nominatim communication including the required User-Agent header.

**Q: What happens if an address cannot be found?**
**A:** The actor returns a result record with the original query preserved in the `query` field and all location fields (`latitude`, `longitude`, `displayName`, `city`, etc.) set to `null`. This makes it easy to identify and retry failed lookups.

**Q: Can I geocode addresses in any country?**
**A:** Yes. OpenStreetMap has global coverage spanning 200+ countries. Use the `countryCode` parameter with an ISO 3166-1 alpha-2 code (e.g., "US", "GB", "JP", "BR", "IN") to restrict results and improve accuracy for a specific country.

**Q: How many addresses can I geocode in one run?**
**A:** There is no hard limit on batch size. Processing time scales linearly at approximately 1.1 seconds per address. A batch of 100 addresses takes about 2 minutes; 1,000 addresses takes about 18 minutes. For batches larger than 1,000, consider splitting across multiple scheduled runs.

**Q: Can I get results in languages other than English?**
**A:** Yes. Set the `language` parameter to any ISO 639-1 language code (e.g., "de" for German, "fr" for French, "ja" for Japanese, "ar" for Arabic). Address components will be returned in the specified language when translations exist in OpenStreetMap.

**Q: What is the difference between forward and reverse geocoding?**
**A:** Forward geocoding converts a text address (like "1600 Pennsylvania Ave, Washington DC") into GPS coordinates (38.8977, -77.0365). Reverse geocoding does the opposite -- it takes GPS coordinates and returns the corresponding street address.

**Q: Why does the actor take over a second per address?**
**A:** The public Nominatim API enforces a strict rate limit of 1 request per second. The actor waits 1.1 seconds between requests to ensure compliance and prevent your requests from being blocked. This is a limitation of the free public service, not the actor itself.

**Q: Can I use the results for commercial purposes?**
**A:** Yes. OpenStreetMap data is licensed under the Open Database License (ODbL), which permits commercial use provided you give appropriate attribution and share any modifications to the data itself. Geocoding results derived from the data can be used freely.

**Q: What coordinate format does reverse geocoding expect?**
**A:** Latitudes and longitudes should be provided as decimal degree strings (e.g., "48.8584" for latitude, "2.2945" for longitude). Use at least 4 decimal places for street-level accuracy. The latitudes and longitudes lists must be the same length.

**Q: How does the actor handle errors or API failures?**
**A:** If a Nominatim request fails due to a network error or API issue, the actor logs the error and returns a result with null location fields for that query. The batch continues processing remaining items. No data is lost -- you can identify failures by checking for null `latitude` values.

***

### Related actors

| Actor | Description |
|-------|-------------|
| [OpenStreetMap POI Search](https://apify.com/ryanclinton/osm-poi-search) | Search for points of interest (restaurants, shops, parks, hospitals) near geocoded coordinates using OpenStreetMap data |
| [Open Charge Map EV Stations](https://apify.com/ryanclinton/open-charge-map) | Find electric vehicle charging stations near any geocoded location worldwide |
| [Weather Forecast Search](https://apify.com/ryanclinton/weather-forecast-search) | Get weather forecasts and current conditions for geocoded GPS coordinates |
| [IP Geolocation Lookup](https://apify.com/ryanclinton/ip-geolocation-lookup) | Convert IP addresses to geographic locations -- a complementary approach to address-based geocoding |
| [REST Countries Data Search](https://apify.com/ryanclinton/rest-countries-search) | Look up country details including ISO codes, currencies, and languages to pair with geocoded country results |

# Actor input Schema

## `mode` (type: `string`):

Forward: address to coordinates. Reverse: coordinates to address.

## `workflow` (type: `string`):

Job-named preset for your use case (composes with Mode). 'crm-cleanse' = repair dirty CRM addresses; 'lead-routing' = assign sales territory + owner; 'delivery-validation' = logistics serviceability; 'audit' = grade dataset quality; 'validate' = check addresses; 'enrich' = full location intelligence; 'deduplicate' = find duplicate places. Tunes the default output detail; the full decision layer is always computed.

## `queries` (type: `array`):

List of addresses to geocode (used in forward mode).

## `latitudes` (type: `array`):

List of latitudes for reverse geocoding. Must match longitudes list length.

## `longitudes` (type: `array`):

List of longitudes for reverse geocoding. Must match latitudes list length.

## `countryCode` (type: `string`):

Restrict results to a country (ISO 3166-1 alpha-2 code, e.g. 'US', 'GB', 'DE'). Forward mode only.

## `language` (type: `string`):

Preferred language for results (e.g. 'en', 'de', 'fr').

## `maxAlternatives` (type: `integer`):

Forward mode only. When greater than 0, also return up to N alternative candidate matches per address in an 'alternatives' array, so you can disambiguate when the top match might be wrong. 0 returns only the single best match.

## `minConfidence` (type: `integer`):

Quality gate (0-100). Matches scoring below this confidence still appear in the dataset flagged as suppressed, but are not charged. 0 keeps and charges every match.

## `outputProfile` (type: `string`):

How many fields each result carries. 'minimal' = query, coordinates, precision, confidence and recommended action only. 'standard' = full address breakdown plus the decision layer. 'full' = everything including OpenStreetMap internals, bounding box, decision trail and next-actor suggestions.

## `structuredAddresses` (type: `array`):

Forward mode: provide addresses as structured objects ({ houseNumber, street, city, county, state, postcode, country }) for higher-precision matching than free text. When set, this overrides the Addresses field. Ideal for CRM / Salesforce / HubSpot exports.

## `expectedCountry` (type: `string`):

ISO 3166-1 alpha-2 code. When set, every result gets a countryMatched flag verifying it resolved to this country.

## `allowedCountries` (type: `array`):

Boundary validation: ISO 3166-1 alpha-2 codes a result must fall within. Results outside get boundaryViolation=true.

## `allowedStates` (type: `array`):

Boundary validation: state or region names a result must fall within (case-insensitive, e.g. 'England', 'California').

## `referenceLat` (type: `number`):

When set with reference longitude, every result reports its distance and bearing from this point.

## `referenceLon` (type: `number`):

When set with reference latitude, every result reports its distance and bearing from this point.

## `maxRadiusKm` (type: `number`):

When set with a reference point, results within this many km are flagged insideRadius=true.

## `geofences` (type: `array`):

Circular or polygon geofences. Each result reports which it falls inside. Circle: { "name": "Depot", "lat": 51.5, "lon": -0.1, "radiusKm": 25 }. Polygon: { "name": "Zone A", "polygon": \[\[lat,lon],\[lat,lon],\[lat,lon]] }. Useful for delivery zones and territory routing.

## `salesTerritories` (type: `array`):

Named territories; each result is assigned the first match (the territory field) and its optional owner (recommendedOwner). Define geographically — { "name": "North East", "owner": "jane@co", "polygon": \[\[lat,lon],…] } or { "name": "London", "lat": 51.5, "lon": -0.1, "radiusKm": 40 } — or administratively — { "name": "UK Enterprise", "countries": \["GB"], "states": \["England"] }. Revenue teams use this for territory assignment and lead routing.

## `cacheName` (type: `string`):

Opt-in cross-run cache. When set, repeated addresses are served instantly from a named store instead of re-calling Nominatim, and are not charged. Leave blank to disable caching.

## `cacheMaxAgeDays` (type: `integer`):

How long a cached result stays valid before being re-fetched.

## `inputDatasetId` (type: `string`):

Read forward-mode addresses from another Apify dataset (e.g. a previous actor's output) instead of, or in addition to, the Addresses field.

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

The field name to read the address from when an input dataset ID is set.

## Actor input object example

```json
{
  "mode": "forward",
  "workflow": "standard",
  "queries": [
    "1600 Pennsylvania Ave, Washington DC"
  ],
  "language": "en",
  "maxAlternatives": 0,
  "minConfidence": 0,
  "outputProfile": "standard",
  "cacheMaxAgeDays": 30,
  "addressField": "address"
}
```

# Actor output Schema

## `results` (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 = {
    "queries": [
        "1600 Pennsylvania Ave, Washington DC"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ryanclinton/nominatim-geocoder").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 = { "queries": ["1600 Pennsylvania Ave, Washington DC"] }

# Run the Actor and wait for it to finish
run = client.actor("ryanclinton/nominatim-geocoder").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 '{
  "queries": [
    "1600 Pennsylvania Ave, Washington DC"
  ]
}' |
apify call ryanclinton/nominatim-geocoder --silent --output-dataset

```

## MCP server setup

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

```

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/i4BCLB2eHbePe78aj/builds/T2YSZfhGPZdomrSCR/openapi.json
