# Geocoding Scraper — Address ↔ Coordinates (OpenStreetMap) (`yadroo/osm-geocode`) Actor

Free geocoding for agents and CRMs via OpenStreetMap: address or place → lat/lon with full address breakdown, structured queries (street/city/postcode), reverse geocoding with zoom levels, OSM id lookup, bounded POI search, boundary polygons, multilingual names. Nominatim or Photon (fuzzy).

- **URL**: https://apify.com/yadroo/osm-geocode.md
- **Developed by:** [Samat Makatov](https://apify.com/yadroo) (community)
- **Categories:** Developer tools, Lead generation, Travel
- **Features:** Limited permissions
- **Stats:** 20 total users, 8 monthly users, 95.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 result items

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Key-less geocoding on OpenStreetMap data for agents, CRMs and data pipelines. Forward (address or place → lat/lon + full address breakdown), **structured** queries (street/city/postcode fields for messy CRM data), reverse (`lat,lon` → address at the zoom level you need), OSM id lookup, bounded POI search inside a box, boundary polygons as GeoJSON and names in any language. Two engines: **Nominatim** (exact, rich) and **Photon** (typo-tolerant). Worldwide, no API key, no proxy.

> **Need it tailored to your workflow?** Missing a field, a filter, another source or a different output format? [Open an issue](https://apify.com/yadroo/osm-geocode/issues) and describe what you need — we read every request and adapt the actor.

### Use cases

- **CRM address cleaning** — geocode 500 customer addresses with `structuredQueries`, keep `found`, `lat/lon`, normalized `postcode`/`city`; with `emitNotFound: true` rows stay 1:1 with input.
- **Store / branch enrichment** — `reverseZoom: 10` turns delivery GPS points into city names for territory reports.
- **Local lead lists** — `queries: ["pharmacy"]` + `viewbox` + `bounded: true` lists every mapped pharmacy in a district with street and house number; `extraTags` adds opening hours and websites where mapped.
- **Service-area maps** — `polygon: true` gives district/city boundaries as GeoJSON for coverage checks.
- **Multilingual labels** — `lang: "ru"` or `nameDetails: true` for Cyrillic / Kazakh / local names on the same object.
- **Dedupe & entity resolution** — stable `osmType`+`osmId`, `placeRank`, `importance` and `bestMatch` to pick the right candidate among ambiguous names.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `queries` | string\[] | — | Text address/place → forward; `lat,lon` → reverse; `R62422` / `N1,W2` → lookup. Points are also read in other notations: `40.7488° N, 73.9854° W`, `48°51'30"N 2°17'40"E`, `(51.5007, -0.1246)`, `48,8584; 2,2945`, `lat: 51.5 lon: -0.12`, `POINT(2.2945 48.8584)` (lon lat) and Google Maps / openstreetmap.org links; the log and `SUMMARY.readAsCoordinates` show what each was read as. |
| `structuredQueries` | object\[] | — | `{ amenity, street, city, county, state, country, postalcode, label }` (Nominatim). |
| `mode` | select | `auto` | `auto`, `forward`, `reverse`, `lookup`. |
| `engine` | select | `nominatim` | `nominatim` or `photon` (structured & lookup always use Nominatim). |
| `lang` | string | `en` | Accept-Language, e.g. `ru`, `kk`, `de`, `ru,en`; empty = `en`. Photon: `en`/`de`/`fr` only. |
| `countryCodes` | string | — | `kz,ru` — ISO-3166 alpha-2, separated by commas (or `;`, `|`, new lines). Alpha-3 codes (`USA`, `KAZ`) and English country names (`Germany`, `United Kingdom`) are read too and named in `SUMMARY.warnings`; any other value stops the run before the first request with the closest countries. |
| `viewbox` | string | — | `minLon,minLat,maxLon,maxLat`, e.g. Almaty `76.75,43.15,77.10,43.40` (spaces, or `;` with decimal commas, work too). |
| `bounded` | bool | `false` | Only results inside `viewbox` (enables area POI search). |
| `featureType` | select | — | `country`, `state`, `city`, `settlement`. |
| `layers` | multi | — | `address`, `poi`, `railway`, `natural`, `manmade`. |
| `limit` | int | `1` | Results per query, 1–50. Nominatim returns at most 40 (a higher value is lowered to 40 with a warning), Photon at most 50. |
| `reverseZoom` | int | `18` | 18 building · 17 street · 16 major street · 14 suburb · 12 town · 10 city · 8 county · 5 state · 3 country. |
| `addressDetails` | bool | `true` | `address` object + derived columns. |
| `extraTags` | bool | `false` | `extraTags`: website, opening_hours, wikidata, population… |
| `nameDetails` | bool | `false` | `nameDetails`: `name:ru`, `name:kk`, `official_name`… |
| `polygon` | bool | `false` | `geojson` outline. |
| `polygonThreshold` | number | `0` | Simplification tolerance in degrees (0.005 ≈ city level). |
| `dedupe` | bool | `true` | Nominatim result dedupe. |
| `emitNotFound` | bool | `false` | Emit `found: false` rows to keep 1:1 alignment. Such rows are charged like hits, so this is opt-in; not-found queries are always listed free in `SUMMARY.notFoundInputs`. |
| `outputFields` | string\[] | — | Projection in your order, e.g. `["query","lat","lon","countryCode"]`; `query` is always included. Wrong case, aliases (`lng`, `zip`) and near misses are corrected with a warning; unknown names are ignored with a warning; a list with no known column fails the run and lists the columns. |
| `maxItems` | int | `10000` | Hard cap on rows (one entry gives up to `limit` rows). |
| `osmTag` | string | — | Photon: `amenity:cafe`, `shop`, `!amenity:parking`. |
| `photonLayers` | multi | — | Photon: `house`, `street`, `locality`, `district`, `city`, `county`, `state`, `country`, `other`. |
| `biasLat`, `biasLon` | number | — | Photon: prefer results near this point. |
| `nominatimUrl` | string | — | Base URL of your own Nominatim server (the part before `/search`). No 1.1 s pace and no per-run cap there. Empty = nominatim.openstreetmap.org. |
| `photonUrl` | string | — | Base URL of your own Photon server (the part before `/api`). Empty = photon.komoot.io. |

Off-list values of the select fields (`mode`, `engine`, `featureType`, `layers`, `photonLayers`) are refused by Apify before the run starts; the allowed values are the ones listed above.

### Reference

#### Nominatim vs Photon

| | Nominatim | Photon |
|---|---|---|
| Matching | exact tokens, structured fields | fuzzy / prefix (typos OK) |
| Address breakdown | full (`address` object, 20+ keys) | street, housenumber, district, city, state, postcode, country |
| Reverse | yes, zoom-controlled | yes (nearest object) |
| OSM id lookup, polygons, extra tags, name details | yes | no |
| Language | any (`accept-language`) | en / de / fr |
| POI filter | `layers`, `featureType`, `bounded` box | `osm_tag`, `layer`, `bbox`, location bias |
| Policy | max 1 request/s, one at a time, identify the app, cache results (the actor keeps the pace, sends one request at a time with its own User-Agent, and requests a repeated entry of a run once; it keeps nothing between runs) | fair use (the actor uses the same pace) |

#### `reverseZoom` levels

| zoom | returns |
|---|---|
| 3 | country |
| 5 | state |
| 8 | county |
| 10 | city |
| 12 | town / borough |
| 13 | village / suburb |
| 14 | neighbourhood |
| 16 | major streets |
| 17 | major and minor streets |
| 18 | building |

#### Common `type` / `category` values

`boundary/administrative` (countries, regions, cities, districts) · `place/city|town|village|suburb|neighbourhood` · `highway/residential|primary|…` (streets) · `building/yes|office|apartments|…` · `amenity/pharmacy|cafe|bank|school|…` · `shop/*` · `tourism/*` · `office/*`. Full tag catalogue: https://wiki.openstreetmap.org/wiki/Map_features

### Examples

**Batch-geocode CRM addresses (1:1 rows)**

```json
{
  "structuredQueries": [
    { "street": "Nauryzbai Batyr 154A", "city": "Almaty", "country": "Kazakhstan", "label": "Kaspi HQ" },
    { "street": "Mangilik El 55", "city": "Astana", "country": "Kazakhstan", "label": "Client 2" }
  ],
  "lang": "ru",
  "emitNotFound": true,
  "outputFields": ["query", "found", "lat", "lon", "displayName", "postcode", "city", "osmUrl"]
}
```

**All pharmacies in a district (POI lead list)**

```json
{ "queries": ["pharmacy"], "viewbox": "76.85,43.20,77.00,43.30", "bounded": true, "limit": 40, "extraTags": true, "lang": "ru" }
```

**GPS points → city names for reporting**

```json
{ "queries": ["43.2363,76.9457", "51.1282,71.4307", "42.3417,69.5901"], "reverseZoom": 10, "outputFields": ["query", "city", "state", "countryCode"] }
```

**City boundaries as GeoJSON**

```json
{ "queries": ["Almaty", "Astana"], "featureType": "city", "countryCodes": "kz", "polygon": true, "polygonThreshold": 0.005 }
```

**Fuzzy search of user-typed input (Photon)**

```json
{ "queries": ["Almty Nauryzbai Batyr 154"], "engine": "photon", "limit": 3, "biasLat": 43.24, "biasLon": 76.95 }
```

### Output

```json
{
  "query": "Nauryzbai Batyr 154A, Almaty",
  "mode": "forward",
  "engine": "nominatim",
  "rank": 1,
  "bestMatch": true,
  "found": true,
  "placeId": 213728564,
  "osmType": "way",
  "osmId": 51267846,
  "osmUrl": "https://www.openstreetmap.org/way/51267846",
  "displayName": "Kaspi.kz, 154А, Nauryzbai Batyr Street, Керемет, Көктем, Bostandıq District, Almaty, 050013, Kazakhstan",
  "name": "Kaspi.kz",
  "lat": 43.2399856,
  "lon": 76.9388247,
  "category": "building",
  "type": "office",
  "addressType": "building",
  "placeRank": 30,
  "importance": 0.0000743,
  "boundingBox": [43.2398836, 43.2400877, 76.9383802, 76.9392699],
  "country": "Kazakhstan",
  "countryCode": "KZ",
  "state": null,
  "county": null,
  "city": "Almaty",
  "district": "Bostandıq District",
  "suburb": "Көктем",
  "postcode": "050013",
  "road": "Nauryzbai Batyr Street",
  "houseNumber": "154А",
  "address": { "building": "Kaspi.kz", "house_number": "154А", "road": "Nauryzbai Batyr Street", "city": "Almaty", "ISO3166-2-lvl4": "KZ-75", "postcode": "050013", "country": "Kazakhstan", "country_code": "kz" },
  "extraTags": { "building:levels": "7" },
  "nameDetails": null,
  "geojson": null,
  "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
  "sourceUrl": "https://nominatim.openstreetmap.org/search?q=…",
  "fetchedAt": "2026-09-12T23:50:01.000Z"
}
```

| Field | Description |
|---|---|
| `query`, `mode`, `engine` | Input label (or structured `label`), detected mode, engine used. |
| `rank`, `bestMatch`, `found` | 1-based rank; rank 1 is `bestMatch`; `found: false` rows carry nulls. |
| `placeId`, `osmType`, `osmId`, `osmUrl` | Stable OSM identity (use `osmType+osmId` as key; `placeId` changes between Nominatim imports). |
| `displayName`, `name` | Full label and object name. |
| `lat`, `lon`, `boundingBox` | WGS84; bbox as `[minLat, maxLat, minLon, maxLon]` (Nominatim order). |
| `category`, `type`, `addressType`, `placeRank`, `importance` | OSM class/type, address level, 0–1 relevance. |
| `country`…`houseNumber` | Normalized address columns; raw parts in `address`. |
| `extraTags`, `nameDetails`, `geojson` | Only when requested. |
| `structured` | Echo of the structured query object (structured rows only). |

The dataset schema describes every column (type, title, description, example) for agents and MCP clients.

`SUMMARY` key-value record: `{ queries, found, notFound, rows, errors[{query, error}], engine, errorCount, notFoundCount, notFoundInputs[], notFoundRowsEmitted, notSentOverCap, notSentRefused, notProcessed, notProcessedInputs[], requests{server: n}, reusedAnswers, refusedBy, warnings[], delivered, stoppedBy, status }` — `notProcessedInputs` lists (up to 100) entries the run did not reach (spending limit, timeout or `maxItems`), so you can run just those again.

### Use it from code / agents

```bash
curl -X POST "https://api.apify.com/v2/acts/yadroo~osm-geocode/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' -d '{"queries":["Almaty, Kazakhstan","43.2363,76.9457"],"lang":"ru"}'
```

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/osm-geocode').call({ structuredQueries: [{ street: 'Nauryzbai Batyr 154A', city: 'Almaty', country: 'Kazakhstan' }] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/osm-geocode").call(run_input={"queries": ["pharmacy"], "viewbox": "76.85,43.20,77.00,43.30", "bounded": True, "limit": 40})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

MCP: connect `https://mcp.apify.com` and call the `yadroo/osm-geocode` tool with the same JSON.

### Pricing

Pay per event: **$0.005 per run start + $0.002 per result row** (from 5 November 2026; until then $0.001 + $0.001) (not-found rows are included only when you turn `emitNotFound` on; failed queries and entries that were not sent are never charged). Bronze −5 %, Silver and above −10 % on the row price (from 5 November 2026; until then −10 / −20 / −30 %); the start event is the same on every plan; platform usage is included. From 5 November: 100 addresses at `limit: 1` ≈ $0.21; a bounded POI search returning 40 rows ≈ $0.085.

The run respects your **maximum cost per run**: it stops when the next row would go over it and ends with "Stopped at your spending limit: N rows delivered".

### Limits & FAQ

- **Usage policy and speed.** The public Nominatim server allows at most 1 request per second, from one machine, one request at a time, with no parallel or distributed jobs, and asks bulk users to cache results; Photon asks for fair use. The actor starts one request every 1.1 s per server, requests a repeated entry only once per run (repeats reuse the first answer while the kept answers fit in 16 MB, beyond that a repeat is asked again; nothing is kept between runs), sends at most 1,000 requests per run to each public server (a migrated or restarted run continues the count), and slows down when a server throttles: after a 429 (too many requests from this Apify server — other users share its IP), a 5xx or a timeout it pauses 30 s, 60 s, then 120 s and continues at half the pace each time (down to one request per 4.4 s); the log says so and `SUMMARY.pausesAfterThrottling` counts it. It stops sending to a server that answers 401/403, that keeps throttling after these pauses, or that refuses from the very first request (then after one 20 s pause, so the run ends within a minute) — those entries are listed in `SUMMARY.errors` as not sent, never charged. **Do not start several runs in parallel against the public servers.** For big batches, run your own Nominatim or Photon and set `nominatimUrl` / `photonUrl`: no pace and no per-run cap there.
- **Capacity per run.** The default 20-minute timeout fits about 1,000 entries — the per-run cap — at 1.1 s each while the server does not throttle. When a list is longer, the log says so at the start (how many fit and the timeout that would fit them), and a bigger batch logs a progress line every 15 s (entries done, rows saved, time left) — the run is working, not stuck. The run stops starting new requests shortly before its timeout, keeps every row it has, ends SUCCEEDED with "Stopped before the run timeout: N rows saved" and lists the entries it did not reach in `SUMMARY.notProcessedInputs`. A longer list: raise the timeout (up to the 1,000-request cap), or run the rest later, one run after another.
- **Accuracy.** OSM coverage varies: exact house numbers exist for most cities in Europe/CIS/US; rural addresses may resolve to the street or village (check `addressType`/`placeRank`).
- **Ambiguity.** Use `countryCodes`, `viewbox`, `featureType` or `structuredQueries`; inspect `importance` and `rank` when `limit > 1`.
- **Errors.** HTTP 429/5xx and timeouts → up to 3 attempts with backoff; a failing query is listed in `SUMMARY.errors` (no row, not charged) and the run continues. This also covers queries that do not fit a forced `mode` (e.g. an address while `mode: "reverse"`) and structured queries with no address part (only `label`): they are reported per query, the rest of the batch still runs. Nothing fetched at all (for example the public server refuses this Apify server from the first request) → the run fails within about a minute, uncharged, and the status message gives the reason once with what to do.
- **Licence.** Data © OpenStreetMap contributors, ODbL — attribution required when you display it; Nominatim results may not be used for autocomplete-as-you-type (use Photon for that).
- **Restarts.** If Apify migrates or restarts the run, it continues where it stopped; rows already saved are not saved or charged again.
- **Roadmap.** Distance/nearest computation between rows.

***

Made by **Yadroo** · Sibling actors: [open-meteo-weather](https://apify.com/yadroo/open-meteo-weather) · [ip-intel](https://apify.com/yadroo/ip-intel) · [public-holidays](https://apify.com/yadroo/public-holidays) · [krisha-kz](https://apify.com/yadroo/krisha-kz)

# Actor input Schema

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

Free-text address or place → forward geocoding; `lat,lon` → reverse (also `40.7° N, 73.9° W`, degrees/minutes/seconds, `(lat, lon)` and map links); OSM ids like `R62422` / `N123,W456` → lookup (Nominatim). A repeated entry reuses the first answer (up to 16 MB of kept answers) and still gets its own rows. Public servers: one request at a time, every 1.1 s (Nominatim policy) — about 700 entries per run at the default 15-minute timeout, at most 1,000 per run.

## `structuredQueries` (type: `array`):

Precise address parts instead of one string: objects with any of `amenity`, `street` (house number + street), `city`, `county`, `state`, `country`, `postalcode`, optional `label`. Better hit-rate for CRM addresses than free text.

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

Force one mode for all queries or let the actor detect it per entry.

## `engine` (type: `string`):

Both are public OpenStreetMap geocoding servers. Nominatim: exact matching, rich address breakdown, 1 request per second. Photon: fuzzy matching (good for messy user input), fewer address fields, languages en/de/fr only. Structured queries and OSM lookups always use Nominatim.

## `lang` (type: `string`):

Accept-Language for names, e.g. `en`, `ru`, `kk`, `de`, or a list `ru,en`. Empty = `en`. Photon supports only en/de/fr (otherwise local names).

## `countryCodes` (type: `string`):

Comma-separated ISO-3166 alpha-2 codes, e.g. `kz,ru`. Alpha-3 codes (`USA`) and English country names (`Germany`) are read too; an unknown value stops the run before any request. Empty = worldwide.

## `viewbox` (type: `string`):

Prefer results inside this box, e.g. Almaty: `76.75,43.15,77.10,43.40`. With `bounded` = true results outside are excluded (turns a search like `pharmacy` into an in-area POI search).

## `bounded` (type: `boolean`):

Nominatim: exclude results outside `viewbox`.

## `featureType` (type: `string`):

Nominatim: return only places of this administrative type.

## `layers` (type: `array`):

Nominatim: restrict to `address`, `poi`, `railway`, `natural`, `manmade`. Empty = all.

## `limit` (type: `integer`):

Results per query. Nominatim returns at most 40 (a higher value is lowered to 40 with a warning), Photon at most 50. `bestMatch` marks rank 1.

## `reverseZoom` (type: `integer`):

Nominatim reverse: 18 = building, 17 = street, 16 = major street, 14 = suburb, 12 = town/borough, 10 = city, 8 = county, 5 = state, 3 = country.

## `addressDetails` (type: `boolean`):

Include the `address` object and derived `country`, `city`, `road`… columns.

## `extraTags` (type: `boolean`):

Nominatim: `extraTags` with wikidata, website, opening_hours, building:levels, population… when mapped.

## `nameDetails` (type: `boolean`):

Nominatim: `nameDetails` with `name:ru`, `name:kk`, `official_name`, `alt_name`…

## `polygon` (type: `boolean`):

Nominatim: add the outline of the place as `geojson` (city/district boundaries, building footprints). Can be large — combine with `polygonThreshold`.

## `polygonThreshold` (type: `number`):

Degrees; 0.005 ≈ city-level simplification. 0 = full detail.

## `dedupe` (type: `boolean`):

Nominatim: merge the same street/place appearing as several OSM objects.

## `emitNotFound` (type: `boolean`):

Writes a `found: false` row for each query with no match, keeping input/output aligned 1:1. Each such row is a dataset item and is charged like a hit, so it is off by default; not-found queries are always listed free in the SUMMARY record. Failed queries (errors) never produce rows.

## `outputFields` (type: `array`):

Optional projection, e.g. `["query","lat","lon","displayName","countryCode"]`, in this order; `query` is always included. Wrong case, common aliases (`lng`, `zip`) and near misses are corrected with a warning; names that match no column are ignored with a warning, and a list with no column at all fails the run with the list of columns.

## `maxItems` (type: `integer`):

Hard cap on dataset rows (one entry gives up to `limit` rows). The run also stops at your max cost per run and before its timeout.

## `osmTag` (type: `string`):

Photon only. Filter/boost by tag: `amenity:cafe`, `tourism`, `!amenity:parking` (exclude), `shop:supermarket`.

## `photonLayers` (type: `array`):

Photon only: restrict result granularity.

## `biasLat` (type: `number`):

Photon only: prefer results near this point (with biasLon).

## `biasLon` (type: `number`):

Photon only.

## `nominatimUrl` (type: `string`):

Base URL of a Nominatim server you run (or may use without the public limits), e.g. `https://nominatim.example.com` — the part before `/search`. Requests then go there without the 1.1 s pace and the 1,000-requests-per-run cap. Empty = the public server nominatim.openstreetmap.org.

## `photonUrl` (type: `string`):

Base URL of a Photon server you run, e.g. `https://photon.example.com` (the part before `/api`). Empty = the public server photon.komoot.io.

## Actor input object example

```json
{
  "queries": [
    "Almaty, Kazakhstan",
    "Nauryzbai Batyr 154A, Almaty",
    "43.2363,76.9457",
    "R62422"
  ],
  "structuredQueries": [
    {
      "street": "Nauryzbai Batyr 154A",
      "city": "Almaty",
      "country": "Kazakhstan",
      "label": "Kaspi HQ"
    }
  ],
  "mode": "auto",
  "engine": "nominatim",
  "lang": "en",
  "bounded": false,
  "featureType": "",
  "limit": 1,
  "reverseZoom": 18,
  "addressDetails": true,
  "extraTags": false,
  "nameDetails": false,
  "polygon": false,
  "polygonThreshold": 0,
  "dedupe": true,
  "emitNotFound": false,
  "maxItems": 10000
}
```

# Actor output Schema

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

No description

## `summary` (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": [
        "Almaty, Kazakhstan",
        "Nauryzbai Batyr 154A, Almaty",
        "43.2363,76.9457",
        "R62422"
    ],
    "structuredQueries": [
        {
            "street": "Nauryzbai Batyr 154A",
            "city": "Almaty",
            "country": "Kazakhstan",
            "label": "Kaspi HQ"
        }
    ],
    "lang": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("yadroo/osm-geocode").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": [
        "Almaty, Kazakhstan",
        "Nauryzbai Batyr 154A, Almaty",
        "43.2363,76.9457",
        "R62422",
    ],
    "structuredQueries": [{
            "street": "Nauryzbai Batyr 154A",
            "city": "Almaty",
            "country": "Kazakhstan",
            "label": "Kaspi HQ",
        }],
    "lang": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("yadroo/osm-geocode").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": [
    "Almaty, Kazakhstan",
    "Nauryzbai Batyr 154A, Almaty",
    "43.2363,76.9457",
    "R62422"
  ],
  "structuredQueries": [
    {
      "street": "Nauryzbai Batyr 154A",
      "city": "Almaty",
      "country": "Kazakhstan",
      "label": "Kaspi HQ"
    }
  ],
  "lang": "en"
}' |
apify call yadroo/osm-geocode --silent --output-dataset

```

## MCP server setup

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

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/S2JxnDI1OgwXYCtJY/builds/kG1E0gqyOzcutsxQc/openapi.json
