Two versions of the same explanation: site copy written for pilots, then the engineering breakdown with diagrams, taken from the code.
For the site
Most EVE tools ask CCP's servers for your data at the moment you click. EVE's API has strict rate limits and some answers take a while, so you sit and wait for the page.
EveSynapse works the other way around. A background process keeps a fresh copy of your characters, skills, assets, markets and corporation data, and refreshes each piece as soon as CCP says it has changed. When you open a page, it is built from that copy, so it loads right away.
It also looks ahead. The data closest to you, like the pilots you trade and talk with and the items in your hangars and industry jobs, is fetched before you ever click on it. As you type in search, the likely matches start loading before you pick one.
If you open something the copy does not have yet, like an item or a pilot nobody has looked up before, the page still loads. That one section shows as syncing, and EveSynapse moves it to the front of the line. It usually fills in on its own within a few seconds, without a refresh.
All of this runs on a fixed fetch budget that stays inside CCP's limits. If CCP's servers push back, EveSynapse slows down instead of retrying harder, and keeps showing you the last good copy in the meantime.
The result is pages that open instantly, data that stays current, and a tool that is a good citizen on CCP's servers.
EveSynapse builds every page from a live local copy of your EVE data, kept fresh in the background. Pages open instantly, and anything new jumps the queue and fills in within seconds.
Technical breakdown
The core rule is that a page render reads only from Postgres and in-process caches. Two background loops are the only code that talks to CCP's ESI API, and both spend from a shared, bounded budget. Anything a render could not resolve becomes a queue row (a "want") instead of a network call.
Snapshots. The minute worker walks every linked character, refreshing each ESI dataset whose cached_until has passed, stored as raw JSON keyed by character and kind. Most-overdue characters go first; fresh logins and Sync-page requests jump ahead.
Two-tier names. Item, place and pilot names render from the SDE tables and local caches only. A name still missing shows as Type #1234 until a warm-up pass fills it.
Wants. When a render meets something unresolved, notePageWant writes a queue row at "viewed" priority and registers it against the page, which drives the sync indicator in the top banner. Handlers only write queue rows; they never fetch.
Live regions. Sections rendered while pending carry a poll URL. The browser polls a fragment endpoint that also reads only stored rows, and swaps the section in once its state leaves pending.
Here is what happens when a pilot opens a market item nobody has viewed before, so its price history is not cached yet.
Most of the time this sequence never runs: proactive coverage (market coverage tiers, the pilot "orbit" around your characters) has usually fetched the data before anyone asks. The urgent path exists for the long tail, such as a pilot ID typed into search.
Every fetch competes for the same small budget, so the order matters more than the speed of any single call. EveSynapse ranks work by how close it is to the pilot: what is on screen, what they are about to open, what their own data touches, and only then everything else.
Viewed wants jump the queue, but every fetch still passes the same gates. A busy page cannot make EveSynapse spend more of ESI's error budget; it can only change what gets spent first.
The interactive market search may make a single name lookup for an item nobody has cached yet, so a typed search can resolve. Every other render stays cache-only, and the Briefing module's test suite asserts zero outbound calls.
Constants as they stand in the current source.
| Setting | Value | Where |
|---|---|---|
| Background worker cycle | 60 s | worker.go |
| ESI fetches per worker cycle | 120 | maxFetchesPerCycle |
| Urgent drain tick | 5 s | urgent.go |
| Urgent caps per tick: pilots / history / corp + alliance names / item details | 3 / 3 / 8 / 2 | urgent.go |
| Urgent drain hold after an ESI error limit | 2 min | urgentErrorBackoff |
| Retry after a missing in-game role (403) | 6 h | roleMissingBackoff |
| Snapshot freshness when ESI sends no Expires header | 5 min | README |
| Fragment polling, then slowing to | 1.5 s → 5 s | app.js |
| Sync indicator stops counting a want after | 3 min | pageWantTTL |
| Killmail details warmed per character per cycle | 10 | README |
| War details warmed per cycle | 50 | README |