Terrain Grid API
Access & limitsPublic, no key needed · Heavy▾
Open to everyone. A free API key from your profile raises the limits: send it in an X-API-Key header, or as api_key= in the address.
| 5 minutes | Hour | Day | |
|---|---|---|---|
| Without a key | 20 | 120 | 500 |
| With a key | 60 | 400 | 2,000 |
Each 100 square km of area counts as one request, so a 10 km square is 1 and a 30 km square is 9.
Log in to create a free key on your profile page.
Every answer carries X-RateLimit-Remaining and X-RateLimit-Reset. Over a limit, the answer is error 429 with retry_after in seconds. Refused requests do not count.
Every ground height over an area in one request, from the same terrain store as the Terrain Elevation API and giving the same heights. Use it for maps, 3D models and path studies rather than asking for points one at a time.
lat lon size_kmParameters
Give the area one of two ways: its middle and size, or its edges.
| Parameter | Meaning |
|---|---|
lat, lon | The middle of the area, decimal degrees (lng also accepted) |
size_km | A square area this many km on each side, up to 100 |
width_km, height_km | Instead of size_km: a rectangle, east to west and north to south, each up to 100 km |
north, south, west, east | Instead of the middle and size: the area's edges in decimal degrees, up to 100 km each way. A box across 180 degrees can have east less than west. |
spacing_m | Optional. Metres between points, 30 to 5000. Left out, the finest spacing that keeps the grid to 1,000,000 points: 30 m for areas up to about 30 km across, about 100 m for 100 km. |
format | Optional. json (the default) or asc, an ESRI ASCII grid that GIS software such as QGIS opens directly |
encoding | Optional, for JSON. base64 (the default): the heights as packed binary values, compact and quick to decode, packed as pack says. array: the heights as nested lists of numbers to 0.1 m, easy to read but several times the size. |
pack | Optional, for base64. How each height is packed: uint16 (the default), int16 or float32. See Packing below. |
Packing
With the default base64 encoding the heights come as one block of binary values, little endian, one value per point. Choose the packing that suits the area:
pack | Bytes per height | Height in metres | Resolution | Range | 1,000,000 points |
|---|---|---|---|---|---|
uint16 (default) | 2 | value * 0.1 - 100 | 0.1 m | -100 m to 6,453.5 m | about 2.7 MB |
int16 | 2 | value | 1 m | every height on Earth | about 2.7 MB |
float32 | 4 | value | full precision | every height on Earth | about 5.3 MB |
The default suits nearly everywhere people fly. A height outside its range, such as the shore of the Dead Sea (about 430 m below sea level) or a summit above 6,453 m, is set to the nearest limit and counted in pack.clamped, with a note_clamped saying so; ask for int16 or float32 there. The answer always says how it is packed in pack, so code can read scale and offset from it rather than assume them.
A grid can hold at most 1,000,000 points. The finest spacing is 30 m, the resolution of the best terrain the store holds; where the store is coarser, closer points simply interpolate between its values.
Examples
https://www.altimetercloud.com/api/terrain_grid/?lat=54.45&lon=-3.05&size_km=10&spacing_m=100
https://www.altimetercloud.com/api/terrain_grid/?north=54.6&south=54.4&west=-3.2&east=-2.9&spacing_m=250&encoding=array
https://www.altimetercloud.com/api/terrain_grid/?lat=54.45&lon=-2.95&size_km=4&spacing_m=100&format=asc
How the answer is laid out
The grid is regular in latitude and longitude. Row 0 is the north edge and column 0 the west edge; rows run south and columns run east, and the point in row j, column i is at latitude north - j * lat_step and longitude west + i * lon_step. The steps are the spacing asked for, in metres, at the middle of the area.
{
"success": true,
"rows", how many rows, north to south
"cols", "points_per_row", how many points in each row, west to east (the same number)
"points", rows * cols
"north", "south", "west", "east", the first and last rows and columns, degrees
"lat_step", "lon_step", degrees between rows and between columns
"spacing_m", "width_km", "height_km",
"min_m", "max_m", lowest and highest height in the grid
"tiers": { "ultra", "fine", "medium", "coarse", "none" }, how many points came from each resolution
"encoding",
"pack": { "type", "bytes_per_value", "scale", "offset", "formula",
"resolution_m", "byte_order", "clamped" }, encoding=base64
"heights_b64", encoding=base64: rows * cols packed values, row 0 first
"note_clamped", only when some heights were outside the packing's range
"heights": [ [ ... ], [ ... ] ], encoding=array: one list per row, north first, to 0.1 m
"order", "datum", "units", "no_data", "seconds",
"credits": { "terrain", "full_statements", "attribution", "source" }
}
Heights are metres above mean sea level (EGM96). Sea, and anywhere the store has no data, is 0, and those points are counted in tiers.none. The height of row j, column i is value number j * cols + i.
Reading the heights
// JavaScript
const d = await (await fetch('https://www.altimetercloud.com/api/terrain_grid/?lat=54.45&lon=-3.05&size_km=10')).json();
const bytes = Uint8Array.from(atob(d.heights_b64), c => c.charCodeAt(0));
const P = d.pack; // typed arrays are little endian on nearly every machine
const raw = P.type === 'uint16' ? new Uint16Array(bytes.buffer)
: P.type === 'int16' ? new Int16Array(bytes.buffer)
: new Float32Array(bytes.buffer);
const heightAt = (row, col) => raw[row * d.cols + col] * P.scale + P.offset;
const latOf = row => d.north - row * d.lat_step;
const lonOf = col => d.west + col * d.lon_step;
# Python
import base64, requests, numpy as np
d = requests.get('https://www.altimetercloud.com/api/terrain_grid/',
params={'lat': 54.45, 'lon': -3.05, 'size_km': 10}).json()
P = d['pack']
dtype = {'uint16': '<u2', 'int16': '<i2', 'float32': '<f4'}[P['type']]
h = np.frombuffer(base64.b64decode(d['heights_b64']), dtype=dtype).reshape(d['rows'], d['cols']) * P['scale'] + P['offset']
lats = d['north'] - np.arange(d['rows']) * d['lat_step']
lons = d['west'] + np.arange(d['cols']) * d['lon_step']
The ESRI ASCII grid
With format=asc the answer is plain text: a short header, then one line per row, north first, with one height per column, to 0.1 m. Away from the equator the points are not square in degrees, so the header gives the steps as dx and dy, which GDAL and QGIS read. Coordinates are WGS 84 latitude and longitude (EPSG:4326). The format has no room for credits, so they come in the X-Data-Credits and Link headers of the answer.
ncols 41
nrows 41
xllcenter -2.98090096
yllcenter 54.43203378
dx 0.0015450478
dy 0.0008983112
nodata_value -32768
256.9 261.7 265.6 268.3 270 271 271 270.2 ...
264.9 269.4 273.6 276.9 278.7 279.7 280.1 279.3 ...
...
Errors
Errors come back as JSON with "success": false, a code and a message.
| Code | Meaning |
|---|---|
412 | A parameter is missing or not valid, or the area is more than 100 km on a side |
413 | The spacing asked for would give more than 1,000,000 points; least_spacing_m gives the finest that fits the area |
429 | Too many requests from your address, or one still being answered |
Try it
Latitude of the middle
Longitude of the middle
Size (km, up to 100)
Spacing (metres, 30 to 5000, or leave empty)
Format (json or asc)
Packing (uint16, int16 or float32)
Made with this data: a 3D map
This is an example of something built from the grid, not part of the API. The API returns only the grid of heights. Here your browser asks the API for a grid (one request, counted in your hourly allowance), turns the heights into a 3D surface with three.js, and lays map imagery from other services over it: the same approach as the 3D views in Altimeter Cloud's own tools. Changing the map after that reuses the same heights.
Latitude of the middle
Longitude of the middle
Size (km, up to 40 for this example)
Crediting AltimeterCloud
Free to use. If you use this API, or anything made from the answers, in a commercial product or service, credit AltimeterCloud with a link to www.altimetercloud.com at the point of use: on the screen, page or printout where the data appears, for example “Data from AltimeterCloud.com”. Any credits the data's own sources ask for, listed on this page, apply as well.
Terrain data credits
The ground heights come from the Terrain Tiles dataset (Mapzen / Tilezen, on AWS Open Data), reduced into resolution tiers by Altimeter Cloud. Wherever you show heights from this API, credit their sources. Each answer carries a short form of the list below in its credits field.
- ArcticDEM terrain data DEM(s) were created from DigitalGlobe, Inc., imagery and funded under National Science Foundation awards 1043681, 1559691, and 1542736
- Australia terrain data © Commonwealth of Australia (Geoscience Australia) 2017
- Austria terrain data © offene Daten Österreichs – Digitales Geländemodell (DGM) Österreich
- Canada terrain data contains information licensed under the Open Government Licence – Canada
- Europe terrain data produced using Copernicus data and information funded by the European Union - EU-DEM layers
- Global ETOPO1 terrain data U.S. National Oceanic and Atmospheric Administration
- Mexico terrain data source: INEGI, Continental relief, 2016
- New Zealand terrain data Copyright 2011 Crown copyright (c) Land Information New Zealand and the New Zealand Government (All rights reserved)
- Norway terrain data © Kartverket
- United Kingdom terrain data © Environment Agency copyright and/or database right 2015. All rights reserved
- United States 3DEP (formerly NED) and global GMTED2010 and SRTM terrain data courtesy of the U.S. Geological Survey
The licences behind each statement are on the terrain tiles attribution page.



















