API reference
JSON over HTTPS. Available on the Pro plan and above.
Overview
The Unearth AI API exposes the same site-selection logic that powers our mapping tool, programmatically. Query a circle of demand and supply infrastructure, get a deduplicated list with operators, distances, and a 0–100 opportunity score.
Base URL: https://api.unearth-ai.com
API version: v1 (current). Versioned via the URL path.
MCP server
The fastest way to use Unearth from an LLM-driven agent (Claude Desktop, Cursor, Cline, your custom agent): install the Model Context Protocol server. One config line and the agent gets seven Unearth tools as first-class function calls.
{
"mcpServers": {
"unearth": {
"command": "npx",
"args": ["-y", "@unearth-ai/mcp"],
"env": { "UNEARTH_API_KEY": "unearth_pro_…" }
}
}
}
Full setup at /agents. Source at github.com/unearth-ai/mcp.
For OpenAI / Gemini / function-calling-style agents, we also publish a ready-to-use tool spec at /api/tools-openai.json.
JavaScript SDK
Optional one-file ES module that wraps every endpoint with helper methods. Works in browsers, Node 18+, Deno, and Bun. ~1KB minified, no dependencies.
import { UnearthClient } from 'https://unearth-ai.com/assets/sdk/unearth.js';
const u = new UnearthClient({ apiKey: process.env.UNEARTH_API_KEY });
const result = await u.search({ lat: 37.77, lng: -122.41, radiusKm: 10, datasets: 'coworking' });
console.log(result.opportunity, result.total_in_radius);
Errors throw with status, code, hint, and rateLimitResetEpoch properties so you can build retry/backoff logic without re-parsing the response.
Authentication
All authenticated endpoints expect a Bearer token in the Authorization header. Provision a key by completing checkout on the pricing page; the key is shown once on welcome.html after payment.
Authorization: Bearer unearth_pro_a1b2c3d4e5f6…
Treat your API key like a password. Store it in an environment variable, not in source control. If a key is exposed, manage it via the billing portal and contact support to rotate.
Rate limits
Limits are per calendar month, reset at 00:00 UTC on the first of the month. Every authenticated response includes the current period's usage:
{
"meta": {
"rate_limit": { "used": 247, "limit": 10000, "period": "month" }
}
}
- Starter: 1,000 requests / month
- Pro: 10,000 requests / month
- Enterprise: negotiated volume
Exceeding the cap returns 429 rate_limit_exceeded. Need higher? Talk to us.
Errors
Errors return a JSON body with an error string and (sometimes) a hint:
{ "error": "invalid_lat_lng" }
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_lat_lng | Missing or non-numeric coordinates. |
| 400 | lat_lng_out_of_range | Outside ±90° / ±180°. |
| 400 | invalid_radius_km | Must be between 0 and 200. |
| 400 | unknown_datasets | One or more dataset IDs are not recognised. Check Dataset IDs. |
| 401 | missing_api_key | No Authorization header. |
| 401 | invalid_api_key | Token is unknown or revoked. |
| 429 | rate_limit_exceeded | Monthly cap reached. Upgrade or wait until next period. |
| 503 | stripe_not_configured | Billing endpoints aren't ready yet (pre-launch). |
Health check
$ curl https://api.unearth-ai.com/v1/health
{ "ok": true, "ts": "2026-04-27T10:00:00.000Z" }
List datasets
Returns the dataset IDs you can pass to /v1/search, plus their role (supply or demand) and whether they're temporarily deferred.
$ curl https://api.unearth-ai.com/v1/datasets
{
"datasets": [
{ "id": "ev-charger", "role": "supply", "deferred": false },
{ "id": "data-centers", "role": "demand", "deferred": false },
{ "id": "coworking", "role": "demand", "deferred": false },
{ "id": "coliving", "role": "demand", "deferred": false },
{ "id": "self-storage", "role": "demand", "deferred": false },
{ "id": "solar-farm", "role": "demand", "deferred": false }
]
}
Sample queries
Try a query right now without an API key — the /v1/datasets/<id>/sample endpoint returns the first records of any dataset. For full radius queries you'll need a Pro key.
Search
Find every record across one or more datasets that falls inside a circle, score the opportunity, and return the top operators.
Query parameters
| Param | Type | Description |
|---|---|---|
| latrequired | number | Center latitude, -90 to 90. |
| lngrequired | number | Center longitude, -180 to 180. |
| radius_km | number | Search radius in kilometres. Default 10. Max 200. |
| datasets | string | Comma-separated dataset IDs. Defaults to all non-deferred. |
Example
curl -H "Authorization: Bearer $UNEARTH_API_KEY" \
"https://api.unearth-ai.com/v1/search?lat=37.77&lng=-122.41&radius_km=10&datasets=coworking,data-centers"
Response
{
"query": { "lat": 37.77, "lng": -122.41, "radius_km": 10, "datasets": ["coworking","data-centers"] },
"deferred_datasets": [],
"total_in_radius": 84,
"supply_count": 0,
"demand_count": 84,
"opportunity": 0,
"area_km2": 314.2,
"density_per_100km2": 26.7,
"top_operators": [
{ "name": "WeWork", "count": 9 },
{ "name": "Industrious", "count": 4 }
],
"by_dataset": {
"coworking": { "count": 76, "records": [ /* … */ ] },
"data-centers": { "count": 8, "records": [ /* … */ ] }
},
"meta": {
"api_version": "1.0",
"generated_at": "2026-04-27T10:00:00.000Z",
"rate_limit": { "used": 247, "limit": 10000, "period": "month" },
"attribution": { /* see below */ }
}
}
Record fields (in by_dataset.<id>.records[])
| Field | Type | Description |
|---|---|---|
| id | string | Source-stable identifier. |
| name | string | Display name of the facility / operator. |
| operator | string | Operator (parent company / brand) if known. |
| lat, lng | number | Geographic coordinates (WGS84). |
| city, country | string | Resolved location. |
| distance_km | number | Great-circle distance from the query center. |
Usage
Returns this month's consumption plus 12 months of history (oldest-first, ready to chart).
{
"plan": "pro",
"rate_limit": { "used": 247, "limit": 10000, "remaining": 9753, "period": "2026-04" },
"history": [
{ "period": "2025-05", "requests": 0 },
{ "period": "2025-06", "requests": 0 },
/* … */
{ "period": "2026-04", "requests": 247 }
],
"created_at": "2026-04-12T15:32:11.000Z"
}
Key introspection
Returns the calling key's fingerprint, plan, monthly limit, and (when available) email and Stripe customer ID. Useful for verifying which key the client is presenting without round-tripping through the dashboard. The full key is never returned.
$ curl -H "Authorization: Bearer $UNEARTH_API_KEY" \
https://api.unearth-ai.com/v1/keys/me
{
"fingerprint": "unearth_…a3f6",
"plan": "pro",
"rate_limit_per_month": 10000,
"email": "ada@example.com",
"created_at": "2026-04-12T15:32:11.000Z",
"stripe_customer_id": "cus_xyz"
}
Key rotation
Generates a new API key tied to the same subscription and revokes the old one. The old key stops working immediately — save the new one before closing the response. The dev-mode key (when used) cannot be rotated.
$ curl -X POST -H "Authorization: Bearer $UNEARTH_API_KEY" \
https://api.unearth-ai.com/v1/keys/rotate
{
"api_key": "unearth_pro_b7c9…ef21",
"fingerprint": "unearth_…ef21",
"plan": "pro",
"rotated_at": "2026-04-29T19:22:08.103Z",
"notice": "Save this key now — it will not be shown again. The previous key is revoked."
}
Rate-limit response headers
Every successful authenticated response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix epoch seconds at the start of next month, UTC). Surface these to your users for backoff logic — same convention as GitHub's API.
Billing portal
Returns a one-time URL to the Stripe-hosted billing portal where customers can update payment method, change plan, or cancel.
curl -X POST -H "Authorization: Bearer $UNEARTH_API_KEY" \
https://api.unearth-ai.com/v1/billing-portal
# → { "url": "https://billing.stripe.com/p/session/…" }
Redirect the user to response.url; Stripe returns them to https://unearth-ai.com/pricing.html when they're done.
Dataset IDs
| ID | Role | Source |
|---|---|---|
| ev-charger | supply | Open Charge Map (CC BY-SA 4.0) + OSM (ODbL). Stratified sample of 5k records — full dataset (177k+) ships on Enterprise. |
| data-centers | demand | OSM (ODbL) + Unearth Data Intelligence. |
| coworking | demand | OpenStreetMap (ODbL). |
| coliving | demand | OpenStreetMap (ODbL). |
| self-storage | demand | OpenStreetMap (ODbL). |
| solar-farm | demand | OpenStreetMap (ODbL). |
Attribution
API responses include a meta.attribution object naming each source and its license. When you display, redistribute, or build on top of records, please credit the underlying sources:
- OpenStreetMap data is © OpenStreetMap contributors, available under the Open Database License (ODbL).
- EV charging records are © Open Charge Map contributors, available under CC BY-SA 4.0.
Full posture, including share-alike triggers and Derivative-Database boundaries: LICENSES.md.