1. API Overview

The Guild Wars 2 API (Application Programming Interface) is a free, publicly accessible REST API maintained by ArenaNet that exposes game data in JSON format. It powers dozens of third-party tools including GW2Efficiency, Discretize, Snow Crows, and — most relevantly — MistIntel.

🌐 Base URL: https://api.guildwars2.com
📦 Format: JSON
🔐 Auth: API Key (optional for most WvW endpoints)
📄 Official Docs: GW2 Wiki API:2
ℹ️
No Key Required for Most WvW Data

The majority of WvW endpoints — matches, objectives, worlds, scores — are publicly accessible without authentication. An API key is only required for personal account data (your WvW rank, reward track progress, etc.).

2. Authentication & API Keys

API keys are generated in-game via Account → Application Keys. A key grants access to specific permission scopes based on what you select when creating it.

PermissionWhat it accessesNeeded for WvW?
accountBasic account info, world IDYes — to read your current world
progressionWvW rank, achievements, reward tracksYes — for personal progression data
pvpPvP stats — not WvWNo
guildsGuild roster, bank, upgradesOptional — for guild-specific tools
charactersCharacter builds, equipmentNot for WvW intel
walletCurrency amountsNo

Pass the key via the Authorization: Bearer {API_KEY} header or as an access_token query parameter:

// Header method (recommended) GET https://api.guildwars2.com/v2/account/wvw/pvp/stats Authorization: Bearer your-api-key-here // Query parameter method GET https://api.guildwars2.com/v2/account?access_token=your-api-key-here

3. Rate Limits

ArenaNet applies rate limiting to API requests to prevent abuse. Typical limits:

  • ~600 requests per minute per IP for unauthenticated requests
  • ~300 requests per minute per API key for authenticated requests
  • Exceeding limits returns HTTP 429 Too Many Requests
  • Cache responses aggressively — match data updates every ~5 minutes; no need to poll faster
⚠️
Respect the Rate Limits

Excessive API hammering can result in IP bans. For WvW match data, polling every 30–60 seconds is sufficient and well within limits. Use the If-None-Match ETag header to avoid re-downloading unchanged data.

4. WvW Endpoints

GET /v2/wvw/matches

Returns all current WvW matches. The primary endpoint for real-time tracking.

// Get all match IDs GET https://api.guildwars2.com/v2/wvw/matches // Get specific match data GET https://api.guildwars2.com/v2/wvw/matches?id=2-1 // Get match for a specific world GET https://api.guildwars2.com/v2/wvw/matches?world=2201

Key fields in response:

FieldTypeDescription
idstringMatch ID (e.g. "2-1")
start_timestring (ISO8601)Match start timestamp
end_timestring (ISO8601)Match end (reset) timestamp
worldsobjectWorld IDs for red/blue/green
mapsarrayPer-map objective ownership data
scoresobjectCurrent score per team
victorypointsobjectVP totals per team
skirmishesarraySkirmish score history

Sample response (abridged):

{ "id": "2-1", "start_time": "2025-06-06T18:00:00Z", "scores": { "red": 142850, "blue": 138210, "green": 119440 }, "worlds": { "red": 2201, "blue": 2202, "green": 2204 }, "maps": [ { "id": 38, "type": "Center", "objectives": [ { "id": "38-6", "type": "Castle", "owner": "Red", "last_flipped": "2025-06-07T14:22:00Z", "yaks_delivered": 18, "tier": 3 } ] } ] }

GET /v2/wvw/scores

Lightweight endpoint returning just the current scores for matches — useful if you only need score data without the full match object.

GET https://api.guildwars2.com/v2/wvw/scores?id=2-1

GET /v2/wvw/stats

Returns kill/death statistics for each team in a match.

GET https://api.guildwars2.com/v2/wvw/stats?id=2-1

GET /v2/wvw/objectives

Returns the static metadata (name, position, type, chat link) for WvW objectives. Use this to build a reference map of all objectives.

// List all objective IDs GET https://api.guildwars2.com/v2/wvw/objectives // Get specific objective by ID GET https://api.guildwars2.com/v2/wvw/objectives?id=38-6 // Get multiple objectives GET https://api.guildwars2.com/v2/wvw/objectives?ids=38-6,38-1,38-2

Sample response:

{ "id": "38-6", "name": "Stonemist Castle", "type": "Castle", "map_id": 38, "map_type": "Center", "coord": [10241, 14337], "chat_link": "[&DAUAAAA=]", "label_coord": [10241, 14337], "marker": "https://render.guildwars2.com/file/..." }

GET /v2/wvw/upgrades

Returns information about WvW objective upgrades — tiers, automatic upgrades, guild upgrades.

GET https://api.guildwars2.com/v2/wvw/upgrades?id=5

GET /v2/wvw/abilities

Returns all WvW Abilities (passive upgrades unlocked with WvW rank points): names, descriptions, costs, and tiers.

GET https://api.guildwars2.com/v2/wvw/abilities GET https://api.guildwars2.com/v2/wvw/abilities?ids=all

GET /v2/wvw/ranks

Returns WvW Rank information: rank tiers, titles, and XP thresholds.

GET https://api.guildwars2.com/v2/wvw/ranks?ids=all

GET /v2/wvw/rewardtracks

Lists all available WvW Reward Tracks including the Gift of Battle track and Skirmish tracks.

GET https://api.guildwars2.com/v2/wvw/rewardtracks

GET /v2/worlds

Returns world (server) names and IDs. Essential for mapping world IDs in match data to human-readable names.

GET https://api.guildwars2.com/v2/worlds?ids=all&lang=en // Returns: [{ "id": 2201, "name": "Jade Quarry", "population": "Full" }, ...]

GET /v2/account (with WvW data)

Requires API key with account permission. Returns your account's current WvW world ID.

GET https://api.guildwars2.com/v2/account // Response includes: "world": 2201 (your current server ID)

GET /v2/guild/:id/teams

Requires guild API key. Returns PvP team data — note this is PvP not WvW specific, but guild IDs from WvW claims can be resolved here.

5. Key Response Schemas

Match Map Object

{ "id": 38, // Map ID "type": "Center", // Center | RedHome | BlueHome | GreenHome "scores": { "red": 42000, "blue": 38000, "green": 30000 }, "bonuses": [ { "type": "Bloodlust", "owner": "Red" } ], "objectives": [ /* array of objective objects */ ] }

Objective Object (inside match)

{ "id": "38-6", "type": "Castle", // Castle | Keep | Tower | Camp | Ruins | Mercenary | Spawn "owner": "Red", // Red | Blue | Green | Neutral "last_flipped": "2025-06-07T14:22:00Z", "claimed_by": "guild-id-here", // null if unclaimed "claimed_at": "2025-06-07T14:30:00Z", "points_tick": 35, "points_capture": 6, "yaks_delivered": 18, // supply delivered this match "tier": 3, // 1-3 upgrade tier "guild_upgrades": [52, 68] // active guild upgrade IDs }

6. Live Fetch Examples (JavaScript)

These examples show how to query the WvW API from a browser or Node.js environment.

Get Current Match for a World

async function getMatchForWorld(worldId) { const url = `https://api.guildwars2.com/v2/wvw/matches?world=${worldId}`; const response = await fetch(url); if (!response.ok) throw new Error(response.statusText); return response.json(); } // Usage: getMatchForWorld(2201).then(match => { console.log(`Match: ${match.id}`); console.log(`Scores:`, match.scores); });

Get All Objective Names Map

async function buildObjectiveNameMap() { const ids = await fetch("https://api.guildwars2.com/v2/wvw/objectives") .then(r => r.json()); const details = await fetch( `https://api.guildwars2.com/v2/wvw/objectives?ids=${ids.join(",")}` ).then(r => r.json()); return Object.fromEntries( details.map(obj => [obj.id, obj.name]) ); }

Detect Flipped Objectives (Diff)

function detectFlips(previousMatch, currentMatch) { const flips = []; for (const map of currentMatch.maps) { const prevMap = previousMatch.maps.find(m => m.id === map.id); for (const obj of map.objectives) { const prev = prevMap?.objectives.find(o => o.id === obj.id); if (prev && prev.owner !== obj.owner) { flips.push({ id: obj.id, from: prev.owner, to: obj.owner, time: obj.last_flipped }); } } } return flips; }

7. Polling Strategy

WvW match data updates server-side approximately every 5 minutes (aligned to the PPT tick). A good polling strategy:

  1. Poll every 30–60 seconds — frequent enough to detect flips quickly without hammering the API.
  2. Use ETag / If-None-Match — if data has not changed, the API returns HTTP 304 with no body, saving bandwidth.
  3. Cache objective metadata — the /v2/wvw/objectives static data changes rarely; cache it for the session.
  4. Cache world names — the /v2/worlds data is static between game updates.
  5. Back off on errors — if you receive 429 or 5xx, wait before retrying.
let etag = null; async function pollMatch(matchId) { const headers = {}; if (etag) headers["If-None-Match"] = etag; const res = await fetch( `https://api.guildwars2.com/v2/wvw/matches?id=${matchId}`, { headers } ); if (res.status === 304) return null; // no change etag = res.headers.get("ETag"); return res.json(); } setInterval(async () => { const data = await pollMatch("2-1"); if (data) processMatchUpdate(data); }, 30000); // poll every 30s

8. How MistIntel Uses the API

MistIntel is built entirely on the GW2 API and processes it through a real-time pipeline:

  1. Server-side polling of /v2/wvw/matches every 30 seconds for all active matches.
  2. Diff detection — comparing current vs previous objective ownership to detect flips in real time.
  3. WebSocket push — flip events are pushed instantly to all connected browser clients.
  4. Score projection — current PPT is calculated from live objective ownership and projected forward to estimate end-of-skirmish scores.
  5. Map rendering — objective coordinates from /v2/wvw/objectives are overlaid on map tile imagery to show live ownership.
🔌
Build Your Own Tool

The GW2 API is free, open, and well-documented. Whether you want a simple Discord bot that posts flips or a full-scale tracking dashboard, the WvW API provides everything you need. The community-maintained API:2 wiki page is the authoritative reference.

See the API in Action

MistIntel processes the GW2 WvW API in real time — live map ownership, flip alerts, PPT projections, and more.

Open MistIntel →