Migration Guide: v1 → v2
TL;DR
Five things to change, in order of priority:
| # | What | v1 | v2 |
|---|---|---|---|
| 1 | Auth | API key in URL /{apikey}/... | Header: x-api-key: YOUR_KEY |
| 2 | URLs | /{apikey}/{mode}/endpoint | /v2/endpoint |
| 3 | Refresh | live mode in path | ?refresh=true query param |
| 4 | Errors | Always HTTP 200, check success === 0 | Real HTTP status codes (401, 404, 429…) |
| 5 | json_path | Supported | Removed — filter client-side |
If you're in a hurry, jump straight to the checklist or the before/after examples.
Recommended Migration Order
If you can't migrate everything at once:
Phase 1 — Authentication
Everything else depends on it.
- Move the API key from the URL to the
x-api-keyheader - Update error handling to check HTTP status codes instead of
success === 0
Phase 2 — URL paths (can iterate)
Old paths stay working via legacy middleware — temporarily. Migrate one endpoint at a time.
/{apikey}/{mode}/endpoint→/v2/endpoint- Replace
livewith?refresh=true, drop everything else
Phase 3 — Response parsing (safe to defer)
success === 1→success === true(or justif (response.success)— works for both)- Handle 400 / 401 / 403 / 404 / 429 / 500 properly
Phase 4 — Optional polish
- Add
If-None-Match/ ETag support to reduce bandwidth on polling - Use
updatedAt/expiresAtto drive smarter cache invalidation
Migration Checklist
[ ] Move API key from URL to x-api-key header
[ ] Update request URLs: /{apikey}/{mode}/... → /v2/...
[ ] Replace {mode} path segment with ?refresh=true, or drop it entirely
[ ] Update success checks: success 1/0 → true/false
[ ] Update error handling: check HTTP status codes, not the success field
[ ] Remove json_path query params — filter client-side
[ ] Update progress-tracker path: /progress-tracker → /progress-tracker/teams
[ ] Update starmap routes to new sub-paths (see endpoint reference below)
[ ] Handle HTTP 429 (new rate-limit status)
[ ] Remove If-Match header usage (no longer supported)
Quick Before/After Examples
- Auth
- Ships (cached)
- Account info
- Error handling (JS)
- Success check (JS)
# v1
GET /myapikey1234/live/user/SomeHandle
# v2
GET /v2/users/SomeHandle?refresh=true
x-api-key: myapikey1234
# v1
GET /myapikey1234/cache/ships?name=Hornet
# v2
GET /v2/ships?name=Hornet
x-api-key: myapikey1234
# v1
GET /myapikey1234/me
# v2
GET /v2/me
x-api-key: myapikey1234
// v1 — always HTTP 200, check body
const res = await fetch(url);
const body = await res.json();
if (body.success === 0) throw new Error(body.message);
// v2 — check HTTP status
const res = await fetch('/v2/users/SomeHandle', {
headers: { 'x-api-key': key },
});
if (!res.ok) {
const body = await res.json();
throw new Error(`${body.code}: ${body.message}`);
}
const body = await res.json();
// v1
if (response.success === 1) { ... }
// v2
if (response.success === true) { ... }
// Safe for both v1 and v2
if (response.success) { ... }
Detailed Reference
1. Authentication
2. Base URL & path structure
v1 pattern
/{apikey}/{mode}/{endpoint}
/{apikey}/v1/{mode}/{endpoint} ← optional v1 prefix, identical behaviour
v2 pattern
/v2/{endpoint}
All v1 paths are automatically rewritten to v2 by the legacy compatibility middleware — but only temporarily. Migrate explicitly to avoid breakage when legacy support is removed.
3. Fetch mode → ?refresh parameter
v1 exposed a {mode} path segment controlling cache behaviour. v2 replaces this with a single boolean query parameter.
| v1 mode | v2 equivalent | Description |
|---|---|---|
live | ?refresh=true | Scrape RSI in real time. Costs quota. |
cache | ?refresh=false (default) | Return cached data. Free. |
auto | ?refresh=false (default) | Server auto-refreshes behind the scenes |
eager | ?refresh=true | Force live scrape — same as ?refresh=true |
In v2, the API manages cache freshness automatically server-side. Clients simply omit ?refresh for cached data, or pass ?refresh=true when live data is needed.
Only ?refresh=true consumes your daily quota. Omitting ?refresh or setting ?refresh=false is always free.
4. Endpoint URL mapping
Users
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/user/{handle} | GET /v2/users/{handle} |
| (no separate endpoint) | GET /v2/users/{handle}/profile |
| (no separate endpoint) | GET /v2/users/{handle}/organization |
Organizations
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/organization/{sid} | GET /v2/organizations/{sid} |
GET /{apikey}/{mode}/organization_members/{sid} | GET /v2/organizations/searchMembers?sid={sid} |
| (no v1 endpoint) | GET /v2/organizations/search?... |
Ships
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/ships | GET /v2/ships |
Query parameters are largely compatible. page and page_max are preserved.
Versions
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/versions | GET /v2/versions |
GET /{apikey}/{mode}/versions?filter=latest | GET /v2/versions?filter=latest |
| (no v1 endpoint) | GET /v2/versions?version=X.Y.Z |
Roadmap
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/roadmap/{board} | GET /v2/roadmap/{board} |
board values unchanged: starcitizen or squadron42.
date_min, date_max, version query params are preserved.
Progress Tracker
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/progress-tracker | GET /v2/progress-tracker/teams |
GET /{apikey}/{mode}/progress-tracker/{team_slug} | GET /v2/progress-tracker/deliverables/{slug} |
/progress-tracker → /progress-tracker/teams — the path suffix is required in v2.
Stats
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/stats | GET /v2/stats |
New v2 query param: ?chart=hour|day|week|month.
Telemetry
| v1 | v2 |
|---|---|
GET /{apikey}/{mode}/telemetry/{version} | GET /v2/telemetry/{version} |
timetable query param (DAY, WEEK, MONTH) is preserved.
Starmap
v1 used a single /{field} segment. v2 splits this into dedicated sub-routes.
| v1 | v2 |
|---|---|
starmap/search?name=X | GET /v2/starmap/search?q=X |
starmap/systems | GET /v2/starmap/systems |
starmap/star-system?code=X | GET /v2/starmap/star-systems/{code} |
starmap/object?code=X | GET /v2/starmap/systems/{code} |
starmap/affiliations | GET /v2/starmap/affiliations |
starmap/species | GET /v2/starmap/species |
starmap/tunnels | GET /v2/starmap/tunnels |
New v2-only endpoint: GET /v2/starmap/routes?from=SYS1&to=SYS2&ship_size=small
Me (API key info)
| v1 | v2 |
|---|---|
GET /{apikey}/me | GET /v2/me with x-api-key header |
5. Response format
Success response
// v1
{
"success": 1,
"message": "ok",
"data": { ... },
"source": "live"
}
// v2
{
"success": true,
"message": "ok",
"data": { ... },
"updatedAt": "2024-01-15T12:00:00.000Z",
"expiresAt": "2024-01-15T13:00:00.000Z"
}
successis now a boolean (true/false), not an integer (1/0)sourceis removed — replaced byupdatedAt(when data was scraped) andexpiresAt(when cache expires)- Both fields are ISO 8601 strings or
null
Error response
// v1 — HTTP 200, success: 0
{
"success": 0,
"message": "Apikey is not correct.",
"data": null,
"source": null
}
// v2 — HTTP 401
{
"success": false,
"message": "Unauthorized",
"code": 401
}
- v2 uses proper HTTP status codes — do not rely on HTTP 200 for all responses
- Error body uses
code(integer) instead ofdata: null+source: null
HTTP status codes
| Code | Meaning |
|---|---|
200 | Success |
304 | Not Modified — If-None-Match matched ETag (see §8) |
400 | Bad Request — invalid query params |
401 | Unauthorized — missing or invalid API key |
403 | Forbidden — account blocked or insufficient permissions |
404 | Not Found |
429 | Too Many Requests — quota or scrape rate limit exceeded |
500 | Internal Server Error |
6. Removed features
json_path query parameter
v1 supported ?json_path=$.data.some.path on all endpoints.
v2 does not support json_path. Filter client-side instead.
// v1
GET /apikey/live/ships?json_path=$.data[0].name
// v2 — simple case
const { data } = await res.json();
const name = data[0].name;
// v2 — complex filtering (use a library like jsonpath-plus)
import { JSONPath } from 'jsonpath-plus';
const names = JSONPath({ path: '$.data[?(@.size=="large")].name', json: body });
Gamedata endpoints
/gamedata/... routes were deprecated in v1 and are not present in v2.
7. Rate limiting changes
| v1 | v2 | |
|---|---|---|
| Quota tracked | Per live mode request | Per ?refresh=true request |
| Quota exceeded | HTTP 200, "success": 0 | HTTP 429 |
| Global scrape limit | None | 60 scrapes/minute (shared, Redis-backed) |
| Privileged accounts | Skip quota checks | Same (handled server-side) |
When you receive HTTP 429, back off and retry after the current minute window resets.
8. Conditional request headers
ETag support is preserved but semantics changed slightly.
| Header | v1 | v2 |
|---|---|---|
ETag | Returned on success | Returned on success |
If-None-Match | Supported → 304 | Supported → 304 |
If-Match | Supported → 412 on mismatch | Not supported |
Last-Modified | Not returned | Returned |
Expires | Not returned | Returned |
Send If-None-Match with the ETag from a previous response — if the data hasn't changed, the server returns 304 Not Modified with no body. Useful for polling without re-downloading unchanged data.
9. New features in v2
These endpoints did not exist in v1:
| Endpoint | Description |
|---|---|
GET /v2/me/profile | Extended profile including block status |
DELETE /v2/me | Soft-delete your account — preserves historical data |
DELETE /v2/me/unregister | Full unregister — removes all personal data |
POST /auth/renew | Rotate your API key without visiting the dashboard |
GET /v2/starmap/routes | Route search between star systems |
GET /health/check | Public health check (MongoDB + Redis) |
GET /status | Quota status and cache refresh info |
GET /swagger | Interactive API explorer (Swagger UI) |
All responses now include updatedAt and expiresAt — use these to drive smarter cache invalidation instead of guessing TTLs.
Need help? Join the community Discord server.