Migration Guide: v1 → v2
:::info Who is this page for?
Existing v1 users — you call URLs like /{apikey}/live/user/... today.
New here? You don't need this page. Start with Get your key and Make your first request, then use the API Reference. :::
:::warning v1 is deprecated
v1 still works, as a compatibility layer on top of the v2 services. Every v1 route is flagged as deprecated in the v1 Swagger (/v1/swagger) and will be removed in a future release. Migrate when you can, one endpoint at a time.
:::
What changes for you
| # | What | v1 | v2 |
|---|---|---|---|
| 1 | Errors | success: 1 / error body | Real HTTP status codes (401, 404, 429…) and success: true |
| 2 | Auth | API key in URL /{apikey}/... | Header: x-api-key: YOUR_KEY |
| 3 | URLs | /{apikey}/v1/{mode}/endpoint | /v2/endpoint |
| 4 | Refresh | live / eager mode in path | ?refresh=true query param |
| 5 | Parameters & payloads | Per-endpoint v1 params and re-mapped payloads | Reworked per endpoint — see the endpoint mapping |
| 6 | json_path | Documented in the old API | Not supported — filter client-side |
How v1 works today
Knowing this helps you decide what to migrate first.
- Paths:
/{apikey}/v1/{mode}/{endpoint}. The/v1segment is optional./metakes no{mode}. - Same backend: the
{apikey}segment is turned into thex-api-keyheader internally, and v1 uses the same cache and quotas as v2. - Legacy body:
success: 1,source, noupdatedAt/expiresAt. - Real errors: v1 no longer returns HTTP 200 for errors. Failures return a real status code with the v2 error body, so clients that only check
success === 0will miss them. - Gone:
json_pathis not supported, and/gamedata/...answers410 Gone.
Migration steps
Do the steps in order. Step 1 is worth doing even if you stay on v1 for now.
Step 1 — Fix error handling
v1 paths already return real HTTP status codes. Check the status, not success === 0.
const res = await fetch(url);
if (!res.ok) {
const body = await res.json(); // { success: false, message, code }
throw new Error(`${body.code}: ${body.message}`);
}
[ ] Check res.ok / the HTTP status code instead of success === 0
[ ] Handle 401, 403, 404, 429 and 500
[ ] Read the error body as { success: false, message, code }
Details: Errors and status codes.
Step 2 — Move the API key to a header
# v1
GET /myapikey1234/v1/live/user/SomeHandle
# v2
GET /v2/users/SomeHandle
x-api-key: myapikey1234
[ ] Remove the API key from the URL
[ ] Send x-api-key on every request
Keys must match [A-Za-z0-9_]{32,68}. Details: Authentication.
Step 3 — Update URLs and the fetch mode
Replace the prefix and translate the mode. You can migrate one endpoint at a time.
| v1 mode | v2 |
|---|---|
live, eager | ?refresh=true (costs 1 daily request, 10 for ships) |
cache, auto | omit ?refresh (always free) |
[ ] /{apikey}/v1/{mode}/... → /v2/...
[ ] live / eager → ?refresh=true; cache / auto → no parameter
[ ] Look up each endpoint in the mapping page (paths change, not just the prefix)
[ ] Stop calling /gamedata/... (410 Gone)
Details: Fetch mode and the endpoint mapping.
Step 4 — Adapt parameters and payloads
Some v1 endpoints were combined or re-shaped by the compatibility layer. Check the ones you use:
[ ] Users: /user/:handle → /users/:handle (or /profile + /organization)
[ ] Organization members: rank / role / main_org → rankIndex / roleIndex / isMainOrg; pageSize is new (default 10)
[ ] Stats: /stats → /funding-goals (funds are now in cents)
[ ] Versions: filter=latest → read data.latest; the response is { latest, versions }
[ ] Progress tracker: /progress-tracker → /progress-tracker/teams
[ ] Progress tracker: /progress-tracker/:team_slug → /progress-tracker/deliverables/by-team-slug/:slug
[ ] Roadmap: apply date_min / date_max / version filters client-side
[ ] Telemetry: always send ?timetable= (no default in v2)
[ ] Ships: classification takes a single value; id filter, page and page_max are removed
[ ] Starmap: ?name= / ?code= / ?id= lookups become path-based routes
[ ] Starmap search: ?name= → ?query=
[ ] Remove json_path — filter client-side
Details per resource: Endpoint mapping.
Step 5 — Parse the v2 response
// v1
if (response.success === 1) { ... }
// v2
if (response.success === true) { ... }
// Safe for both versions
if (response.success) { ... }
[ ] success is now a boolean (true / false)
[ ] Stop reading `source`; use updatedAt / expiresAt
[ ] Re-check every field you read — v2 returns raw models, v1 re-mapped many of them
Details: Response format.
Step 6 — Optional polish
[ ] Send If-None-Match with the previous ETag to get 304 instead of re-downloading
[ ] Use updatedAt / expiresAt to drive cache invalidation
[ ] Back off on HTTP 429 and retry after the minute window resets
Details: Conditional headers and Rate limits.
Before / after examples
- Ships (cached)
- Live user lookup
- Account info
- Starmap lookup
- Error handling (JS)
# v1
GET /myapikey1234/v1/cache/ships?name=Hornet
# v2
GET /v2/ships?name=Hornet
x-api-key: myapikey1234
# v1
GET /myapikey1234/v1/live/user/SomeHandle
# v2
GET /v2/users/SomeHandle?refresh=true
x-api-key: myapikey1234
# v1 (no mode segment)
GET /myapikey1234/v1/me
# v2
GET /v2/me
x-api-key: myapikey1234
# v1
GET /myapikey1234/v1/cache/starmap/object?code=STANTON.PLANETS.STANTON2
# v2 — system code, then full object code
GET /v2/starmap/objects/STANTON/by-code/STANTON.PLANETS.STANTON2
x-api-key: myapikey1234
// v1 — checking only the body is no longer enough
const res = await fetch(url);
const body = await res.json();
if (body.success === 0) throw new Error(body.message);
// v1 and v2 — check the 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();
Where to go next
| I want to… | Go to |
|---|---|
| Find the v2 equivalent of a v1 endpoint | Endpoint mapping |
| Understand a behaviour change (auth, errors, rate limits, headers) | Behaviour changes |
| See what v2 adds that v1 never had | v2-only endpoints |
| Debug a failing request | Troubleshooting |
Need help? Join the community Discord server.