Documentación

ORBITAL GRID API

Una sola API sobre los satélites de observación terrestre del mundo. Describís qué necesitás — área, fechas, tipo de dato, resolución, tolerancia a nubes — y el motor de ruteo elige el mejor satélite, consulta el catálogo real y hace auto-switch cuando hace falta (incluido óptico→SAR cuando las nubes tapan la vista).

Base URL:


Qué es ORBITAL GRID

  • Un registry de satélites con tres estados honestos: live (datos reales ya), requires_credential (conector listo, se activa al configurar la key) y pending_agreement (comercial, a la espera de convenio).
  • Un motor de ruteo que puntúa cada satélite capaz y explica por qué eligió el que eligió, con la traza completa de auto-switch.
  • Una capa de inteligencia que convierte una pregunta de negocio en español en parámetros técnicos.
  • Un SKU asistido por analista que entrega escenas, índices y evidencia técnica con AOI, fuente, fecha, QA, cobertura, linaje y limitaciones.

Quickstart

1. Solicitá tu API key: contanos quién sos y para qué la necesitás en Solicitar API Key, y el equipo de Orbital Grid te la hace llegar.

2. Hacé tu primera llamada:

curl -s -X POST {{BASE}}/v1/intent \
  -H "Content-Type: application/json" \
  -H "X-API-Key: TU_API_KEY" \
  -d '{"query":"serie NDVI y NDMI de mi AOI en Pergamino en el último mes"}'
import httpx

r = httpx.post(
    "{{BASE}}/v1/intent",
    headers={"X-API-Key": "TU_API_KEY"},
    json={"query": "serie NDVI y NDMI de mi AOI en Pergamino en el último mes"},
    timeout=60,
)
data = r.json()
print(data["selected_satellite"], "→", data["explanation"])
const r = await fetch("{{BASE}}/v1/intent", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": "TU_API_KEY" },
  body: JSON.stringify({ query: "serie NDVI y NDMI de mi AOI en Pergamino en el último mes" }),
});
const data = await r.json();
console.log(data.selected_satellite, data.explanation);

3. Descargá el contrato versionable en /openapi.json. La UI interactiva permanece deshabilitada en producción.

Autenticación

Todas las direcciones de datos piden el header X-API-Key. Pedí tu key en Solicitar API Key: se muestra una única vez y se guarda hasheada del lado del servidor.

Cada key tiene un rate limit por minuto según su plan, y su uso queda medido por endpoint y por día — lo consultás en /v1/account/usage.

Tratá tu API key como una contraseña: no la publiques en frontends ni repos. Si se filtró, revocala con POST /v1/portal/keys/revoke y emití una nueva.

Salud de la plataforma

GET/healthsin key

Estado del servicio, conteos del registry y alcanzabilidad de cada catálogo upstream (probado de verdad, con caché corto).

curl -s {{BASE}}/health
import httpx
print(httpx.get("{{BASE}}/health", timeout=30).json())
const h = await (await fetch("{{BASE}}/health")).json();
console.log(h.status, h.catalogs);

El listado de satélites

GET/v1/satellitessin key

Todos los satélites registrados con sus capacidades y estado de acceso.

Query paramValoresDescripción
data_typeoptical · multispectral · sar · thermal · atmospheric · elevationFiltra por tipo de dato.
accesslive · requires_credential · pending_agreementFiltra por estado de acceso.
cost_tierfree · commercialFiltra por costo del dato.
curl -s "{{BASE}}/v1/satellites?data_type=sar&access=live"
import httpx
sats = httpx.get("{{BASE}}/v1/satellites",
                 params={"data_type": "sar", "access": "live"}, timeout=30).json()
print(sats["count"], "satélites SAR live")
const s = await (await fetch("{{BASE}}/v1/satellites?data_type=sar&access=live")).json();
console.log(s.count, "satélites SAR live");

Preguntá en lenguaje natural

POST/v1/intentX-API-Key

Mandá una pregunta de negocio en español; ORBITAL GRID infiere AOI, tipo de dato, índices (NDVI/NDWI/NBR/SAR), ventana temporal, tolerancia a nubes y prioridad, rutea con el mismo motor y explica la decisión en español.

curl -s -X POST {{BASE}}/v1/intent \
  -H "Content-Type: application/json" -H "X-API-Key: TU_API_KEY" \
  -d '{"query":"buscar escenas comparables y señales candidatas de agua nueva en Pehuajó"}'
import httpx
r = httpx.post("{{BASE}}/v1/intent", headers={"X-API-Key": "TU_API_KEY"},
               json={"query": "buscar escenas comparables y señales candidatas de agua nueva en Pehuajó"},
               timeout=120)
d = r.json()
print(d["interpretation"], d["selected_satellite"], d["explanation"], sep="\n")
const d = await (await fetch("{{BASE}}/v1/intent", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": "TU_API_KEY" },
  body: JSON.stringify({ query: "buscar escenas comparables y señales candidatas de agua nueva en Pehuajó" }),
})).json();
console.log(d.inferred_params, d.selected_satellite);

Agente de IA: pregunta libre sobre una zona

POST/v1/agent/askX-API-Key

La puerta conversacional de ORBITAL GRID: mandás una pregunta en tu idioma y una zona; el agente elige el producto y los satélites, calcula con píxeles/metadata reales y responde en lenguaje simple con semáforo. El LLM redacta pero nunca calcula: sin dato real, la respuesta lo dice.

CampoTipoDescripción
questionstrQué necesitás saber, en tus palabras (3–1000 caracteres). Obligatorio.
lat / lonfloatPunto de interés (lat -90 a 90, lon -180 a 180). Usá esto o bbox.
bbox[o,s,e,n]Zona explícita [oeste, sur, este, norte] en WGS84, alternativa al punto (p. ej. la envolvente de un AOI subido con /v1/aoi/parse). Tope ~10.000 km².
buffer_kmfloatRadio del área alrededor del punto (0–50, default 5). Ignorado si usás bbox.
langes · enIdioma de la respuesta (default es).
curl -s -X POST {{BASE}}/v1/agent/ask \
  -H "Content-Type: application/json" -H "X-API-Key: TU_API_KEY" \
  -d '{"question":"¿cómo está mi campo esta semana?","lat":-33.90,"lon":-60.60}'
import httpx
# Zona por bbox (alternativa al punto): p. ej. la envolvente de un AOI subido.
r = httpx.post("{{BASE}}/v1/agent/ask",
               headers={"X-API-Key": "TU_API_KEY"},
               json={"question": "¿qué escenas y señales de agua requieren revisión?",
                     "bbox": [-60.70, -34.00, -60.50, -33.80]},
               timeout=120)
d = r.json()
print(d["status"], d["semaphore"], d["answer"])
const d = await (await fetch("{{BASE}}/v1/agent/ask", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": "TU_API_KEY" },
  body: JSON.stringify({ question: "¿cómo está mi campo?", lat: -33.90, lon: -60.60 }),
})).json();
console.log(d.status, d.semaphore, d.provenance);

La respuesta trae status (delivered · no_data · unavailable · requires_credential), answer en lenguaje consumidor, semaphore (verde · amarillo · rojo), confidence, provenance (scene_id, satellite, datetime) y technical_detail con el producto, el satélite elegido y el porqué. Cada número del answer sale de píxeles o metadata reales.


Capa de analytics

GET/v1/analyticsX-API-Key

Catálogo de capacidades con su escalera de madurez. registered o searchable no significa motor validado ni claim comercial autorizado.

Demo local de señales SAR por celdas

POST/v1/analytics/fieldsX-API-Key

Endpoint de investigación geocercado a Pergamino. El valor histórico publicado se obtuvo sobre 326 celdas rectangulares y una referencia NDVI proxy, no sobre lotes ni verdad de campo. No autoriza claims de siembra, especie o fecha.

CampoTipoDescripción
bbox[w,s,e,n]AOI en WGS84. Obligatorio.
min_confidence0–1Solo lotes con confianza ≥ este valor (default 0).
include_geometryboolfalse = solo propiedades, sin polígonos (default true).
curl -s -X POST {{BASE}}/v1/analytics/fields \
  -H "Content-Type: application/json" -H "X-API-Key: TU_API_KEY" \
  -d '{"bbox":[-60.65,-33.90,-60.60,-33.85],"min_confidence":0.8}'
import httpx
r = httpx.post("{{BASE}}/v1/analytics/fields",
               headers={"X-API-Key": "TU_API_KEY"},
               json={"bbox": [-60.65, -33.90, -60.60, -33.85], "min_confidence": 0.8},
               timeout=60)
d = r.json()
if d["status"] == "delivered":
    print(d["summary"])          # salida experimental; requiere revisión humana
    geojson = d["result"]        # celdas de demo, no límites de parcelas
else:
    print(d["message"])          # AOI aún no procesada — respuesta honesta
const d = await (await fetch("{{BASE}}/v1/analytics/fields", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": "TU_API_KEY" },
  body: JSON.stringify({ bbox: [-60.65, -33.90, -60.60, -33.85], min_confidence: 0.8 }),
})).json();
console.log(d.status, d.summary ?? d.message);

La salida es una demo estática con VV/VH/RVI y referencia óptica proxy. No representa lotes reales ni se comercializa como siembra detectada. Fuera del piloto devuelve accepted_on_demand; el SKU vendible entrega series descriptivas revisadas por analista.

Subí tu área de interés desde un archivo

POST/v1/aoi/parseX-API-Key

Convertí un archivo geográfico en un polígono WGS84 normalizado, listo para el geometry/bbox del resto de la API. Enviá el archivo como multipart en el campo file, o mandá un JSON con wkt o geojson. Nunca inventa geometría: si el archivo no tiene un polígono válido, responde ok:false con la causa en error.

CampoTipoDescripción
filemultipartArchivo KML · KMZ · GeoJSON · Shapefile (ZIP) · WKT · CSV de puntos. Alternativa al cuerpo JSON.
wktstr (JSON)Un POLYGON / MULTIPOLYGON en WKT (cuerpo JSON, alternativa al archivo).
geojsonobj (JSON)Un objeto GeoJSON (Feature · FeatureCollection · geometría) en el cuerpo JSON.

Respuesta AOIResult: ok, geometry (Polygon/MultiPolygon WGS84), bbox [oeste, sur, este, norte], area_km2, vertex_count, simplified, source_format, notes[] y error.

curl -s -X POST {{BASE}}/v1/aoi/parse \
  -H "X-API-Key: TU_API_KEY" \
  -F "file=@mi_campo.kml"
import httpx
with open("mi_campo.geojson", "rb") as f:
    r = httpx.post("{{BASE}}/v1/aoi/parse",
                   headers={"X-API-Key": "TU_API_KEY"},
                   files={"file": ("mi_campo.geojson", f, "application/geo+json")},
                   timeout=60)
aoi = r.json()
if aoi["ok"]:
    print(aoi["area_km2"], "km²", aoi["vertex_count"], "vértices")
    bbox = aoi["bbox"]          # [oeste, sur, este, norte]
else:
    print("No se pudo:", aoi["error"])
// Cuerpo JSON con WKT (alternativa a subir un archivo)
const aoi = await (await fetch("{{BASE}}/v1/aoi/parse", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": "TU_API_KEY" },
  body: JSON.stringify({ wkt: "POLYGON((-60.7 -34.0,-60.5 -34.0,-60.5 -33.8,-60.7 -33.8,-60.7 -34.0))" }),
})).json();
console.log(aoi.ok, aoi.area_km2, aoi.bbox);
Límites honestos: hasta 8 MB por archivo (si se supera → 413); área máxima 500.000 km² (por encima se rechaza con ok:false y la causa); polígonos de más de 2000 vértices se simplifican conservando la forma (flag simplified + nota); guardas anti zip-bomb en KMZ/Shapefile. Coordenadas fuera del rango WGS84 (archivo proyectado) → rechazo honesto pidiendo reproyectar a EPSG:4326. Un CSV de puntos arma el convex hull (envolvente) y lo aclara en notes.

Exportá los contornos de escena en GeoJSON o Shapefile

POST/v1/exports/footprintsX-API-Key

Exportá los footprints de un conjunto de escenas. Pasá scenes (una lista de escenas, p. ej. las de /v1/imagery/search) o search (un cuerpo ImagerySearchRequest: el motor de ruteo corre y exporta lo que devuelve). Elegí el formato con ?format=geojson (default) o ?format=shapefile.

CampoTipoDescripción
sceneslistLista de escenas a exportar (esto o search).
searchobjUn ImagerySearchRequest; el motor rutea y exporta las escenas que encuentra.
formatquerygeojson (default) · shapefile. Va como query param en la URL.
curl -s -X POST "{{BASE}}/v1/exports/footprints?format=geojson" \
  -H "Content-Type: application/json" -H "X-API-Key: TU_API_KEY" \
  -d '{"search":{"bbox":[-60.70,-34.00,-60.50,-33.80],
       "datetime_from":"2026-04-01","datetime_to":"2026-07-06",
       "data_type":"optical","limit":10}}' \
  -o footprints.geojson
import httpx
# scenes = res["scenes"]  # p. ej. la respuesta de /v1/imagery/search
r = httpx.post("{{BASE}}/v1/exports/footprints",
               params={"format": "shapefile"},
               headers={"X-API-Key": "TU_API_KEY"},
               json={"scenes": scenes},
               timeout=120)
if r.headers.get("X-Export-Fallback") == "geojson":
    print("Shapefile no disponible; llegó GeoJSON:", r.headers.get("X-Export-Note"))
open("footprints.zip", "wb").write(r.content)
const r = await fetch("{{BASE}}/v1/exports/footprints?format=geojson", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-API-Key": "TU_API_KEY" },
  body: JSON.stringify({ scenes }),   // o { search: { bbox, datetime_from, ... } }
});
const fc = await r.json();            // FeatureCollection (application/geo+json)
console.log(fc.features.length, "footprints");
Fallback honesto: si la generación del shapefile falla, la API devuelve el GeoJSON equivalente con el header X-Export-Fallback: geojson (y X-Export-Note con la causa) en vez de romper. El GeoJSON sale como application/geo+json; el shapefile como un .zip (shp+shx+dbf+prj+README).

Solicitar API Key o hablar con Orbital Grid

Para usar la API necesitás una clave personal. Pedila acá y el equipo de Orbital Grid te contacta para darte de alta y ayudarte a arrancar.

Cuenta y uso

GET/v1/account/usageX-API-Key

Uso medido de tu key: total, hoy, por día (últimos 30) y por endpoint. Es la misma medición que alimenta la facturación.

GET/v1/accountX-API-Key

Datos de la key: prefijo visible, plan, rate limit, fecha de alta.

POST/v1/portal/keys/revokeX-API-Key

Revoca la key con la que autenticás. Es irreversible: solicitá una nueva si la necesitás.

curl -s {{BASE}}/v1/account/usage -H "X-API-Key: TU_API_KEY"
import httpx
u = httpx.get("{{BASE}}/v1/account/usage",
              headers={"X-API-Key": "TU_API_KEY"}, timeout=30).json()
print(u["usage"]["total"], "requests")
const u = await (await fetch("{{BASE}}/v1/account/usage",
  { headers: { "X-API-Key": "TU_API_KEY" } })).json();
console.log(u.usage.total, "requests");

Errores y límites

CódigoSignificadoQué hacer
401Key faltante, inválida o revocada.Verificá el header X-API-Key; si todavía no tenés una, solicitala en Solicitar API Key.
422Parámetros inválidos (bbox mal formado, enum desconocido…).El detalle indica el campo exacto.
429Rate limit de tu plan excedido.Respetá el header Retry-After.
503Capacidad temporalmente no disponible.Reintentá con backoff; mirá /health.

Los catálogos upstream pueden estar caídos: la plataforma lo reporta en catalogs_status y en /health en vez de inventar resultados.