API Reference

EVE Emptiness exposes a read-only JSON API over the same origin as the web app. All endpoints are public — no authentication required. Data refreshes hourly from the EVE ESI. Historical data covers a rolling 30-day window stored in PostgreSQL.

Base URL https://jumps.tovdc.com/api

Notes

Auth
None required
Format
JSON
Refresh cadence
Hourly
History window
30 days
Systems returned
~4,900 k-space
Activity score
0–100, P95-norm
Jump counts are last-hour snapshots. The ESI only provides a rolling 1-hour window. avg_daily_jumps_30d is computed by this service: hourly readings are summed per day, then averaged across the 30-day window.
Excluded systems. Wormhole systems, Abyss space, and inaccessible regions (Jove space: UUA-F4, A821-A, J7HZ-F, Polaris, Yasna Zakh) are excluded from all results. Only permanent k-space stargates are modelled.

Live Data

GET /systems/activity Ranked system list with live + trend data

Returns k-space systems ranked by activity score (or weighted recent activity), enriched with 30-day trend metrics, topology data, and ESI flags. Default limit is 50 systems.

Query Parameters
NameTypeDefaultDescription
maxJumps integer Exclude systems with live jumps above this value
maxKills integer Exclude systems with live ship+pod+NPC kills above this value
securityMin float -1.0 Minimum security status (-1.0 to 1.0)
securityMax float 1.0 Maximum security status (-1.0 to 1.0)
region string Filter to a single region by name (e.g. Delve)
constellation string Filter to a single constellation by name
limit integer 50 Max results to return (capped server-side)
sortBy string activity activity (P95-normalized score) or weighted (exponential-decay recent activity)
minConsistency integer 0 Minimum consistency score 0–100 (% of last 30 days that were quiet)
excludeIncursions boolean false Exclude systems in active incursions
excludeSovCampaigns boolean false Exclude systems with active sovereignty campaigns
excludeFW boolean false Exclude faction warfare systems
excludeHighway boolean false Exclude pipe/hub topology systems (high traffic chokepoints)
excludePochven boolean true Exclude Pochven systems
excludeWormholeAccess boolean false Exclude systems with active Thera wormhole connections
includeNPCStation boolean false When true, return only systems that have a permanent NPC station
includeQuietNow boolean false When true, return only systems currently in a quiet window (current UTC hour below 30% of the system's circadian peak)
maxFWOccupancy float Max FW occupancy ratio 0.0–1.0 (only relevant for FW systems)
maxHubDistance integer 0 When > 0, return only systems within N gate jumps of a major trade hub (Jita/Amarr/Dodixie/Rens/Hek)
Response shape (one system)
// GET /api/systems/activity?maxJumps=5&sortBy=weighted&limit=2
{
  "systems": [
    {
      "system_id":        30000053,
      "system_name":      "G-0Q86",
      "security_status":  -0.496,
      "region":           "Delve",
      "constellation":    "B-WPLZ",

      // Live ESI readings (last ~1 hour)
      "jumps":            2,
      "ship_kills":       0,
      "pod_kills":        0,
      "npc_kills":        14,
      "activity_score":   3.2,   // 0–100, P95-normalized; includes industry + ADM terms
      "danger_density":   0,     // PvP kills per 100 jumps
      "pod_ratio":        0,     // pod/ship kills; high = gate camp; null if pods but no ship kills

      // 30-day trend metrics
      "avg_daily_jumps_30d":  18.4,  // SUM/day averaged over 30d
      "avg_daily_kills_30d":  4.1,
      "days_tracked_30d":    28,
      "weighted_activity_score": 2.7, // exp decay, 2-day half-life
      "quiet_days_30":       22,
      "consistency_score":   78,   // % of days that were quiet
      "trend_direction":     "stable",
      "avg_daily_jumps":     17.9,  // 7-day
      "avg_daily_kills":     3.8,

      // Topology
      "topology_type":      "dead-end", // dead-end|pipe|junction|hub
      "gate_count":         1,
      "bottleneck_score":   0,
      "is_regional_gate":   false,
      "has_npc_station":    true,
      "neighbor_avg_activity": 5.8,
      "reachable_via_wormhole": false,
      "hub_distance":       14,    // gate jumps to nearest trade hub; null if unreachable

      // ESI flags
      "in_incursion":         false,
      "incursion_staging":    false,
      "in_sov_campaign":      false,
      "in_fw":               false,
      "fw_occupancy":         null,
      "activity_type":        "quiet",

      // Sovereignty
      "alliance_id":   99005338,
      "corporation_id": null,
      "sov_adm":       3.4   // CCP occupancy index 1–6; null if no sov / not reported
    }
  ],
  "total": 4893,
  "filters": { /* echo of applied filters */ },
  "timestamp": "2026-06-23T04:15:00.000Z"
}
GET /regions All region names

Returns the list of all k-space region names. Useful for populating a region filter dropdown.

Response
[
  { "id": 10000002, "name": "The Forge" },
  { "id": 10000030, "name": "Heimatar" },
  /* … 114 regions total */
]
GET /constellations/:regionId Constellations in a region

Returns constellations for the given region. Pass the numeric region ID (obtained from /regions) as the path segment.

Response
// GET /api/constellations/10000060  (Delve)
[
  { "id": 20000700, "name": "1-A81R" },
  { "id": 20000690, "name": "6-UCYU" },
  /* … */
]
GET /systems/:systemId/circadian 24-hour activity pattern

Returns the average hourly activity distribution for a system over the last 30 days. Hours are in UTC (0–23). Values are averages of the hourly snapshot readings.

Response
// GET /api/systems/30000053/circadian
{
  "system_id": 30000053,
  "hours": [
    { "hour": 0,  "avg_jumps": 1.2, "avg_kills": 0.1 },
    { "hour": 1,  "avg_jumps": 0.8, "avg_kills": 0.0 },
    /* … 24 entries total … */
    { "hour": 23, "avg_jumps": 2.1, "avg_kills": 0.3 }
  ]
}
GET /systems/bulk Live status for many systems (watchlist)

Live status for up to 50 system ids passed as a comma-separated ids parameter. Each entry has the same shape as a /systems/activity system object. Reuses cached ESI maps, so it is cheap.

Response
// GET /api/systems/bulk?ids=30000142,30002187
{ "systems": [ /* same objects as /systems/activity */ ] }
GET /map/systems Galaxy heatmap coordinates + activity

All k-space systems that have map coordinates, with their latest activity score — for rendering the galaxy heatmap.

Response
// GET /api/map/systems
[
  { "system_id": 30000142, "system_name": "Jita", "region": "The Forge",
    "security_status": 0.946, "pos_x": -1.29e17, "pos_z": 1.20e17,
    "activity_score": 92.4, "jumps": 3021 }
]
GET /health Liveness / readiness probe

Cheap health check with no ESI dependency. Returns HTTP 200 when the database is reachable, 503 otherwise.

Response
// GET /api/health
{ "status": "ok", "db": true, "last_collection": "2026-07-02T14:00:00.000Z" }

Historical Data

GET /history/:systemId Raw hourly records

Returns raw hourly snapshot records for a system (up to 30 days). Each row is one collection run reading.

Response
// GET /api/history/30000053
{
  "system_id": 30000053,
  "records": [
    {
      "timestamp":      "2026-06-23T04:00:00.000Z",
      "jumps":          2,
      "ship_kills":     0,
      "pod_kills":      0,
      "npc_kills":      14,
      "activity_score": 3.2
    }
  ]
}
GET /history/:systemId/range Daily totals over N days

Aggregates hourly snapshots into daily totals (SUM per UTC day). This is what powers the 30-day history chart. One row per day with data.

Query Parameters
NameTypeDefaultDescription
days integer 30 Look-back window in days (max 30)
Response
// GET /api/history/30000053/range?days=30
{
  "system_id": 30000053,
  "days": [
    {
      "date":        "2026-06-23",
      "total_jumps": 38,   // SUM of all hourly jump readings that day
      "total_kills": 5,
      "avg_score":   3.1,
      "max_jumps":   8,
      "max_kills":   2
    }
  ]
}
total_jumps is the sum of every hourly reading for that UTC day — not a 24-hour true total, since the ESI itself provides rolling 1-hour windows. It is comparable to avg_daily_jumps_30d on the activity endpoint.

Gate Camping

GET /systems/gatecamping Systems ranked by bottleneck score

Returns systems ranked by betweenness centrality (bottleneck score) — a measure of how many shortest paths in the k-space graph pass through each system. Higher = more traffic must cross this system. Topology data refreshes every 24 hours using Brandes' algorithm on ~5,200 k-space systems.

Query Parameters
NameTypeDefaultDescription
minBottleneck float 0 Minimum bottleneck score
topologyType string dead-end, pipe, junction, or hub
secTransition boolean false Only systems that border a security band change (high↔low, low↔null)
securityMin float -1.0 Minimum security status
securityMax float 1.0 Maximum security status
region string Filter to a region by name
minJumps integer 0 Minimum live jumps in the last hour
limit integer 50 Max results to return
Response shape (one system)
{
  "systems": [
    {
      "system_id":        30000142,
      "system_name":      "Jita",
      "security_status":  0.946,
      "region":           "The Forge",
      "jumps":            4821,
      "ship_kills":       3,
      "bottleneck_score": 18432.7,
      "topology_type":   "hub",
      "gate_count":       6,
      "is_regional_gate": true,
      "connected_systems": [
        { "name": "Perimeter", "security": 0.943 }
      ],
      "security_transitions": [],
      "region_transitions":   []
    }
  ],
  "total": 4893
}
GET /topology/:systemId Single system topology

Returns full topology data for one system: gate connections, bottleneck score, security/region transitions, and Thera wormhole access.

Response
// GET /api/topology/30000053
{
  "system_id":           30000053,
  "topology_type":       "dead-end",
  "gate_count":          1,
  "bottleneck_score":    0,
  "is_regional_gate":    false,
  "has_npc_station":     true,
  "reachable_via_wormhole": false,
  "connected_systems": [
    { "name": "NOL-M9", "security": -0.481 }
  ],
  "security_transitions": [],
  "region_transitions":   [],
  "last_updated": "2026-06-23T03:00:00.000Z"
}

Ore Scouting

GET /systems/orescouting High-sec anomaly scouting candidates

Returns high-sec systems scored and tiered as ore anomaly scouting candidates. Scoring combines stargate topology (border proximity to low-sec) with 30-day historical activity data — quieter systems score higher as lower-competition targets. ESI cannot detect live anomalies; these are candidates requiring in-game confirmation.

No live anomaly data. ESI does not expose probe scanner results, active anomaly presence, or remaining ore volume. All scoring is based on static topology and historical jump/kill activity.
Tiers
TierCriteriaBase Score
Tier 1 0.5 displayed security AND directly adjacent to a low-sec system +100
Tier 2 High-sec AND within 1 stargate jump of a Tier 1 system +30
Tier 3 All other high-sec systems 0
Score Modifiers
ConditionModifier
Security display 0.5+15
Security display 0.6+10
Has NPC station (logistics)+10
More than 2 high-sec neighbors (route chaining)+5
Consistency score ≥ 70% (consistently quiet 30d)+20
Consistency score ≥ 50%+10
Avg daily jumps ≤ 5 (very low traffic)+10
Avg daily jumps ≤ 20 (low traffic)+5
Activity score > 40 (moderate competition)−10
Activity score > 60 (high competition)−20
Hub topology or bottleneck score > 0.005 (major route)−20
Query Parameters
NameTypeDefaultDescription
tier integer Filter to a specific tier: 1, 2, or 3. Omit for all tiers.
region string Filter to a region by name (e.g. Sinq Laison)
constellation string Filter to a constellation by name
minScore integer 0 Exclude systems with a score below this threshold
npcStation string yes = only systems with an NPC station, no = only without. Omit for all.
sortBy string score score | jumps (avg/day 30d) | kills (avg/day 30d) | consistency | activity | security | hub (jumps to nearest trade hub)
sortDir string per sort asc or desc. Defaults to the sort's natural direction (score/consistency desc; jumps/kills/activity/security/hub asc). Ties break by score desc.
origin string Your current system (id or exact name, case-insensitive). Adds per-candidate jumps_from_origin, safe_jumps_from_origin (high-sec-only route; null if none exists) and route_crosses_lowsec, and enables sortBy=distance. Unknown system → 400.
routeMode string fastest Requires origin. fastest = fewest jumps (ties prefer less low-sec); safest = least low-sec exposure first, then fewest jumps (like EVE's Safer autopilot — still routes through low-sec when unavoidable). Controls which route the distance sort and maxJumpsFromOrigin refer to.
avoidLowsec boolean false Requires origin. Hard filter: excludes candidates with no all-high-sec route entirely.
maxJumpsFromOrigin integer 0 Requires origin. When > 0, exclude candidates farther than N jumps (uses high-sec-only distance if avoidLowsec is set).
excludeIncursions boolean false Exclude candidates in active Sansha incursion systems
limit integer 100 Max results (capped at 500)
Response shape (one system)
// GET /api/systems/orescouting?tier=1®ion=Sinq+Laison&limit=2
{
  "systems": [
    {
      "system_id":               30002718,
      "system_name":             "Ney",
      "security_status":         0.4712,   // raw
      "security_status_display": 0.5,       // ceil(raw*10)/10
      "region":                  "Sinq Laison",
      "constellation":           "Alselbosier",
      "tier":                    "Tier 1",
      "score":                   125,
      "candidate_reasons": [
        "0.5 high-sec adjacent to low-sec",
        "Security 0.5",
        "NPC station (logistics)"
      ],

      // Neighbor breakdown
      "lowsec_neighbors": [
        { "system_id": 30002719, "system_name": "Deltole", "security_status": 0.398 }
      ],
      "highsec_neighbors": [
        { "system_id": 30002717, "system_name": "Carirgnottin", "security_status": 0.614 }
      ],

      // Topology
      "topology_type":    "pipe",
      "gate_count":       2,
      "bottleneck_score": 48.2,
      "has_npc_station":  true,
      "hub_distance":     6,     // gate jumps to nearest trade hub

      // Activity (30-day historical)
      "consistency_score":    12,   // % of last 30 days that were quiet
      "quiet_days_30":         4,
      "avg_daily_jumps_30d":   284.1,
      "avg_daily_kills_30d":   1.2,
      "weighted_activity_score": 8.4,
      "activity_score":         14.2,
      "currently_quiet":         true,
      "quiet_until_hour":        17,   // UTC hour the quiet window ends
      "incursion_active":        false,

      // Only when ?origin= is provided
      "jumps_from_origin":        9,     // fastest route; null = unreachable
      "fastest_lowsec_jumps":     2,     // low-sec systems on the fastest route
      "safest_jumps_from_origin": 12,    // least-low-sec route length
      "safest_lowsec_jumps":      0,     // low-sec systems even the safest route must cross
      "safe_jumps_from_origin":   12,    // all-high-sec route; null = none exists
      "route_crosses_lowsec":     true   // fastest route uses low-sec gates
    }
  ],
  "total": 6,
  "tier_counts": {
    "Tier 1": 82,
    "Tier 2": 92,
    "Tier 3": 1072
  }
}

Pochven Market Seeding

GET /pochven/opportunities Activity-scored market seeding candidates

Returns market seeding opportunities for all 27 Pochven systems, ranked by opportunity_score. Each row represents one item in one system. Scoring combines ESI market data (Jita 4-4 reference price, local Pochven sell orders) with zKillboard activity (recent killmails, doctrine ship losses, observed item drops) to identify scarce, high-margin items in active systems.

Results are cached in PostgreSQL. On first call, or when the snapshot is older than maxAgeMinutes, a fresh build is triggered automatically (takes ~20–40 s while ESI and zKill are queried).

Structure market visibility is incomplete. Pochven markets operate on player-owned structures. ESI only exposes orders on structures with public market access. Local sell prices may be missing even when stock exists.
Scoring Components
ComponentWeightSource
activity_score demand proxy ESI jumps (28%) + PvP kills (34%) + NPC kills (22%) + zKill count (16%)
doctrine_relevance item fit Tag match against inferred doctrine (pvp/pve/dictor/cruiser…) + direct item loss count from zKill
scarcity_score supply gap max(0, 1 − local_orders / target_order_count) per category
margin_score profit Suggested sell vs Jita 4-4 sell; targets 35% markup, fallback 45% if no local orders
confidence_score data quality Additive bonus for having Jita reference price, zKill data, ESI activity, local orders
logistics_risk penalty Per-clade constant: Svarog 0.28, Perun 0.34, Veles 0.38
overstock_penalty penalty Applied when local orders exceed 2× the category target
Recommendations
CallThreshold
seed nowopportunity_score ≥ 9 AND confidence_score ≥ 0.55
watchopportunity_score ≥ 3.5 (below seed now threshold)
avoidopportunity_score < 3.5
Query Parameters
NameTypeDefaultDescription
system string Filter to a single Pochven system by name (case-insensitive, e.g. Skarkon)
category string Filter to an item category: ammo, consumables, drones, tackle, tank, scanning, propulsion, deployables, bubbles
recommendation string Filter by call: seed now, watch, or avoid
minScore number 0 Minimum opportunity_score (0–100)
limit integer 100 Max results (1–1000)
maxAgeMinutes integer 60 If the latest snapshot is older than this, a fresh build is triggered automatically (1–1440)
windowHours integer 24 zKillboard lookback window in hours when building a new snapshot (1–168)
force boolean false Pass true to force a fresh build regardless of snapshot age
Response shape (one opportunity)
// GET /api/pochven/opportunities?system=Senda&category=bubbles&limit=1
{
  "snapshot": {
    "id":                   1,
    "completed_at":         "2026-06-29T16:41:34.880Z",
    "status":               "success",
    "source_window_hours":  24,
    "item_count":           27,
    "system_count":         27
  },
  "opportunities": [
    {
      "system_id":             30020141,
      "system_name":           "Senda",
      "clade":                 "Krai Veles",
      "type_id":               41041,
      "item_name":             "Warp Disrupt Probe",
      "category":              "bubbles",
      "recommendation":        "seed now",
      "suggested_quantity":    277,

      // Pricing (null if no orders found)
      "jita_sell_price":       "87300",
      "jita_buy_price":        "76200",
      "local_sell_price":      null,     // no public sell orders in this system
      "local_order_count":     0,
      "suggested_sell_price":  "126585",
      "estimated_margin_pct":  45.0,

      // Scores (all 0–1 except opportunity_score which is 0–100)
      "opportunity_score":    42.30,
      "activity_score":       0.812,
      "scarcity_score":       1.000,
      "margin_score":         1.000,
      "doctrine_relevance":   0.640,
      "confidence_score":     0.900,
      "logistics_risk":       0.380,
      "overstock_penalty":    0.000,

      // Evidence (from ESI + zKillboard)
      "evidence": {
        "jumps":                214,
        "ship_kills":           8,
        "pod_kills":            3,
        "npc_kills":            1842,
        "zkill_killmails":      12,
        "matched_tags":         ["universal", "pvp", "dictor"],
        "observed_item_losses": 0,
        "local_sell_volume":    0,
        "sample_killmails":     [{ "killmail_id": 123456789, "zkb": {}, "killmail_time": "2026-06-29T14:22:00Z" }],
        "confidence_notes": [
          "Jita 4-4 sell reference found",
          "No Pochven local sell order observed",
          "Pochven structure-market visibility may be incomplete through public ESI"
        ]
      }
    }
  ],
  "total": 1,
  "systems": [
    { "system_id": 30020141, "name": "Senda", "clade": "Krai Veles" }
    /* … all 27 Pochven systems … */
  ],
  "scoring_config": {
    "desiredMarginPct": 35,
    "fallbackMarkupPct": 45,
    "logisticsRisk": { "Krai Svarog": 0.28, "Krai Perun": 0.34, "Krai Veles": 0.38 }
  }
}
POST /pochven/refresh Force a fresh market snapshot

Immediately triggers a new market data build, bypassing the snapshot age check. Hits ESI market endpoints for all 27 items and fetches zKillboard for all 27 systems. Expect 20–40 seconds. The new snapshot is stored in the database and returned.

Auth & limits: when the server sets POCHVEN_REFRESH_TOKEN, this endpoint requires a matching X-Refresh-Token header. It is rate-limited to 2 requests/hour, honours a 10-minute minimum rebuild interval (returns the existing snapshot if fresher), and dedupes concurrent rebuilds.

Request Body (JSON, all optional)
FieldTypeDefaultDescription
windowHours integer 24 zKillboard lookback window (1–168 hours)
limit integer 250 Max opportunities to persist (1–1000)
Response
{
  "snapshot_id": 2,
  "systems":     27,
  "items":       27,
  "opportunities": [ /* … full opportunity array … */ ]
}
GET /pochven/opportunities.csv CSV export of current opportunities

Returns the current opportunity snapshot as a downloadable CSV file. Accepts the same filter parameters as GET /pochven/opportunities (system, category, recommendation, minScore, limit). Content-Type is text/csv; browsers will prompt a download.

CSV columns
system, clade, item, category, recommendation, quantity,
jita_sell, local_sell, suggested_sell, margin_pct, score, confidence

Stats

GET /stats/collection Data collection statistics

Returns a 7-day rolling summary of hourly data collection runs: success rate, systems collected, and recent run history.

Response
{
  "total_runs":     168,
  "successful_runs": 166,
  "failed_runs":    2,
  "success_rate":   98.8,
  "avg_systems_collected": 4891,
  "recent_runs": [
    {
      "start_time":        "2026-06-23T04:00:00.000Z",
      "end_time":          "2026-06-23T04:27:14.000Z",
      "status":            "success",
      "systems_collected": 4893
    }
  ]
}