# Google Maps Directions Scraper (`romy/google-maps-directions-scraper`) Actor

Get driving/walking/transit routes from Google Maps. Talks directly to Google Maps' internal mobile API, no login needed. Split from the mature, published google-maps-all-in-one-api for a focused, single-purpose workflow.

- **URL**: https://apify.com/romy/google-maps-directions-scraper.md
- **Developed by:** [Romy](https://apify.com/romy) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 route chargeds

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

### What does Google Maps Directions Scraper do?

**Google Maps Directions Scraper** fetches routes (driving, walking, transit, motorcycle) to one or more destinations and pushes one dataset row per route — duration, distance, tolls/advisories, and, for DRIVE/WALK/MOTORCYCLE, a `linestring` of `[lng, lat]` points; TRANSIT routes instead return per-leg detail (line, stops, schedule).

It talks directly to the same internal gRPC API the official Google Maps Android app uses (`mobilemaps-pa-gz.googleapis.com`, `MobileMapsDirectionsService/GetDirections`), reverse-engineered by live capture against a real device. No Google account, no API key, no browser automation. This is the `/directions` slice of [Google Maps API](https://apify.com/romy/google-maps-all-in-one-api) (the "all-in-one" Standby REST API), split out as its own focused, batch-run Actor.

### Why use Google Maps Directions Scraper?

- **Real, live route data** — actual server-computed durations, distances, tolls/advisories, and turn-by-turn/transit detail, not a scrape of the maps.google.com web page
- **All four travel modes** — DRIVE, WALK, TRANSIT, MOTORCYCLE, each parsed into a typed shape
- **Batch input** — pass an array of destinations in one run, get one dataset row per resulting route
- **No account needed** — every request works fully anonymously
- **Use cases:** trip-planning tools, delivery/logistics ETA checks, transit-time comparison across candidate locations, real-estate "commute time" enrichment

### Known limitations

- **The origin is fixed and NOT configurable.** Every request starts from a stub location baked into the captured request template that this Actor replays — only the destination is configurable. This is a limitation inherited directly from the parent Actor's own documented note: *"Directions always starts from a fixed stub location baked into the request template — only the destination is configurable. A proper origin parameter would need a wider capture of the request shape."* In practice this means the routes returned are **not** true point-A-to-point-B directions between two addresses you choose — treat the output as "routes from the fixed stub origin to your destination," not a general-purpose router. Confirmed live: the baked-in origin resolves to a real-world point in the Jakarta, Indonesia area — DRIVE/WALK/MOTORCYCLE requests for a destination outside that region (e.g. another continent) will correctly come back with zero routes for that mode, since no drivable/walkable route exists; this is expected, not a bug.
- **Route parsing is heuristic**, based on empirically reverse-engineered `.proto` field numbers (no official schema exists from Google). A handful of fields (e.g. `durationTextRaw`) are typed as raw bytes rather than validated UTF-8 strings for this reason, and travel-mode inference for ambiguous alternatives falls back to a heuristic (duration/via-text based) when a mode isn't explicitly forced.
- **Coordinate extraction** (`linestring`) only covers DRIVE/WALK/MOTORCYCLE routes and only the coordinates present in walk-step detail — it is not a dense, full-resolution polyline of the entire route.
- Only DRIVE, WALK, TRANSIT, and MOTORCYCLE modes are supported (the modes the reverse-engineered request templates cover).

### Input

| Field                          | Type     | Description                                                                           |
| ------------------------------ | -------- | ------------------------------------------------------------------------------------- |
| `destinations`                 | array    | Required, non-empty. One entry per destination to fetch routes to.                    |
| `destinations[].destName`      | string   | Required. Human-readable destination name.                                            |
| `destinations[].destFeatureId` | string   | Required. Google place feature id, e.g. from a search/geocode result (`0x...:0x...`). |
| `destinations[].destLat`       | number   | Required. Destination latitude.                                                       |
| `destinations[].destLng`       | number   | Required. Destination longitude.                                                      |
| `destinations[].modes`         | string\[] | Optional. Any of `DRIVE`, `WALK`, `TRANSIT`, `MOTORCYCLE`. Default `["DRIVE"]`.       |
| `destinations[].maxRoutes`     | integer  | Optional. Max routes returned per requested mode. Default `3`.                        |

**Origin is not part of the input schema — it cannot be set.** See Known limitations above.

Example:

```json
{
    "destinations": [
        {
            "destName": "Grand Indonesia",
            "destFeatureId": "0x2e69f421c2ebd463:0xccfcc89b95aaf1ae",
            "destLat": -6.1953681,
            "destLng": 106.8204181,
            "modes": ["DRIVE", "WALK"],
            "maxRoutes": 2
        }
    ]
}
```

### Output

One dataset row per route:

```json
{
    "destName": "Grand Indonesia",
    "destFeatureId": "0x2e69f421c2ebd463:0xccfcc89b95aaf1ae",
    "travelMode": "DRIVE",
    "routeIndex": 0,
    "via": "I-95 N",
    "durationText": "1 hr 4 min",
    "durationS": 3849,
    "distanceM": 122000,
    "hasTolls": true,
    "isRecommended": true,
    "linestring": [
        [-77.5, 39.1],
        [-77.4, 39.05]
    ]
}
```

TRANSIT routes instead carry a `legs` array (per-leg line/stop/schedule detail) rather than a single `linestring`.

### Pricing

Pay per event, via the `route` event — charged once per route pushed to the dataset (one event per dataset row, regardless of how many destinations/modes you request), starting at $0.001 (FREE tier). See the Actor's Pricing tab for current rates.

# Actor input Schema

## `destinations` (type: `array`):

One entry per destination to fetch routes to. Origin is NOT configurable — it is fixed to a stub location baked into the captured request template (see the actor's Known limitations). The origin is in the Jakarta area, so destinations must be reachable from there (a destination on another continent returns no routes). Each entry: destName (required), destFeatureId (required, Google place feature id, e.g. from a search/geocode result), destLat/destLng (required), modes (optional array of "DRIVE"|"WALK"|"TRANSIT"|"MOTORCYCLE", default \["DRIVE"]), maxRoutes (optional, default 3).

## Actor input object example

```json
{
  "destinations": [
    {
      "destName": "Grand Indonesia",
      "destFeatureId": "0x2e69f421c2ebd463:0xccfcc89b95aaf1ae",
      "destLat": -6.1953681,
      "destLng": 106.8204181,
      "modes": [
        "DRIVE",
        "WALK"
      ]
    }
  ]
}
```

# Actor output Schema

## `dataset` (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 = {
    "destinations": [
        {
            "destName": "Grand Indonesia",
            "destFeatureId": "0x2e69f421c2ebd463:0xccfcc89b95aaf1ae",
            "destLat": -6.1953681,
            "destLng": 106.8204181,
            "modes": [
                "DRIVE",
                "WALK"
            ]
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("romy/google-maps-directions-scraper").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 = { "destinations": [{
            "destName": "Grand Indonesia",
            "destFeatureId": "0x2e69f421c2ebd463:0xccfcc89b95aaf1ae",
            "destLat": -6.1953681,
            "destLng": 106.8204181,
            "modes": [
                "DRIVE",
                "WALK",
            ],
        }] }

# Run the Actor and wait for it to finish
run = client.actor("romy/google-maps-directions-scraper").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 '{
  "destinations": [
    {
      "destName": "Grand Indonesia",
      "destFeatureId": "0x2e69f421c2ebd463:0xccfcc89b95aaf1ae",
      "destLat": -6.1953681,
      "destLng": 106.8204181,
      "modes": [
        "DRIVE",
        "WALK"
      ]
    }
  ]
}' |
apify call romy/google-maps-directions-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,romy/google-maps-directions-scraper"
        }
    }
}
```

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/x4880OaP7ximy2NAi/builds/zfGSHifIkK9cY1OkJ/openapi.json
