EviavEviav · Docs

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.

  1. Creá tu cuenta. Andá a /register y registrá una cuenta business.
  2. 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.
  3. Cargá una tarjeta (opcional hasta el free tier) en /dashboard/facturacion. Hacemos un cargo de USD 1 que se reembolsa automáticamente para verificar.
  4. Hacé tu primera request:
    curl "https://api.eviav.com/v1/geocode?q=Av.%20Amazonas%20Quito" \
      -H "x-api-key: TU_KEY"
  5. 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 en api.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/maps para 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ículo

Isó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ígonos

Manejo 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"
}
HTTPcodeSignificado
400bad_requestParámetros inválidos o faltantes.
401unauthorizedAPI key inválida, revocada o ausente.
402payment_requiredSin tarjeta cargada (gate de billing). Cargá una en /dashboard/facturacion.
403scope_deniedLa key no tiene scope para este endpoint.
404not_foundRecurso no existe (tileset, dataset, etc.).
429rate_limitedRate limit excedido (ver headers Retry-After).
500internal_errorError interno — reintentá con backoff exponencial.
503service_unavailableMantenimiento 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 reintentar

Los 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.

ServicioFree tier / mesUSD / 1 000 req
Búsqueda
Geocoding100.000$0,38
Reverse Geocoding100.000$0,38
Search & Autofill50.000$0,50
Places30.000$1,00
Ruteo
Directions100.000$1,00
Matrix100.000$1,00
Optimization100.000$1,00
Fleet Routing10.000$2,50
Map Matching100.000$1,00
Nearest100.000$1,00
Mapas
Static Maps50.000$0,50
Vector & Raster Tiles200.000$0,13
Datos geográficos
Isochrone100.000$1,00
Elevation50.000$1,00
Timezone50.000$0,50
Tile Query50.000$0,50
Inteligencia artificial
Maps Grounding5.000$5,00
Tilesets propios
Custom Tilesets200.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.

Ruteo

Direcciones, matriz, optimización y emparejamiento de trazas GPS.

Directions

GET/v1/directions

Ruta entre 2+ puntos con distancia, duración, geometría y steps.

scope: directions100.000 gratis/mes$1,00 / 1k
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

GET/v1/matrix

Matriz de tiempos y distancias entre orígenes y destinos (máx 2 500 elementos).

scope: matrix100.000 gratis/mes$1,00 / 1k
curl "https://api.eviav.com/v1/matrix?coordinates=-0.22,-78.51;-0.12,-78.48" -H "x-api-key: TU_KEY"

Optimization

POST/v1/optimization

Ordena varias paradas en el recorrido más eficiente (TSP single-vehicle).

scope: optimization100.000 gratis/mes$1,00 / 1k
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

POST/v1/fleet

Optimización de flota: múltiples vehículos + trabajos con restricciones (VROOM).

scope: fleet10.000 gratis/mes$2,50 / 1k
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

GET/v1/match

Ajusta una traza GPS ruidosa a la red vial real (snap-to-road).

scope: match100.000 gratis/mes$1,00 / 1k
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

GET/v1/nearest

Encuentra la o las vías más cercanas a un punto en la red vial.

scope: nearest100.000 gratis/mes$1,00 / 1k
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

GET/v1/static

Imagen PNG renderizada con el estilo Eviav (center+zoom o bbox, markers, path).

scope: static50.000 gratis/mes$0,50 / 1k
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.png

Vector & Raster Tiles

GET/v1/tiles/{tileset}/{z}/{x}/{y}

Tiles autenticados de streets (vector), satellite y terrain.

scope: tiles200.000 gratis/mes$0,13 / 1k
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

GET/v1/isochrone

Áreas alcanzables en X minutos (polígonos GeoJSON) con Valhalla.

scope: isochrone100.000 gratis/mes$1,00 / 1k
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

GET/v1/elevation

Elevación en metros sobre el nivel del mar (DEM terrain-rgb) para una coordenada.

scope: elevation50.000 gratis/mes$1,00 / 1k
curl "https://api.eviav.com/v1/elevation?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"

Timezone

GET/v1/timezone

Zona horaria IANA y offset UTC para una coordenada.

scope: timezone50.000 gratis/mes$0,50 / 1k
curl "https://api.eviav.com/v1/timezone?lat=-0.18&lon=-78.48" -H "x-api-key: TU_KEY"

Tile Query

GET/v1/tilequery

Features OSM cercanos a una coordenada, con sus propiedades.

scope: tilequery50.000 gratis/mes$0,50 / 1k
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

POST/v1/grounding

Respuesta en lenguaje natural fundamentada con datos del mapa.

scope: grounding5.000 gratis/mes$5,00 / 1k
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

GET/v1/tiles/custom/{id}/{z}/{x}/{y}.pbf

Sirve vector tiles de tus tilesets self-serve (privados por organización).

scope: datasets200.000 gratis/mes$0,13 / 1k
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].