# Photon Geocoding Scraper (`automation-lab/photon-forward-reverse-geocoding`) Actor

Batch forward and reverse geocode addresses and coordinates through Photon into normalized OpenStreetMap location records.

- **URL**: https://apify.com/automation-lab/photon-forward-reverse-geocoding.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.76 / 1,000 geocoding-record extracteds

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?

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

## Photon Geocoding Scraper

Batch forward-geocode addresses and place names and reverse-geocode GPS coordinates through the public Photon service. The Actor exports ranked coordinates, normalized labels, place types, address components, and exposed OpenStreetMap properties as integration-ready dataset rows.

Use one run for forward inputs, reverse inputs, or both. No Photon API key, login, browser, or paid proxy is required.

### What does Photon Geocoding Scraper do?

The Actor sends bounded requests to Photon’s GeoJSON API and converts every valid matched feature into a flat record. It preserves the full properties object while promoting commonly used fields such as latitude, longitude, city, postcode, country code, OSM ID, and match rank.

It is designed for repeatable batch location enrichment, not for scraping a map user interface.

### Who is this Actor for?

- Data engineers enriching CRM, delivery, property, or event tables with coordinates.
- Operations teams converting field GPS points into readable places.
- GIS analysts who need OSM-linked candidates for disambiguation.
- Developers prototyping location search without maintaining Photon request code.
- Automation builders scheduling the same geocoding input through Apify Tasks.

### Why use this Actor?

You get forward and reverse lookup in one input contract, globally capped output, stable field names, ranked forward candidates, raw OSM-derived properties, retry handling, and Apify dataset export to JSON, CSV, Excel, APIs, webhooks, and integrations.

The Actor only charges matched features with numeric coordinates. Invalid input or an exhausted upstream request fails clearly instead of returning a misleading successful empty run.

### Getting started

1. Open the Actor input.
2. Add at least one string under `queries` or one latitude/longitude object under `coordinates`.
3. Choose the number of ranked forward matches with `resultsPerQuery`.
4. Optionally set a language, bounding box, or location bias.
5. Set `maxItems` to cap the complete dataset.
6. Start the run and open **Geocoding matches** in the Output tab.

### Input parameters

| Field | Type | Default | Meaning |
| --- | --- | ---: | --- |
| `queries` | string array | — | Place names or addresses for forward geocoding. |
| `coordinates` | object array | — | `{ latitude, longitude }` points for reverse geocoding. |
| `resultsPerQuery` | integer | `5` | Ranked matches requested per forward query, from 1 to 20. |
| `language` | string | — | Optional two-letter preferred language such as `en`, `de`, or `fr`. |
| `boundingBox` | number array | — | Optional `[minLon, minLat, maxLon, maxLat]` restriction for forward queries. |
| `locationBias` | object | — | Optional latitude, longitude, and zoom used to favor nearby forward matches. |
| `maxItems` | integer | `100` | Global output cap, from 1 to 5,000 rows. |

A run accepts at most 500 combined query and coordinate inputs.

### Forward geocoding example

```json
{
  "queries": [
    "Brandenburg Gate, Berlin",
    "Eiffel Tower, Paris"
  ],
  "resultsPerQuery": 3,
  "language": "en",
  "maxItems": 6
}
```

Forward matches retain a one-based `rank` for each original query. Use `inputIndex` and `query` to join candidates back to your source rows.

### Reverse geocoding example

```json
{
  "coordinates": [
    { "latitude": 52.516275, "longitude": 13.377704 },
    { "latitude": 48.85837, "longitude": 2.294481 }
  ],
  "language": "en",
  "maxItems": 2
}
```

Reverse rows include `requestedLatitude` and `requestedLongitude` alongside Photon’s matched point.

### Output fields

Each default-dataset row can include:

- lookup context: `mode`, `inputIndex`, `query`, requested coordinates, and `rank`;
- normalized geography: `label`, `latitude`, `longitude`, `geometryType`, and `placeType`;
- address components: `name`, `houseNumber`, `street`, `locality`, `district`, `city`, `county`, `state`, `postcode`, `country`, and `countryCode`;
- source identity: `osmType`, `osmId`, `osmKey`, and `osmValue`;
- advanced fields: `extent`, complete `properties`, `sourceUrl`, and `retrievedAt`.

Photon and OpenStreetMap coverage varies, so address and administrative fields are nullable.

### Output example

```json
{
  "mode": "forward",
  "inputIndex": 0,
  "query": "Brandenburg Gate, Berlin",
  "requestedLatitude": null,
  "requestedLongitude": null,
  "rank": 1,
  "matchStatus": "matched",
  "label": "Brandenburger Tor, Pariser Platz, Berlin, 10117, Deutschland",
  "latitude": 52.5162699,
  "longitude": 13.3777034,
  "geometryType": "Point",
  "placeType": "house",
  "countryCode": "DE",
  "osmType": "W",
  "osmId": 518071791,
  "osmKey": "tourism",
  "osmValue": "attraction",
  "retrievedAt": "2025-01-15T12:00:00.000Z"
}
```

### How much does it cost to geocode Photon locations?

Pay-per-event pricing has a **$0.00005 start fee** and charges each valid matched geocoding record. The current BRONZE record price is **$0.002936**; other subscription tiers use the prices shown on the Actor page.

Example BRONZE charges:

| Matched records | Estimated charge |
| ---: | ---: |
| 1 | $0.002986 |
| 10 | $0.02941 |
| 100 | $0.29365 |
| 1,000 | $2.93605 |

No-match inputs do not create or charge dataset records. Your final bill also follows Apify’s displayed pricing and platform rounding.

### Batch enrichment workflow

Keep a stable input order in your source table. Send addresses in `queries` and field coordinates in `coordinates`, run the Actor, then join output using `inputIndex`. For ambiguous forward inputs, keep multiple ranks and apply your own country, place-type, or distance rule downstream.

For recurring jobs, save the input as an Apify Task and schedule it. Each run creates a fresh default dataset; the Actor does not maintain history or claim to detect changes itself.

### Bounding and ranking tips

- Use a bounding box when results must fall inside a strict operational area.
- Use location bias when nearby results are preferred but results outside the center remain acceptable.
- Ask for several forward matches when names are ambiguous.
- Include city, region, postcode, or country in address strings for better precision.
- Keep `maxItems` deliberately low during workflow development.

### Reliability and limits

Photon is an upstream public service. Availability, ranking, language coverage, OSM freshness, and field completeness are controlled by Photon and OpenStreetMap.

Requests use a 20-second timeout and up to three bounded attempts for network errors, rate limits, and temporary server errors. Permanent 4xx responses are not retried. Requests run with conservative concurrency; the Actor has no automatic residential proxy or browser fallback.

The Actor returns ranked candidates, not an assertion that every candidate is the intended real-world entity. Validate high-impact results before using them for routing, safety, finance, or legal decisions.

### Export and integrations

Open the default dataset to download JSON, CSV, XML, RSS, Excel, or HTML. You can also:

- send finished-run webhooks to a data pipeline;
- connect Apify datasets to Make, Zapier, Google Sheets, or your warehouse;
- call the dataset API from an ETL job;
- schedule a saved Task for recurring enrichment;
- use MCP to run the Actor from an AI-assisted workflow.

### API usage with cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~photon-forward-reverse-geocoding/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"queries":["Brandenburg Gate, Berlin"],"resultsPerQuery":3,"maxItems":3}'
```

Use `run-sync-get-dataset-items` instead of `runs` when a small request should wait for and return dataset rows directly.

### API usage with JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/photon-forward-reverse-geocoding').call({
  queries: ['Eiffel Tower, Paris'],
  resultsPerQuery: 3,
  maxItems: 3,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### API usage with Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("automation-lab/photon-forward-reverse-geocoding").call(
    run_input={
        "coordinates": [{"latitude": 52.516275, "longitude": 13.377704}],
        "maxItems": 1,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with MCP

Add this Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/photon-forward-reverse-geocoding"
```

Claude Desktop, Cursor, and VS Code setup uses the same HTTP MCP server configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/photon-forward-reverse-geocoding"
    }
  }
}
```

Example prompts:

- “Forward-geocode these store addresses and return the top three candidates for each.”
- “Reverse-geocode these GPS points and group the results by country code.”
- “Run Photon geocoding, then export latitude, longitude, OSM ID, and normalized label.”

### Legality and responsible use

Photon serves data derived from OpenStreetMap. Follow Photon’s usage guidance and OpenStreetMap’s attribution and license requirements for your application and redistributed data. Do not submit personal or sensitive location data unless you have a lawful basis and appropriate safeguards.

This Actor does not bypass access controls, use private accounts, or enrich people with non-public data.

### Troubleshooting

**The dataset is empty.** Check logs for the completed lookup count. A well-formed query can legitimately have no Photon match; try a more specific address, a different language, or remove an overly strict bounding box.

**The run fails with an input error.** Provide at least one query or coordinate pair. Latitude must be -90 to 90, longitude -180 to 180, and bounding-box minimums must be lower than maximums.

**Photon returns a temporary error.** The Actor already retries transient failures. Retry later rather than increasing concurrency or launching many identical runs.

**The first candidate is wrong.** Increase `resultsPerQuery`, add region/country context, use a bounding box, or apply location bias.

### FAQ

#### Does this Actor require a Photon API key?

No. It uses Photon’s anonymous public JSON endpoints.

#### Does it support forward and reverse geocoding in one run?

Yes. Supply both `queries` and `coordinates`; output identifies each mode and input position.

#### Does it return raw GeoJSON?

It returns normalized flat rows plus geometry fields, extent, and the complete Photon `properties` object. It does not copy the outer FeatureCollection wrapper.

#### Are unmatched inputs charged?

No. Only valid matched features saved to the dataset trigger the `geocoding-record` event.

#### Can it monitor location changes?

You can schedule recurring Tasks and compare their datasets downstream. The Actor itself does not store history, generate diffs, or send change alerts.

### Related Automation Lab Actors

- [Apple Maps Business Listings Scraper](https://apify.com/automation-lab/apple-maps-business-listings-scraper) for Apple Maps business identity, contact, and listing fields.
- [Open Food Facts EAN UPC GTIN Lookup](https://apify.com/automation-lab/ean-upc-gtin-product-lookup) for barcode-based product enrichment rather than geographic enrichment.

Choose Photon Geocoding Scraper when the central job is converting place/address text and coordinates into OSM-derived geographic candidates.

# Actor input Schema

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

Place names or full/partial addresses to convert into ranked geographic matches (maximum 500 total inputs per run).

## `coordinates` (type: `array`):

Latitude/longitude pairs to convert into the nearest Photon place or address.

## `resultsPerQuery` (type: `integer`):

Maximum ranked Photon features requested for each forward query. Reverse lookups return the nearest feature.

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

Optional two-letter language code for localized labels, such as en, de, or fr.

## `boundingBox` (type: `array`):

Optional \[minimum longitude, minimum latitude, maximum longitude, maximum latitude] box that restricts forward matches.

## `locationBias` (type: `object`):

Optional map center and zoom used to rank forward matches nearer a location without restricting results.

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

Global cap on matched features saved across all forward and reverse lookups.

## Actor input object example

```json
{
  "queries": [
    "Brandenburg Gate, Berlin",
    "Eiffel Tower, Paris"
  ],
  "coordinates": [
    {
      "latitude": 52.516275,
      "longitude": 13.377704
    }
  ],
  "resultsPerQuery": 5,
  "language": "en",
  "maxItems": 20
}
```

# Actor output Schema

## `dataset` (type: `string`):

Default dataset containing matched Photon features.

# 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": [
        "Brandenburg Gate, Berlin",
        "Eiffel Tower, Paris"
    ],
    "coordinates": [
        {
            "latitude": 52.516275,
            "longitude": 13.377704
        }
    ],
    "resultsPerQuery": 5,
    "language": "en",
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/photon-forward-reverse-geocoding").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": [
        "Brandenburg Gate, Berlin",
        "Eiffel Tower, Paris",
    ],
    "coordinates": [{
            "latitude": 52.516275,
            "longitude": 13.377704,
        }],
    "resultsPerQuery": 5,
    "language": "en",
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/photon-forward-reverse-geocoding").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": [
    "Brandenburg Gate, Berlin",
    "Eiffel Tower, Paris"
  ],
  "coordinates": [
    {
      "latitude": 52.516275,
      "longitude": 13.377704
    }
  ],
  "resultsPerQuery": 5,
  "language": "en",
  "maxItems": 20
}' |
apify call automation-lab/photon-forward-reverse-geocoding --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/photon-forward-reverse-geocoding"
        }
    }
}
```

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/MFMK3UtPD6rhjnKpu/builds/D4uPkpnJlIy0py5ua/openapi.json
