Base URL: https://api.solana.eo-router.com/v1 โ STAC-API 1.0.0 compliant. Paid endpoints use x402 USDC payments on Solana devnet. No API key needed โ just a funded wallet.
Solana Platform Overview
EO Router Solana is a satellite imagery marketplace where AI agents pay per API call with USDC on Solana. No accounts, no API keys, no credit cards โ just a funded Solana wallet.
Every image ordered through the platform carries a cryptographic provenance proof anchored on Solana, verifiable by anyone. The full trust chain:
Layer
Technology
What it proves
1. Data source
TLSNotary MPC-TLS
API response came from a specific provider domain (e.g. earth-search.aws.element84.com) โ no tampering
2. Verification
RISC Zero zkVM
Image metadata matches the order: correct location, time window, and TLSNotary hash โ all verified inside a zero-knowledge proof
3. On-chain proof
Bonsol (Groth16)
RISC Zero STARK wrapped to Groth16 SNARK, verified on-chain in <200k compute units
4. Settlement
Solana Programs
USDC escrow, provenance attestation, and treasury fees โ all on Solana devnet
Devnet. This instance runs on Solana devnet. USDC is the Circle devnet faucet token (4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU). No real funds are involved.
x402 USDC Payments
Paid endpoints return HTTP 402 Payment Required with a JSON body describing the payment terms. The client (agent or wallet) constructs a Solana USDC transfer and re-sends the request with an X-PAYMENT header.
No account needed. Any agent or wallet that can sign a Solana USDC transfer can use the API. The x402 protocol (x402.org) is an open standard by Coinbase for machine-to-machine payments.
On-Chain Escrow
When an agent places a confirmed order, USDC is locked in an on-chain escrow PDA (Program Derived Address). The escrow guarantees the buyer's funds are held until delivery, with automatic refund on expiry.
Escrow PDA is derived from ["escrow", order_id]. Anyone can look up an escrow on Solana Explorer to verify locked funds.
TLSNotary MPC-TLS
TLSNotary uses multi-party computation (MPC) to cryptographically prove that data came from a specific TLS server โ without revealing the TLS session keys. This proves the satellite provider's API response is authentic and untampered.
EO Router runs a TLSNotary sidecar using the tlsn crate (v0.1.0-alpha.14). The Prover and Verifier run in-process connected via tokio::io::duplex. This is real MPC-TLS, not a simulation.
1. The Prover connects to the provider's HTTPS server through a TLS channel that is jointly controlled by Prover and Verifier via MPC.
2. Neither party alone has the TLS session key โ they split the key via 2-party computation.
3. The Verifier can confirm the server's TLS certificate chain (proving the domain) and the response data, without the Prover being able to forge anything.
4. The Prover uses selective disclosure to reveal only the API response while redacting auth headers.
Verified against real providers: Earth Search (18,984 bytes, 1.8s), NASA CMR (11,553 bytes, 1.3s). Logs show starting MPC-TLS โ finished MPC-TLS for every proof.
ZK Provenance (RISC Zero + Bonsol)
After TLSNotary authenticates the provider response, a RISC Zero zkVM guest program verifies the provenance claims inside a zero-knowledge proof. The guest is written in normal Rust and compiled to RISC-V.
The guest verifies three checks:
Check
What it does
Failure means
TLSNotary hash match
Private tlsnotary_presentation_hash == public expected_tlsn_hash
Image was captured outside the requested area of interest
Temporal (window)
timestamp >= time_start AND timestamp <= time_end
Image was captured outside the requested time window
// RISC Zero guest output (committed to journal, public on-chain)
{
"image_hash": [u8; 32], // SHA-256 of delivered image"provenance_valid": true, // all 3 checks passed"aoi_center_lat": 44400000, // ร 1e6"aoi_center_lon": 8900000, // ร 1e6"tlsnotary_hash": [u8; 32], // TLSNotary presentation hash"binding_hash": [u8; 32] // sha256(image || tlsn || scene_id)
}
// Guest compiled to RISC-V ELF (220 KB)// IMAGE_ID: 2c8c4671fc085bd1d54580eb986671bd790f13ad8f1cc09d30649e5d1c5033bd// Registered with Bonsol on Solana devnet
Bonsol handles the full ZK pipeline: RISC Zero STARK proof (off-chain, ~30s) โ Groth16 SNARK wrapping โ on-chain Solana verification in <200k compute units. No trusted setup required.
Trust Chain
Every delivered image has an end-to-end cryptographic trust chain from the provider's TLS connection to an on-chain attestation.
Provider TLS โ TLSNotary (MPC-TLS) โ RISC Zero zkVM โ Bonsol (Groth16) โ Solana
Step 1: Provider HTTPS API response authenticated via real MPC-TLS
โ presentation_hash = SHA-256(verified_transcript || domain)
Step 2: RISC Zero guest verifies: TLSNotary hash match + AOI check + time window
โ STARK proof of correct execution
Step 3: Bonsol wraps STARK โ Groth16 SNARK (constant-size, cheap to verify on-chain)
Step 4: eo_provenance program stores attestation on Solana:
โ image_hash, binding_hash, tlsnotary_hash, scene_id, coordinates, timestamp
Binding hash: sha256(image_hash || tlsnotary_hash || scene_id_hash)
Ties the image file to its provenance proof โ anyone can recompute and verify
The binding hash is the key verifiability primitive. Given an image file and its provenance record on Solana, anyone can: (1) SHA-256 the image, (2) read the on-chain attestation, (3) recompute the binding hash, and (4) confirm it matches. No trust in EO Router required.
Solana Programs
Three Anchor programs deployed on Solana devnet:
Program
Address
Purpose
eo_escrow
2widvLF5ZU3gXcoyHLMd8EeghwSudbZjiLKGUCfWNLX3
USDC lock/release/refund for orders
eo_treasury
6THM4f1Cqbpn9Uhb57fgNw42e9hYtHyahpftEifhdmg7
Platform fee collection + OpenRouter sweep
eo_provenance
CeeXzXw3XEYXNgL6CmhRWy9qYGJG857Zkea3nyb3R9Bq
ZK attestation registry (Bonsol callback)
All programs are viewable on Solana Explorer (devnet). Escrow PDAs derived from ["escrow", order_id], provenance PDAs from ["provenance", order_id].
Bonsol ZK Verifier: Guest image registered on-chain with Bonsol program (BoNsHRcyLLNdtnoDf8hiCNZpyehMC4FDMxs6NTxFi3ew). Deployment account: 5rz7EQjdGei34ehsnNCR61y9ntsdJaWJ3ViTRnR2XQwr.
Treasury Flywheel
A self-sustaining business model: USDC fees collected from x402 payments and escrow releases accumulate in the treasury program. When the balance exceeds a threshold, it sweeps to fund OpenRouter API credits โ which power the AI chat and agent capabilities.
// The flywheel
Agent pays USDC โ x402 / escrow โ Treasury PDA โ sweep_to_openrouter
โ โ
โโโโโ AI-powered search, chat, MCP tools โโโ OpenRouter credits
// Treasury instructions
receive_fee(order_id, fee_amount) // 2.5% platform fee from each order
sweep_to_openrouter(amount) // USDC โ OpenRouter wallet when threshold hit
set_sweep_threshold(new_threshold) // governance (authority only)// Treasury wallet
ALpKsLNWqg3iPYz13b5ZqExESK4BGsDni9ASszLA7N5K
The flywheel is the core economic model: AI agents pay for satellite imagery โ fees fund the AI that helps them find and order imagery. Self-reinforcing.
Authentication
All API endpoints require authentication via Bearer token. You can use either a JWT (from login/signup) or an API key.
# 1. Sign up
POST /v1/auth/signup
{ "email": "you@company.com", "password": "SecurePass10+", "name": "Your Name" }
// โ { "access_token": "eyJ...", "user": { ... } }# 2. Create an API key (recommended for production)
POST /v1/keys
{ "name": "CI/CD pipeline", "type": "live" }
// โ { "full_key": "eor_live_sk_a1b2c3..." } โ save this! shown ONCE# 3. Use in all requests
Authorization: Bearer eor_live_sk_a1b2c3...
API keys are bcrypt-hashed โ the full key is only returned at creation. Store it in a secrets manager immediately. Test keys (eor_test_sk_...) work identically but incur no real charges.
Provider Reference
EO Router connects to 14 satellite imagery providers. Commercial providers are billed per kmยฒ; free providers serve open data at no cost.
Provider
ID
Sensor
GSD
Free
Tasking
BYOK
Credential Type
Planet Labs
planet
optical
3m / 0.5m
No
Yes
Yes
api_key
ICEYE
iceye
sar
0.5m / 3m
No
Yes
Yes
oauth2_client
Capella Space
capella
sar
0.35m
No
Yes
Yes
api_key
Umbra
umbra
sar
0.25m
Partial
Yes
Yes
api_key
Airbus
airbus
optical
0.3m / 1.5m
No
Yes
Yes
oauth2_client
Maxar
maxar
optical
0.31m
No
Yes
No
api_key
BlackSky
blacksky
optical
0.5m
No
Yes
Yes
api_key
Earth Search
earthsearch
optical
10m / 30m
Yes
No
No
โ
ESA / CDSE
cdse
optical, sar
10m / 5m
Yes
No
No
โ
NASA CMR
nasa
optical
30m
Yes
No
No
โ
USGS M2M
usgs
optical
30m
Yes
No
No
โ
Sentinel Hub
sentinelhub
optical
10m
Yes
No
Yes
oauth2_client
Google Earth Engine
gee
optical, sar
500m / 10m
Yes
No
Yes
service_account
LGND.ai
lgnd
optical, sar
varies
Yes
No
Yes
bearer_token
Maxar WV-3 / WV-4: ITAR-controlled. Only available to users with country_code: "US" in their profile. See ITAR Compliance section.
POST /search
Federated STAC search across all connected providers. Queries run in parallel; results are merged and sorted by composite relevance score.
Task a satellite for a future acquisition. Call feasibility first (free) to get opportunity windows and pricing from all 7 commercial providers: ICEYE, Capella, Planet, Airbus, Maxar, Umbra, BlackSky. Then confirm with orders.
// Step 1: feasibility (free, no charge)
POST /v1/tasking/feasibility
{
"aoi": { ... },
"time_window": { "from": "2026-03-22T00:00:00Z", "to": "2026-03-28T23:59:59Z" },
"requirements": { "sensor_type": "sar", "max_gsd_m": 1.0, "orbit_direction": "ascending" },
"providers": ["iceye", "capella", "umbra"],
"priority": "standard", // standard (1x) | priority (1.75x) | rush (3x)"budget_cap": 2000// optional: max EUR per opportunity
}
// Step 2: order from opportunity_id
POST /v1/tasking/orders
{
"opportunity_id": "topp_iceye_...",
"estimated_eur": 1240.5, // from feasibility response"confirm": true// false = estimate only
}
Priority cost multipliers: standard = 1x base cost, priority = 1.75x (faster scheduling), rush = 3x (next available pass).
Deviation alerts: Each opportunity includes a deviations array warning you when it differs from your request:
Deviation
Severity
Description
time_shift
warning
Opportunity is outside the requested time window
gsd_exceeded
warning
Resolution is coarser than requested max_gsd_m
off_nadir_exceeded
warning
Off-nadir angle exceeds requested maximum
orbit_direction_mismatch
info
Orbit direction differs from preference
budget_exceeded
critical
Estimated cost exceeds budget_cap
high_off_nadir
info
Off-nadir > 40ยฐ (geometric distortion risk)
The response also includes a top-level warnings array for systemic issues (e.g. all opportunities shifted, all exceed budget, provider API failures).
Subscriptions
Monitor an AOI for new matching imagery. Set auto_order: true with budget caps to purchase and deliver every match automatically.
Bring Your Own Key (BYOK) lets you use your own provider API credentials. Orders route directly to the provider at wholesale cost with a 10% routing fee (vs 2.5% platform fee without BYOK).
Retrieve 64-dimensional satellite embeddings from Google DeepMind's AlphaEarth Foundations. Embeddings capture land cover, vegetation, and built environment features at 10m resolution. Coverage: global, 2017-2025.
Use cases: similarity search, land cover classification, change detection, feature extraction for ML pipelines.
AlphaEarth embeddings are served by the platform โ no credentials required from your side. Max bbox area: ~100 kmยฒ. Rate limit: 10 requests/minute.
LGND.ai โ Similarity Search & Change Detection
LGND.ai provides embedding-based similarity search, chip-level search, and AI change detection on satellite and aerial imagery. Free tier covers California NAIP (2020-2022) and France Sentinel-2 (2024-2025). All endpoints are fully integrated into the EO Router API.
List available LGND collections (imagery sources, date ranges, coverage areas).
LGND is fully integrated โ authenticate with your EO Router API key, no separate LGND token needed. Rate limit: 10 requests/minute. Free tier: read-only collections only.
Balance
Your account balance is in EUR. Amounts are pre-authorized on order confirm and captured on delivery against actual clipped area.
Extensions: EO (eo:cloud_cover), SAR (sar:frequency_band), View (view:off_nadir), Processing (processing:level), EO Router (eorouter: namespace).
Processing options
Parameter
Default
Description
format
COG
COG ยท GeoTIFF ยท NITF ยท SLC ยท SICD ยท SIDD
clip_to_aoi
true
Clip to AOI polygon โ reduces cost
reproject_epsg
null
Reproject to EPSG; null = provider default
harmonize_sr
false
Planet: normalize to surface reflectance
radiometric_correction
sigma0
SAR: beta0 ยท sigma0 ยท gamma0
compress
deflate
COG: deflate ยท lzw ยท lerc ยท none
COG deliveries include auto-generated overview levels (256ร256 tiles, levels 2โ5ร). GDAL, QGIS and rio-cogeo read them natively.
ITAR Compliance
Maxar WorldView-3 (WV-3) and WorldView-4 (WV-4) sub-50cm imagery is classified as defense articles under 15 USC ยง2778 (Arms Export Control Act / ITAR).
Restriction
Details
Affected collections
maxar_wv03, maxar_wv04
Requirement
User must have country_code: "US" in their profile
Error
403 itar_restriction for non-US users
Scope
Ordering only โ search and browse are unrestricted
// Set your country code
PATCH /v1/auth/me
{ "country_code": "US" }
// Non-US users ordering Maxar WV-3 will get:// 403 { "code": "itar_restriction", "message": "...ITAR-controlled..." }
Error Codes
All errors return a JSON body with code and message fields.
Code
HTTP
When
itar_restriction
403
Non-US user ordering Maxar WV-3 / WV-4
insufficient_balance
402
Available EUR balance is less than order cost
email_taken
409
Signup with an email that already exists
invalid_credentials
401
Login with wrong email or password
wrong_password
401
Password change with incorrect current password
not_found
404
Resource does not exist or belongs to another user
invalid_geometry
422
Non-Polygon geometry or missing AOI
license_not_accepted
400
Order placed without license_accepted: true
order_not_cancellable
409
Cancel attempt on completed/delivering order
stripe_not_configured
501
Stripe payment not configured on server
oauth_not_configured
501
OAuth provider not configured on server
Tutorial: Change detection pipeline
from eorouter import EoRouterClient
client = EoRouterClient(api_key="...")
before = client.search(aoi=aoi, date_from="2026-01-01", date_to="2026-01-31",
max_cloud_cover=15, limit=1)[0]
after = client.search(aoi=aoi, date_from="2026-03-01", date_to="2026-03-21",
max_cloud_cover=15, limit=1)[0]
order = client.orders.create(
scene_ids=[before.scene_id, after.scene_id],
processing={"format":"COG", "clip_to_aoi":True, "aoi":aoi},
delivery={"type":"s3", "destination_id":"dest_prod"},
confirm=True)
order.wait(timeout_minutes=90)
print([i["s3_key"] for i in order.manifest()["items"]])
// Claude Desktop (MCP connected) conversation:
User: "Find best SAR over Suez Canal last 72h, ascending orbit,
cheapest, order it."
Claude calls:
eo_search(aoi=suez, sensor_types=["sar"], days=3, routing="cheapest_matching")
eo_get_balance()
eo_order_archive(scene_ids=["iceye_..."], confirm=False) // estimate first
Claude: "Found ICEYE Stripmap (3m, ascending, 8h ago) โ โฌ312.00.
Your balance is โฌ1,240. Shall I place the order?"
User: "Yes, confirm it."
Claude calls: // MCP clients can set allow_orders in their implementationeo_order_archive(scene_ids=["iceye_..."], confirm=True)
eo_get_order_status(order_id="ord_...")
Claude: "Order placed. ICEYE scene queued for delivery to
s3://your-bucket/... โ โฌ312.00 charged."
The full MCP tool schema is at GET https://api.eo-router.com/mcp/schema.
AI Chat (Beta)
BETA
EO Router provides an optional hosted AI assistant powered by Claude. Send natural language queries and the AI will search imagery, check feasibility, query balances, and more using the same tools available via MCP.
Disabled by default. Enable in Dashboard โ Settings โ AI Chat (Beta). No additional API keys required โ the AI service is provided by EO Router.
Rate limit: 5 requests per minute. AI calls use Claude Haiku to minimize latency. This is a beta feature โ responses may be imperfect.
POST /chat
Send a natural language query to EO Router's AI assistant.
POST /v1/chat
{
"message": "Find Sentinel-2 images over Paris from last week with less than 20% cloud cover",
"conversation_id": null, // optional, for multi-turn"allow_orders": false// see Order Safety below
}
// Response
{
"response": "I found 8 Sentinel-2 scenes over Paris from the last 7 days...",
"tool_calls": [
{ "tool": "eo_search", "result_summary": "8 scenes found" }
],
"conversation_id": "conv_abc123",
"beta": true
}
Order Safety
By default, the AI can only provide cost estimates โ it cannot place real orders or spend credits. When the AI estimates an order, the response includes _requires_approval: true.
To let the AI place a confirmed order, set allow_orders: true in your request. Use this only after your app has obtained explicit user approval (e.g. a confirmation dialog).
// Step 1: User asks to order โ AI returns estimate (allow_orders=false, default)
POST /v1/chat
{ "message": "Order that Sentinel-2 scene over Paris" }
// โ AI calls eo_order_archive(confirm=false), returns estimate + _requires_approval// Step 2: Your app shows the estimate, user clicks "Confirm"// Step 3: Re-send with allow_orders=true
POST /v1/chat
{ "message": "Yes, confirm the order", "allow_orders": true }
// โ AI places the order, charges EUR balance