API Documentation
Quick Start
Add Terratile terrain to a CesiumJS viewer in a few lines. Replace YOUR_API_KEY with the key from your dashboard.
const viewer = new Cesium.Viewer("cesiumContainer");
viewer.scene.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
"https://terrain.terratile.io?key=YOUR_API_KEY",
{ requestVertexNormals: true }
);
The provider fetches layer.json for metadata, then streams quantized-mesh tiles as the camera moves.
Authentication
Every request requires an API key. You can get one by signing up and visiting the dashboard. Keys use the prefix tt_live_.
Query parameter
GET https://terrain.terratile.io/layer.json?key=tt_live_...
Request header
X-API-Key: tt_live_...
If both are present the query parameter takes precedence.
CLI Authentication
Terratile supports a device authorization flow for terminals, scripts, and AI agents. No browser automation or credential handling required.
One-liner setup
curl -sL https://terratile.io/auth/cli.sh | sh
This opens a browser for GitHub sign-in, then prints your API key directly to the terminal.
Manual device flow
For programmatic control (CLI tools, IDE plugins, CI pipelines), use the device flow endpoints directly:
# 1. Start the device flow
curl -s -X POST https://terratile.io/auth/device \
-H "Content-Type: application/json" -d '{}'
# Response:
# {
# "device_code": "a1b2c3...",
# "user_code": "ABCD-1234",
# "verification_uri": "https://terratile.io/device",
# "expires_in": 600,
# "interval": 5
# }
# 2. Direct the user to open verification_uri and enter user_code
# (or open verification_uri?code=ABCD-1234 directly)
# 3. Poll for the token
curl -s "https://terratile.io/auth/device/token?device_code=a1b2c3..."
# While waiting: { "status": "pending" } (HTTP 202)
# On success: { "status": "authorized", "api_key": "tt_live_..." }
# On expiry: { "error": "Code expired or invalid" } (HTTP 404)
CLI plan upgrade
If you're hitting rate limits (HTTP 429), upgrade directly from the terminal:
curl -sL https://terratile.io/auth/cli.sh | sh -s upgrade starter
Or with the device flow endpoints:
# Start upgrade flow
curl -s -X POST https://terratile.io/auth/device \
-H "Content-Type: application/json" \
-d '{"plan": "starter"}'
# User signs in with GitHub, completes Stripe checkout in browser
# Poll for confirmation
curl -s "https://terratile.io/auth/device/token?device_code=..."
# { "status": "completed", "tier": "starter" }
Valid plan values: starter, production, business.
Endpoints
Terrain tile
GET https://terrain.terratile.io/{z}/{x}/{y}.terrain?key=KEY
Returns a binary quantized-mesh tile, gzip-encoded. z is the zoom level, x and y are tile coordinates. Vertex normals are included when requested via CesiumTerrainProvider.
Layer metadata
GET https://terrain.terratile.io/layer.json?key=KEY
Returns a JSON document describing available zoom levels, tile scheme, and extensions. CesiumJS fetches this automatically when you create a terrain provider.
Response details
| Header | Value |
|---|---|
Content-Type |
application/octet-stream (tiles) / application/json (layer.json) |
Content-Encoding |
gzip |
Access-Control-Allow-Origin |
* |
Integrations
Terratile serves standard quantized-mesh tiles compatible with any client that supports the format. Below are integration examples for popular frameworks.
CesiumJS — Query Parameter Auth
The simplest integration. Pass your API key as a query parameter:
const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayerPicker: false,
geocoder: false,
});
viewer.scene.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
"https://terrain.terratile.io?key=YOUR_API_KEY",
{ requestVertexNormals: true }
);
CesiumJS — Header Auth (X-API-Key)
For production apps where you don't want the key visible in network URLs, use the X-API-Key header via a custom Resource:
const resource = new Cesium.Resource({
url: "https://terrain.terratile.io",
headers: {
"X-API-Key": "YOUR_API_KEY",
},
});
const viewer = new Cesium.Viewer("cesiumContainer");
viewer.scene.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
resource,
{ requestVertexNormals: true }
);
The header is sent on every tile and layer.json request. If both a query parameter and header are present, the query parameter takes precedence.
CesiumJS — Error Handling & Retry on 429
CesiumJS does not natively retry on 429. For production apps under load, add a retry callback via Cesium.Resource:
const resource = new Cesium.Resource({
url: "https://terrain.terratile.io?key=YOUR_API_KEY",
retryCallback: async (resource, error) => {
if (error && error.statusCode === 429) {
const retryAfter = error.getResponseHeader?.("Retry-After");
const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : 10000;
await new Promise((r) => setTimeout(r, delay));
return true; // retry the request
}
return false;
},
retryAttempts: 3,
});
const viewer = new Cesium.Viewer("cesiumContainer");
viewer.scene.terrainProvider = await Cesium.CesiumTerrainProvider.fromUrl(
resource,
{ requestVertexNormals: true }
);
The retryCallback returns true to signal CesiumJS to retry. The Retry-After header tells you exactly how long to wait.
Resium (React + CesiumJS)
Resium wraps CesiumJS as React components. Create the terrain provider once and pass it to the Viewer:
import { Viewer } from "resium";
import { CesiumTerrainProvider } from "cesium";
import { useEffect, useState } from "react";
function App() {
const [terrainProvider, setTerrainProvider] = useState(null);
useEffect(() => {
CesiumTerrainProvider.fromUrl(
"https://terrain.terratile.io?key=YOUR_API_KEY",
{ requestVertexNormals: true }
).then(setTerrainProvider);
}, []);
if (!terrainProvider) return <div>Loading terrain...</div>;
return <Viewer full terrainProvider={terrainProvider} />;
}
export default App;
Install with npm install resium cesium. Resium works with Vite, Next.js, and Create React App.
deck.gl TerrainLayer
deck.gl can render quantized-mesh terrain tiles via its TerrainLayer (v8.8+):
import { Deck } from "@deck.gl/core";
import { TerrainLayer } from "@deck.gl/geo-layers";
const terrainLayer = new TerrainLayer({
id: "terrain",
minZoom: 0,
maxZoom: 15,
elevationData:
"https://terrain.terratile.io/{z}/{x}/{y}.terrain?key=YOUR_API_KEY",
texture:
"https://services.arcgisonline.com/ArcGIS/rest/services/" +
"World_Imagery/MapServer/tile/{z}/{y}/{x}",
meshMaxError: 4.0,
});
const deck = new Deck({
initialViewState: {
latitude: 46.24,
longitude: -122.18,
zoom: 11.5,
bearing: 140,
pitch: 60,
maxPitch: 89,
},
controller: true,
layers: [terrainLayer],
});
The TerrainLayer automatically parses the binary quantized-mesh format when the tile URL ends in .terrain.
Rate Limits
Requests are rate-limited per API key using a 10-second fixed window. When the limit is exceeded the API returns 429 with a Retry-After header.
| Tier | Rate | Price |
|---|---|---|
| Sandbox | 5 req/s | Free |
| Starter | 20 req/s | $49/mo |
| Production | 50 req/s | $119/mo |
| Business | 150 req/s | $299/mo |
| Custom | Custom | Contact us |
Errors
The API returns standard HTTP status codes. Error responses include a JSON body with a message field.
| Status | Message | Cause |
|---|---|---|
401 |
Missing API key | No key param or header provided |
403 |
Invalid API key | Key not found or revoked |
429 |
Rate limit exceeded | Over tier limit; retry after Retry-After seconds |
500 |
Server error | Internal issue |