Add distance and ETA functions with Maps
Fill drive times and distances between two cells inline using the Maps service.
Published Jul 9, 2025
Northwind delivers studio kits to clients across the country, and the logistics sheet needs a drive time and distance for every job. Looking each one up in a maps app and typing the answer back in is tedious, and the numbers go stale the moment a route plan changes.
These two custom functions put the lookup in the cell. =DRIVE_TIME(A2, B2)
returns the driving ETA between two addresses, and =DRIVE_DISTANCE_KM(A2, B2)
returns the distance in kilometres. Both share a cached route lookup, so a sheet
full of formulas does not hammer the Maps service.
What you’ll need
- A Google Sheet with an origin and a destination in their own columns. Each can be a postcode, a full address or a place name.
- Nothing else — the Maps service and
CacheServiceare built into Apps Script, no API key required.
The functions
// How long a route lookup stays cached, in seconds (6 hours).
const ROUTE_CACHE_TTL = 6 * 60 * 60;
/**
* Custom function: driving ETA between two locations.
* Use it in a cell, e.g. =DRIVE_TIME("London", "Manchester")
*
* @param {string} from Origin address, postcode or place name.
* @param {string} to Destination address, postcode or place name.
* @return {string} A human-readable duration, or '' if not found.
* @customfunction
*/
function DRIVE_TIME(from, to) {
// Bail out on empty inputs so the Maps service is never called for nothing.
if (!from || !to) return '';
const route = lookupRoute(from, to);
return route ? route.duration.text : '';
}
/**
* Custom function: driving distance between two locations, in kilometres.
* Use it in a cell, e.g. =DRIVE_DISTANCE_KM("London", "Manchester")
*
* @param {string} from Origin address, postcode or place name.
* @param {string} to Destination address, postcode or place name.
* @return {number|string} Distance in km, or '' if not found.
* @customfunction
*/
function DRIVE_DISTANCE_KM(from, to) {
if (!from || !to) return '';
const route = lookupRoute(from, to);
// Maps returns distance in metres — convert to km.
return route ? route.distance.value / 1000 : '';
}
/**
* Looks up the first driving route between two points and returns its
* leg. Results are cached so repeated formulas reuse one Maps call.
*
* @param {string} from Origin.
* @param {string} to Destination.
* @return {Object|null} The route leg, or null if no route exists.
*/
function lookupRoute(from, to) {
// 1. Check the cache first — a hit means no Maps call at all.
const cache = CacheService.getScriptCache();
const key = `route:${from}->${to}`;
const hit = cache.get(key);
if (hit) return JSON.parse(hit);
// 2. Cache miss — ask the Maps service for driving directions.
const directions = Maps.newDirectionFinder()
.setOrigin(from)
.setDestination(to)
.setMode(Maps.DirectionFinder.Mode.DRIVING)
.getDirections();
// 3. No route found (bad address, no road link) — give up cleanly.
if (!directions.routes.length) return null;
// 4. Cache the first leg for later formulas, then return it.
const leg = directions.routes[0].legs[0];
cache.put(key, JSON.stringify(leg), ROUTE_CACHE_TTL);
return leg;
}
How it works
DRIVE_TIMEandDRIVE_DISTANCE_KMare custom functions — the@customfunctiontag makes each callable straight from a cell.- Both check their two arguments are present first. An empty origin or destination returns a blank cell instead of a wasted lookup.
- Both delegate to
lookupRoute, which is where the real work — and the caching — happens. lookupRoutebuilds a cache key from the origin and destination and checksCacheServicefirst. On a hit it returns the stored route leg without touching the Maps service at all.- On a miss it calls
Maps.newDirectionFinderfor a driving route. If no route comes back it returnsnull, and the caller shows an empty cell. - A successful lookup is cached for six hours (
ROUTE_CACHE_TTL), so a column of formulas — or a recalculation — reuses one Maps call per unique pair. DRIVE_TIMEreturns the human-readableduration.text;DRIVE_DISTANCE_KMtakesdistance.valuein metres and divides by 1000.
Example run
With origins and destinations in columns A and B and the formulas in C and D:
| From | To | =DRIVE_TIME(A,B) | =DRIVE_DISTANCE_KM(A,B) |
|---|---|---|---|
| Northwind HQ, London | Manchester | 4 hours 12 mins | 335.6 |
| Northwind HQ, London | Brighton | 1 hour 38 mins | 86.4 |
| Northwind HQ, London | (blank) |
The third row is blank because the destination cell is empty — the guard skips the lookup.
Use them
There is nothing to schedule — these are custom functions:
- Paste the script into the spreadsheet’s bound Apps Script project and save.
- Back in the sheet, reference your origin and destination cells:
=DRIVE_TIME("Northwind HQ, London", B2)
=DRIVE_DISTANCE_KM("Northwind HQ, London", B2)
- Approve the authorisation prompt the first time a function runs.
Watch out for
- The Maps service has a daily directions quota. A cache miss costs one lookup; the six-hour cache absorbs repeats, but a sheet full of unique routes recalculated often can still hit the limit.
- Custom functions run with a 30-second limit. A slow cluster of fresh lookups in one recalculation can time cells out — let the cache warm up, or paste results as static values once correct.
CacheServiceentries are capped at 100 KB each and can be evicted under pressure. A route leg is small, so this is rarely an issue, but do not treat the cache as permanent storage.- A cached route does not update for live traffic or roadworks. The six-hour
TTL keeps numbers reasonably fresh; shorten
ROUTE_CACHE_TTLif you need tighter accuracy. - Vague inputs give vague routes. “Manchester” picks a city centre point — use full addresses or postcodes when the exact start and end matter.
Related
Parse messy mixed-format dates
Normalise inconsistently formatted strings into real date values with a single formula.
Updated Aug 2, 2025
Scrape a web table into cells with one formula
Pull HTML tables into Sheets as a custom function — no IMPORTHTML quirks.
Updated Jul 30, 2025
Mask sensitive columns for shareable copies
Redact PII with a custom function so you can share a copy of the sheet without exposing names, emails, or numbers.
Updated Jul 26, 2025
Build a sentiment-scoring function without AI
Rate text positive or negative with a tiny built-in lexicon — no API key, no quota.
Updated Jul 23, 2025
Build a unit-conversion function library
Convert between any units with one custom formula — kg/lb, km/mi, °C/°F, and the rest.
Updated Jul 19, 2025