Documentación Eviav
API REST para mapas, geocoding, ruteo, IA geoespacial. Self-hosted, pago por uso con free tier generoso, 50% del precio Mapbox en cada SKU equivalente.
Quickstart
5 pasos para hacer tu primera request.
- Creá tu cuenta. Andá a /register y registrá una cuenta business.
- Generá una API key. En /dashboard/api-keys elegí los servicios que vas a consumir y creá la key. Copiá el texto plano — solo se muestra una vez.
- Cargá una tarjeta (opcional hasta el free tier) en /dashboard/facturacion. Hacemos un cargo de USD 1 que se reembolsa automáticamente para verificar.
- Hacé tu primera request:
curl "https://api.eviav.com/v1/geocode?q=Av.%20Amazonas%20Quito" \ -H "x-api-key: TU_KEY" - Monitoreá uso y costo en /dashboard/uso y /dashboard/facturacion.
Autenticación
Toda request a api.eviav.com requiere una API key válida. Hay dos formas equivalentes de enviarla:
# Header recomendado:
curl https://api.eviav.com/v1/geocode?q=quito -H "x-api-key: TU_KEY"
# Alternativa estándar Bearer:
curl https://api.eviav.com/v1/geocode?q=quito -H "Authorization: Bearer TU_KEY"Las keys tienen dos entornos: live (producción, factura uso real) y test (no factura, free tier de 10k req/mes combinados). El prefijo de la key indica el entorno: ev_live_… / ev_test_….
Cada key puede limitarse a un subconjunto de servicios (scopes). Si una key no tiene scopes, accede a todos. Una key sin scopes y otra con scopes específicos al mismo servicio tienen el mismo permiso de acceso para ese servicio.
Código de implementación
La API es REST estándar con JSON — funciona desde cualquier lenguaje con un cliente HTTP, sin necesidad de instalar nada. Acá tenés wrappers listos para copiar-pegar en los stacks más comunes.
Importante: nunca expongas tu API key live en código que corre en el browser. Para apps web, ponela en variables de entorno del backend y proxieá las requests, o usá una key con scopes limitados (ej. solo tiles) con restricciones de dominio configuradas en el dashboard.
OpenAPI, SDKs y Postman
Tres formas de integrar Eviav sin escribir un wrapper HTTP a mano:
- OpenAPI 3.1 spec — fuente única de verdad descargable.
https://api.eviav.com/openapi.json. Usalo con cualquier generator (openapi-typescript, autorest, openapi-generator, etc.) para crear tu propio cliente tipado. Swagger UI interactiva enapi.eviav.com/docs. - SDK TypeScript oficial — instalá
npm install @eviav/api-client(Node 20+, Bun, Deno, browser). Ya tipado, con retries automáticos. - SDK MapLibre wrapper — instalá
npm install @eviav/mapspara mapas interactivos en browser con tiles autenticados Eviav. - Postman / Insomnia / Bruno collection — descargá eviav.postman_collection.json (18 endpoints con sample requests). Importá en Postman, seteá tu API key en variables y probá inmediatamente.
Node / TypeScript
Wrapper minimalista con fetch nativo (Node 20+):
// lib/eviav.ts
const BASE = process.env.EVIAV_BASE_URL ?? "https://api.eviav.com";
const KEY = process.env.EVIAV_API_KEY!;
async function eviav<T = unknown>(path: string, init: RequestInit = {}): Promise<T> {
const r = await fetch(`${BASE}${path}`, {
...init,
headers: {
"x-api-key": KEY,
"Content-Type": "application/json",
...(init.headers ?? {}),
},
});
if (!r.ok) {
const err = await r.json().catch(() => ({}));
throw new Error(`Eviav ${r.status} — ${err.error ?? r.statusText} [${err.request_id ?? "?"}]`);
}
return r.json() as Promise<T>;
}
// Geocoding: dirección → coordenadas
export const geocode = (q: string) =>
eviav<{ features: Array<{ center: [number, number]; place_name: string }> }>(
`/v1/geocode?q=${encodeURIComponent(q)}`,
);
// Reverse: coordenadas → dirección
export const reverse = (lat: number, lon: number) =>
eviav(`/v1/reverse?lat=${lat}&lon=${lon}`);
// Ruta entre dos puntos
export const route = (from: [number, number], to: [number, number]) =>
eviav(`/v1/directions?coordinates=${from.join(",")};${to.join(",")}&steps=true`);
// POIs por categoría cerca de un punto
export const places = (lat: number, lon: number, category: string, radius = 1000) =>
eviav(`/v1/places?lat=${lat}&lon=${lon}&category=${category}&radius=${radius}`);
// Optimización multi-parada (TSP)
export const optimize = (stops: Array<[number, number]>) =>
eviav("/v1/optimization", {
method: "POST",
body: JSON.stringify({ coordinates: stops, roundtrip: true }),
});Uso:
import { geocode, route } from "./lib/eviav";
const g = await geocode("Av. Amazonas, Quito");
console.log(g.features[0].center); // [-78.4836, -0.1807]
const r = await route([-78.51, -0.22], [-78.48, -0.12]);
console.log(r.routes[0].distance, "m,", r.routes[0].duration, "s");Python
Con httpx (recomendado por sync + async unificado):
# eviav.py
import os, httpx
BASE = os.getenv("EVIAV_BASE_URL", "https://api.eviav.com")
KEY = os.environ["EVIAV_API_KEY"]
HEADERS = {"x-api-key": KEY}
client = httpx.Client(base_url=BASE, headers=HEADERS, timeout=20.0)
def _get(path: str, **params):
r = client.get(path, params=params)
r.raise_for_status()
return r.json()
def _post(path: str, body: dict):
r = client.post(path, json=body)
r.raise_for_status()
return r.json()
def geocode(q: str):
return _get("/v1/geocode", q=q)
def reverse(lat: float, lon: float):
return _get("/v1/reverse", lat=lat, lon=lon)
def route(from_, to_):
coords = f"{from_[0]},{from_[1]};{to_[0]},{to_[1]}"
return _get("/v1/directions", coordinates=coords, steps="true")
def places(lat: float, lon: float, category: str, radius: int = 1000):
return _get("/v1/places", lat=lat, lon=lon, category=category, radius=radius)
def optimize(stops):
return _post("/v1/optimization", {"coordinates": stops, "roundtrip": True})Uso:
import eviav
g = eviav.geocode("Av. Amazonas, Quito")
print(g["features"][0]["center"]) # [-78.4836, -0.1807]
r = eviav.route([-78.51, -0.22], [-78.48, -0.12])
print(r["routes"][0]["distance"], "m,", r["routes"][0]["duration"], "s")Browser — mapa interactivo con MapLibre
MapLibre GL JS (open-source, drop-in replacement de Mapbox GL JS): npm install maplibre-gl
// MapView.tsx (React + MapLibre)
import { useEffect, useRef } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
export function MapView() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
const map = new maplibregl.Map({
container: containerRef.current,
// ⚠ Usá una key con scope SOLO 'tiles' para exponer en browser.
style: `https://api.eviav.com/v1/tiles/streets/style.json?apikey=${process.env.NEXT_PUBLIC_EVIAV_TILES_KEY}`,
center: [-78.48, -0.18],
zoom: 12,
});
return () => map.remove();
}, []);
return <div ref={containerRef} style={{ width: "100%", height: 500 }} />;
}Casos de uso comunes
Dibujar una ruta en el mapa
const r = await route([-78.51, -0.22], [-78.48, -0.12]);
const geometry = r.routes[0].geometry; // GeoJSON LineString
map.addSource("route", { type: "geojson", data: { type: "Feature", geometry, properties: {} } });
map.addLayer({
id: "route-line",
type: "line",
source: "route",
paint: { "line-color": "#FF6A1A", "line-width": 4 },
});Autocompletado de direcciones (input con debounce)
let timer: ReturnType<typeof setTimeout> | null = null;
input.addEventListener("input", (e) => {
const q = (e.target as HTMLInputElement).value;
if (timer) clearTimeout(timer);
timer = setTimeout(async () => {
if (q.length < 3) return;
const r = await fetch(`/api/search?q=${encodeURIComponent(q)}`); // proxy backend
const { features } = await r.json();
renderSuggestions(features);
}, 200);
});
// Backend (Next.js / Express / Fastify):
// app/api/search/route.ts
export async function GET(req: Request) {
const q = new URL(req.url).searchParams.get("q") ?? "";
const r = await fetch(
`https://api.eviav.com/v1/search?text=${encodeURIComponent(q)}&lat=-0.18&lon=-78.48`,
{ headers: { "x-api-key": process.env.EVIAV_API_KEY! } },
);
return new Response(await r.text(), { status: r.status, headers: { "content-type": "application/json" } });
}Optimización de flota (delivery, técnicos en ruta)
const result = await fetch("https://api.eviav.com/v1/fleet", {
method: "POST",
headers: { "x-api-key": process.env.EVIAV_API_KEY!, "Content-Type": "application/json" },
body: JSON.stringify({
vehicles: [
{ id: 1, start: [-78.51, -0.22], end: [-78.51, -0.22], capacity: [50], time_window: [28800, 64800] },
{ id: 2, start: [-78.50, -0.20], end: [-78.50, -0.20], capacity: [50], time_window: [28800, 64800] },
],
jobs: [
{ id: 1, location: [-78.48, -0.12], amount: [5], time_windows: [[32400, 43200]] },
{ id: 2, location: [-78.49, -0.15], amount: [8], time_windows: [[36000, 50400]] },
{ id: 3, location: [-78.47, -0.10], amount: [3] },
],
}),
}).then(r => r.json());
console.log(result.routes); // ruta optimizada por vehículoIsócronas — áreas de cobertura por tiempo
// "¿Qué área cubro en 10, 20 y 30 minutos en auto desde acá?"
const r = await fetch(
"https://api.eviav.com/v1/isochrone?lat=-0.18&lon=-78.48&contours=10,20,30&costing=auto",
{ headers: { "x-api-key": process.env.EVIAV_API_KEY! } },
);
const iso = await r.json(); // FeatureCollection con 3 polígonosManejo de errores con retry exponencial
async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
for (let i = 0; i < retries; i++) {
try { return await fn(); }
catch (err: any) {
// Respeta Retry-After si vino del 429, sino backoff exponencial.
if (i === retries - 1) throw err;
const wait = (err.retryAfter ?? Math.pow(2, i)) * 1000;
await new Promise(r => setTimeout(r, wait));
}
}
throw new Error("unreachable");
}
// Uso:
const data = await withRetry(() => geocode("Av. Amazonas, Quito"));Toda response trae los headers X-Request-Id,X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset y, en caso de 429, Retry-After. Si tenés que reportar un bug a soporte, incluí siempre el X-Request-Id.
Errores
Las respuestas con error siguen una forma consistente:
{
"error": "Mensaje legible para humanos",
"code": "rate_limited",
"request_id": "req_AbCdEf1234567890"
}| HTTP | code | Significado |
|---|---|---|
| 400 | bad_request | Parámetros inválidos o faltantes. |
| 401 | unauthorized | API key inválida, revocada o ausente. |
| 402 | payment_required | Sin tarjeta cargada (gate de billing). Cargá una en /dashboard/facturacion. |
| 403 | scope_denied | La key no tiene scope para este endpoint. |
| 404 | not_found | Recurso no existe (tileset, dataset, etc.). |
| 429 | rate_limited | Rate limit excedido (ver headers Retry-After). |
| 500 | internal_error | Error interno — reintentá con backoff exponencial. |
| 503 | service_unavailable | Mantenimiento o upstream caído. Reintentá. |
Rate limits
Cada API key tiene un límite de requests por segundo y por minuto. El gateway responde 429 Too Many Requests cuando se excede, con headers estándar:
X-RateLimit-Limit: 1000 # max requests por ventana
X-RateLimit-Remaining: 273 # cuántas quedan
X-RateLimit-Reset: 1748394812 # epoch UTC del reset
Retry-After: 7 # segundos hasta poder reintentarLos servicios pesados (Matrix, Isochrone, Optimization, Fleet) consumen más del budget por request — Matrix cuenta 2x, Isochrone 4x, Optimization/Fleet 5x. Esto se refleja solo en el rate limit, no en la facturación (que va por request crudo).
Precios y free tier
Pago por uso: solo facturás lo que consumís por encima del free tier de cada servicio. Sin cuota fija, sin mínimos, sin permanencia.
| Servicio | Free tier / mes | USD / 1 000 req |
|---|---|---|
| Búsqueda | ||
| Geocoding | 100.000 | $0,38 |
| Reverse Geocoding | 100.000 | $0,38 |
| Search & Autofill | 50.000 | $0,50 |
| Places | 30.000 | $1,00 |
| Ruteo | ||
| Directions | 100.000 | $1,00 |
| Matrix | 100.000 | $1,00 |
| Optimization | 100.000 | $1,00 |
| Fleet Routing | 10.000 | $2,50 |
| Map Matching | 100.000 | $1,00 |
| Nearest | 100.000 | $1,00 |
| Mapas | ||
| Static Maps | 50.000 | $0,50 |
| Vector & Raster Tiles | 200.000 | $0,13 |
| Datos geográficos | ||
| Isochrone | 100.000 | $1,00 |
| Elevation | 50.000 | $1,00 |
| Timezone | 50.000 | $0,50 |
| Tile Query | 50.000 | $0,50 |
| Inteligencia artificial | ||
| Maps Grounding | 5.000 | $5,00 |
| Tilesets propios | ||
| Custom Tilesets | 200.000 | $0,13 |
Precios sin IVA. Refresh de free tier el primer día de cada mes UTC. Volúmenes altos (>500k req/mes en un SKU) tienen descuentos automáticos — contactanos en [email protected].
Referencia de servicios
18 endpoints REST agrupados en 6 familias. Cada uno con su método, path, scope, free tier, tarifa y ejemplo curl.
Búsqueda
Geocoding, autocompletado y búsqueda de lugares por nombre o dirección.
Geocoding
Convierte texto (dirección o lugar) en coordenadas.
curl "https://api.eviav.com/v1/geocode?q=Av.%20Amazonas%20Quito" -H "x-api-key: TU_KEY"Reverse Geocoding
Convierte coordenadas en la dirección o lugar más cercano.
curl "https://api.eviav.com/v1/reverse?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Search & Autofill
Autocompletado predictivo de lugares y direcciones con sesgo por ubicación.
curl "https://api.eviav.com/v1/search?text=Av%20Amaz&lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Places
POIs por categoría cerca de un punto y agregaciones de densidad.
curl "https://api.eviav.com/v1/places?lat=-0.18&lon=-78.48&category=restaurant&radius=1000" -H "x-api-key: TU_KEY"Ruteo
Direcciones, matriz, optimización y emparejamiento de trazas GPS.
Directions
Ruta entre 2+ puntos con distancia, duración, geometría y steps.
curl "https://api.eviav.com/v1/directions?coordinates=-0.22,-78.51;-0.12,-78.48&steps=true" -H "x-api-key: TU_KEY"Matrix
Matriz de tiempos y distancias entre orígenes y destinos (máx 2 500 elementos).
curl "https://api.eviav.com/v1/matrix?coordinates=-0.22,-78.51;-0.12,-78.48" -H "x-api-key: TU_KEY"Optimization
Ordena varias paradas en el recorrido más eficiente (TSP single-vehicle).
curl -X POST "https://api.eviav.com/v1/optimization" -H "x-api-key: TU_KEY" -H "Content-Type: application/json" -d '{"coordinates":[[-0.22,-78.51],[-0.12,-78.48],[-0.18,-78.49]],"roundtrip":true}'Fleet Routing
Optimización de flota: múltiples vehículos + trabajos con restricciones (VROOM).
curl -X POST "https://api.eviav.com/v1/fleet" -H "x-api-key: TU_KEY" -H "Content-Type: application/json" -d '{"vehicles":[{"id":1,"start":[-78.51,-0.22],"end":[-78.51,-0.22]}],"jobs":[{"id":1,"location":[-78.48,-0.12]}]}'Map Matching
Ajusta una traza GPS ruidosa a la red vial real (snap-to-road).
curl "https://api.eviav.com/v1/match?coordinates=-0.18,-78.48;-0.181,-78.481;-0.182,-78.482" -H "x-api-key: TU_KEY"Nearest
Encuentra la o las vías más cercanas a un punto en la red vial.
curl "https://api.eviav.com/v1/nearest?point=-0.18,-78.48&number=1" -H "x-api-key: TU_KEY"Mapas
Tiles vectoriales/raster y mapas estáticos renderizados con el estilo Eviav.
Static Maps
Imagen PNG renderizada con el estilo Eviav (center+zoom o bbox, markers, path).
curl "https://api.eviav.com/v1/static?center=-0.18,-78.48&zoom=13&width=600&height=400" -H "x-api-key: TU_KEY" -o mapa.pngVector & Raster Tiles
Tiles autenticados de streets (vector), satellite y terrain.
curl "https://api.eviav.com/v1/tiles/streets/12/1155/2050.pbf" -H "x-api-key: TU_KEY"Datos geográficos
Elevación, zona horaria, isócronas y consultas espaciales sobre POIs.
Isochrone
Áreas alcanzables en X minutos (polígonos GeoJSON) con Valhalla.
curl "https://api.eviav.com/v1/isochrone?lat=-0.18&lon=-78.48&contours=10,20&costing=auto" -H "x-api-key: TU_KEY"Elevation
Elevación en metros sobre el nivel del mar (DEM terrain-rgb) para una coordenada.
curl "https://api.eviav.com/v1/elevation?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Timezone
Zona horaria IANA y offset UTC para una coordenada.
curl "https://api.eviav.com/v1/timezone?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"Tile Query
Features OSM cercanos a una coordenada, con sus propiedades.
curl "https://api.eviav.com/v1/tilequery?lat=-0.18&lon=-78.48&radius=50" -H "x-api-key: TU_KEY"Inteligencia artificial
Respuestas en lenguaje natural fundamentadas con datos del mapa.
Maps Grounding
Respuesta en lenguaje natural fundamentada con datos del mapa.
curl -X POST "https://api.eviav.com/v1/grounding" -H "x-api-key: TU_KEY" -H "Content-Type: application/json" -d '{"query":"cafeterías cerca de la Plaza Foch"}'Tilesets propios
Hospedaje y servido privado de tus tilesets self-serve.
Custom Tilesets
Sirve vector tiles de tus tilesets self-serve (privados por organización).
curl "https://api.eviav.com/v1/tiles/custom/ts_ABC/12/1155/2050.pbf" -H "x-api-key: TU_KEY"Soporte
Reportes de bugs y feature requests: [email protected].
Status del servicio: status.eviav.com (en preparación).
Comercial / volúmenes altos: [email protected].