# OSM Point POI Exporter (`danielmichalek/osm-point-poi-exporter`) Actor

Export bounded OpenStreetMap point POIs from dated Geofabrik snapshots to GeoJSON and CSV. Filter region, exact categories and bounding box; retain source hashes, ODbL attribution and coverage.

- **URL**: https://apify.com/danielmichalek/osm-point-poi-exporter.md
- **Developed by:** [Daniel Michałek](https://apify.com/danielmichalek) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 91.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$50.00 / 1,000 completed poi exports

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?

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

## OSM Point POI Exporter

Export a small, reproducible set of points of interest from an official Geofabrik OpenStreetMap snapshot. Choose a region, optional bounding box and exact categories; receive POI rows, GeoJSON, a spreadsheet-safe CSV and query-level coverage reports.

**Version 0.1.0.** The supported source format has been checked against dated Monaco and Bremen extracts. The coverage and source limitations below apply to every export.

### What this helps with

- Import selected restaurants, hotels, pharmacies or other supported point categories into GIS and map layers.
- Prepare a bounded regional POI list for tourism research or field-work planning.
- Repeat an extraction against a dated snapshot and retain the archive hash needed to identify the exact source bytes.

The source is OpenStreetMap contributors' data distributed by Geofabrik. This format supplies source IDs, a category, a category code, a nullable name and point coordinates. It does not supply phone numbers, email, street addresses, opening hours, reviews or ratings. A point is a mapped feature, not an independently verified business or a complete business-directory entry. Names can be missing or shortened by the source format.

### First three queries

This input selects real categories from three recorded archives: **Monaco on 25 October 2026, Bremen on 25 October 2026, and Bremen on 1 October 2026**. Each query returns at most three matching rows. These are three extraction cases across **two regions in two countries**, using one provider.

```json
{
  "queries": [
    {
      "id": "monaco_restaurants",
      "regionPath": "europe/monaco",
      "snapshotDate": "2026-10-25",
      "categories": ["restaurant"],
      "limit": 3
    },
    {
      "id": "bremen_restaurants",
      "regionPath": "europe/germany/bremen",
      "snapshotDate": "2026-10-25",
      "categories": ["restaurant"],
      "limit": 3
    },
    {
      "id": "bremen_pharmacies",
      "regionPath": "europe/germany/bremen",
      "snapshotDate": "2026-10-01",
      "categories": ["pharmacy"],
      "limit": 3
    }
  ]
}
```

The fixed source URLs for this example are [Monaco 25 October](https://download.geofabrik.de/europe/monaco-261025-free.gpkg.zip), [Bremen 25 October](https://download.geofabrik.de/europe/germany/bremen-261025-free.gpkg.zip) and [Bremen 1 October](https://download.geofabrik.de/europe/germany/bremen-261001-free.gpkg.zip). This example uses three distinct source downloads. Repeated queries against the same region/date share one download within a run. A later run downloads its sources again; there is no promised cross-run cache.

Three successfully delivered, nonempty queries would cost **USD 0.15 in total** at the proposed USD 0.05/query price. That is a charge for three exports, not for each of the up to nine rows. Empty or failed queries are uncharged. Check the Actor's current Pricing tab before running.

### Input reference

| Field | Accepted value |
| --- | --- |
| `queries` | 1–3 query objects; no additional top-level properties |
| `id` | Required unique identifier: 1–32 letters, digits, `_` or `-` |
| `regionPath` | Required supported Geofabrik continent followed by 1–3 lowercase alphanumeric/hyphen path segments, such as `europe/monaco` or `europe/germany/bremen` |
| `snapshotDate` | Optional valid `YYYY-MM-DD`, from 2000 through the current UTC day. Omit for the provider's `latest` file |
| `bbox` | Optional `[west, south, east, north]` in longitude/latitude degrees; finite values, west < east, south < north, longitude −180…180 and latitude −90…90 |
| `categories` | Optional 1–20 unique, exact source `fclass` identifiers, such as `restaurant`, `hotel` or `pharmacy`; lowercase letters, digits and `_`, at most 28 characters each. Omit for all point categories |
| `limit` | Integer 1–1000; default 100, per query |

The allowed continent names are `africa`, `antarctica`, `asia`, `australia-oceania`, `central-america`, `europe`, `north-america` and `south-america`. A syntactically valid path does not guarantee that a compatible, sufficiently small GeoPackage exists. Use the [official download index](https://download.geofabrik.de/) to identify a region. Other regions, including Poland, have not been established by the initial two-region proof.

Bounding-box edges are inclusive. Dateline-wrapping boxes are unsupported; split such work into ordinary non-wrapping boxes within the query limit. Optional fields must be omitted rather than set to `null`. Unknown properties, arbitrary URLs, SQL, API keys and proxy settings are rejected. This version has no radius search, geocoding, comparison mode or automatic schedule.

### Scope and interpretation

Only the Geofabrik **`gis_osm_pois_free` point layer** is read. POIs mapped as polygons are in separate area layers and are excluded; the exporter does not invent entrances or centroids for them. Coverage reports describe the inspected point layer and any known exclusions. An empty export does not prove there are no relevant places in the real world.

Supported geometry is an OGC CRS84 Point in **longitude, latitude** order. Other geometry or coordinate-reference metadata is rejected rather than guessed. The numeric SRS identifier in a GeoPackage can be local to that file; it must not be treated as an EPSG code without its definition.

`sourceRowId` is the source `fid`, identifying a row within one exact snapshot and layer. `osmId` is preserved as a string and **is not guaranteed unique**. Multiple rows or categories for one OSM ID are retained. Do not collapse them into one business or infer a node/way URL from the numeric ID. Row IDs can change between snapshots. Version 0.1.0 does not infer openings, closures, relocations or verified business-name changes.

`matchedCount` counts the matches in the inspected source before the requested limit. `returnedCount` is the number exported, ordered by source row ID. A successful query with `truncated: true` deliberately returns the first `limit` matches and is still eligible for its export fee. It is not a complete geographic inventory.

Source evidence records the requested URL, fetch time, archive SHA256, HTTP `Last-Modified`, requested snapshot date, attribution and licence. `Last-Modified` describes the downloaded file, not an individual OSM edit or business freshness. For `latest`, `requestedSnapshotDate` is `null`; the exporter does not fabricate a snapshot date. A dated URL plus an archive hash identifies the observed source; it does not guarantee that a provider will retain that file forever.

### Outputs

| Artifact | Contents |
| --- | --- |
| Default dataset | POI rows only, each carrying its `queryId` |
| `RESULTS` | Structured reports for attempted queries, with status, completion, counts, truncation, source, coverage, diagnostics and records |
| `GEOJSON` | A real GeoJSON `FeatureCollection` containing the delivered Point features and provenance properties |
| `SUMMARY_CSV` | Rectangular POI export with identifiers, names, coordinates and source/licence columns |
| `OUTPUT` | Overall status, requested/processed coverage, source usage and billing confirmation |

Each dataset row contains `queryId`, `sourceRowId`, `osmId`, `category`, `categoryCode`, `name`, `longitude`, `latitude`, `geometry` and `source`. A GeoJSON feature places the Point in `geometry` and the remaining fields in `properties`. Top-level attribution, licence and scope are retained in the report and GeoJSON outputs.

CSV columns are `queryId`, `sourceRowId`, `osmId`, `category`, `categoryCode`, `name`, `longitude`, `latitude`, `sourceUrl`, `requestedSnapshotDate`, `fetchedAt`, `archiveSha256`, `httpLastModified`, `attribution`, `license` and `licenseUrl`. IDs receive a leading apostrophe to preserve text in spreadsheets, and formula-leading text is neutralized. Some CSV readers display the apostrophe. JSON preserves the source values and is preferable for programmatic use.

Read `OUTPUT` and `RESULTS` together. A complete empty query has `status: "empty"`, `completed: true`, no POI rows and no export fee. A source or processing error has a diagnostic and is uncharged; it does not yield a partially complete result disguised as success. An empty dataset by itself does not distinguish these situations.

### Actual recorded source examples

These values were read from saved official snapshots during source research. They are **selected source excerpts, not Actor output or a promise about the first three rows returned by the example queries**.

| Snapshot | Source row ID | OSM ID | Category | Source name | Longitude, latitude |
| --- | ---: | --- | --- | --- | --- |
| Monaco, 2026-10-25 | 315 | `5670111421` | `restaurant` | 3 Tapas | 7.4195761, 43.7337808 |
| Bremen, 2026-10-25 | 1092 | `456414303` | `restaurant` | "Guter Freund" China Food | 8.5748782, 53.54739 |
| Bremen, 2026-10-25 | 3943 | `2071844324` | `pharmacy` | Adler-Apotheke | 8.8015746, 53.0788613 |

Research inspected four dated archives: 1 and 25 October 2026 for each of these two regions. It did not establish three-country coverage. The local project includes the three-query input, recorded source provenance and labelled research excerpts under `examples/`; these files are not promised as Store downloads when source code is hidden.

### Pricing and run controls

The intended sole PPE event is **`poi-export-completed`, USD 0.05 per successfully delivered nonempty bounded query**, with platform usage included when that pricing is active. The Pricing tab is authoritative. A valid capped export is one paid unit; returning fewer rows does not reduce that unit price. Empty, invalid, failed and cancelled queries are uncharged. No positive startup, default-row or auxiliary fee is intended.

The spending limit is checked before source work. A zero effective event budget performs no source GET. Only the initially affordable prefix of up to three queries is attempted; an empty or failed query does not expand that prefix. Repeating a request in a separate run can create another export and another charge even if the source is unchanged.

Output artifacts are written before a named charge. Until confirmation completes, `OUTPUT` can show pending billing. An ambiguous write or charge blocks automatic replay: inspect the existing run and its confirmation before starting another one. Do not treat a file appearing in storage as proof of a confirmed charge or complete run.

For operator planning, 1,000 eligible paid exports at USD 0.05 would generate USD 50 gross and USD 40 after a 20% platform share. An assumed USD 8–20 in resource costs would leave USD 20–32 before development, support, sales and taxes. **This is a scenario, not measured margin or actual revenue.** Free-plan users do not generate creator revenue. The recorded Bremen ZIP is about 32.5 MB even when the requested output is only three points; a small result does not imply a small source download.

### Resource and source limits

| Bound | Limit |
| --- | --- |
| Queries / distinct sources | 3 / 3 per run; sequential processing |
| Downloaded ZIP | 64 MiB per source; 128 MiB aggregate received |
| Expanded GeoPackage | 256 MiB per source |
| Source table | At most 500,000 rows |
| Output | At most 1,000 POIs per query |
| Source request | 15-second timeout; at most 2 attempts per source within the shared budget |
| Application budget | 120 seconds; platform startup and final storage/delivery can add time |
| Initial runtime allocation | 512 MB; default platform timeout 150 seconds |

The source client constructs HTTPS URLs on `download.geofabrik.de` only, rejects redirects and validates ZIP/database bounds. Files with unsupported structure, unsafe archive entries, multiple candidate databases, unsupported geometry or excessive size are rejected. HTTP 401/403 stop source access without bypass. Retry/backoff for rate limits is bounded by the remaining deadline; a dated source that is absent returns an explicit error. The service does not depend on a public Overpass instance or promise uninterrupted source availability.

### Attribution and licence

**© OpenStreetMap contributors. Data supplied through Geofabrik. Open Database License (ODbL) 1.0.** Retain attribution and the licence link when using or redistributing the data. ODbL includes share-alike and access requirements for databases under the conditions set out in the licence; the data is not public domain. Exporting a small subset does not remove the licence obligations.

See [OpenStreetMap copyright and attribution](https://www.openstreetmap.org/copyright), the [ODbL 1.0 licence](https://opendatacommons.org/licenses/odbl/1-0/), and [Geofabrik's free GIS format documentation](https://download.geofabrik.de/osm-data-in-gis-formats-free.pdf). Keep source hashes and notices with derived exports. These records remain community-maintained source facts, not official business-register or contact-verification results.

# Actor input Schema

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

One to three queries with distinct IDs. Point POIs only, no area conversion, addresses, phones or live business verification. The prefill uses three actual recorded Monaco/Bremen archives from 2026-10-25 and 2026-10-01, limiting each query to three rows.

## Actor input object example

```json
{
  "queries": [
    {
      "id": "monaco_restaurants",
      "regionPath": "europe/monaco",
      "snapshotDate": "2026-10-25",
      "categories": [
        "restaurant"
      ],
      "limit": 3
    },
    {
      "id": "bremen_restaurants",
      "regionPath": "europe/germany/bremen",
      "snapshotDate": "2026-10-25",
      "categories": [
        "restaurant"
      ],
      "limit": 3
    },
    {
      "id": "bremen_pharmacies",
      "regionPath": "europe/germany/bremen",
      "snapshotDate": "2026-10-01",
      "categories": [
        "pharmacy"
      ],
      "limit": 3
    }
  ]
}
```

# Actor output Schema

## `pois` (type: `string`):

No description

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

No description

## `geojson` (type: `string`):

No description

## `csv` (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": [
        {
            "id": "monaco_restaurants",
            "regionPath": "europe/monaco",
            "snapshotDate": "2026-10-25",
            "categories": [
                "restaurant"
            ],
            "limit": 3
        },
        {
            "id": "bremen_restaurants",
            "regionPath": "europe/germany/bremen",
            "snapshotDate": "2026-10-25",
            "categories": [
                "restaurant"
            ],
            "limit": 3
        },
        {
            "id": "bremen_pharmacies",
            "regionPath": "europe/germany/bremen",
            "snapshotDate": "2026-10-01",
            "categories": [
                "pharmacy"
            ],
            "limit": 3
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("danielmichalek/osm-point-poi-exporter").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": [
        {
            "id": "monaco_restaurants",
            "regionPath": "europe/monaco",
            "snapshotDate": "2026-10-25",
            "categories": ["restaurant"],
            "limit": 3,
        },
        {
            "id": "bremen_restaurants",
            "regionPath": "europe/germany/bremen",
            "snapshotDate": "2026-10-25",
            "categories": ["restaurant"],
            "limit": 3,
        },
        {
            "id": "bremen_pharmacies",
            "regionPath": "europe/germany/bremen",
            "snapshotDate": "2026-10-01",
            "categories": ["pharmacy"],
            "limit": 3,
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("danielmichalek/osm-point-poi-exporter").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": [
    {
      "id": "monaco_restaurants",
      "regionPath": "europe/monaco",
      "snapshotDate": "2026-10-25",
      "categories": [
        "restaurant"
      ],
      "limit": 3
    },
    {
      "id": "bremen_restaurants",
      "regionPath": "europe/germany/bremen",
      "snapshotDate": "2026-10-25",
      "categories": [
        "restaurant"
      ],
      "limit": 3
    },
    {
      "id": "bremen_pharmacies",
      "regionPath": "europe/germany/bremen",
      "snapshotDate": "2026-10-01",
      "categories": [
        "pharmacy"
      ],
      "limit": 3
    }
  ]
}' |
apify call danielmichalek/osm-point-poi-exporter --silent --output-dataset

```

## MCP server setup

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

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/N4YSxAiBcc8AkZQpb/builds/S7uthfF8rlcib0II3/openapi.json
