API documentation
v1Read-only AION 2 character data for community bots and tools.
Introduction
The API returns the public data of an AION 2 Global character. All requests use HTTPS and all responses are JSON. Access is granted per community.
https://aion2index.com/api/v1Request access with your Discord account. Once approved, your API key is created from your panel.
Authentication
Send your API key as a bearer token in the Authorization header of every request.
Authorization: Bearer <your-api-key>A key is shown once, when it is created. Keep it on your server and never put it in a public repository or a client application. If it leaks, replace it from your panel: the old key stops working at once.
Get a character
Path parameters
| Name | Type | Description |
|---|---|---|
region | string | nae NA East, naw NA West, eu Europe, la Latin America, as Asia. |
server | string | Server slug, in lower case, as in profile URLs: zikel. |
name | string | Character name, URL-encoded. Not case-sensitive. |
Example request
curl "https://aion2index.com/api/v1/characters/nae/zikel/ExampleDaeva" \
-H "Authorization: Bearer $AION2INDEX_API_KEY"Response
{
"characterId": "aBcD1234ExampleCharacterId00000000000000000=",
"name": "ExampleDaeva",
"region": "nae",
"regionName": "NA East",
"server": "Zikel",
"serverSlug": "zikel",
"faction": "asmodian",
"class": "Gladiator",
"level": 45,
"itemLevel": 2850,
"combatPower": 152340,
"legion": "Example Legion",
"title": { "name": "Saint of Altgard", "grade": "Unique" },
"profileUrl": "https://aion2index.com/c/nae/zikel/ExampleDaeva",
"legionUrl": "https://aion2index.com/legion/nae/zikel/Example%20Legion",
"updatedAt": "2026-10-30T18:00:00.000Z",
"fresh": true
}Fields
| Field | Type | Description |
|---|---|---|
characterId | string | Stable identifier. It survives renames, so store it to link a member to a character. |
name | string | Character name. |
region | string | Region code. |
regionName | string | Region name. |
server | string | Game server name. |
serverSlug | string | Game server slug, as used in the request path. |
faction | string | elyos or asmodian. |
class | string | null | Class name. |
level | integer | Character level. |
itemLevel | integer | null | Item level. |
combatPower | integer | null | Combat power. |
legion | string | null | Legion name. Null when the character has no legion. |
title | object | null | Displayed title, with its name and grade. Null when none is displayed. |
profileUrl | string | Character profile on AION2 Index. |
legionUrl | string | null | Legion page on AION2 Index. |
updatedAt | string | ISO 8601 time of the last read from the game. |
fresh | boolean | True when the data was read from the game within the last minute. |
Data freshness
Each request reads the character from the game, unless it was read within the last minute. Requests for the same character within that minute share the same reading.
If the game does not answer within 10 seconds, or during game maintenance, the response carries the saved data with fresh set to false. Check updatedAt to see how old it is.
Verifying members
For access decisions, only accept responses where fresh is true: a member may have changed legion since the saved reading. This example checks faction and legion.
const ALLOWED_LEGIONS = new Set(['Example Legion', 'Allied Legion']);
async function verifyMember(region, server, name) {
const url = `https://aion2index.com/api/v1/characters/${region}/${server}/${encodeURIComponent(name)}`;
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.AION2INDEX_API_KEY}` },
});
if (response.status === 404) return { ok: false, reason: 'Character not found' };
if (!response.ok) return { ok: false, reason: 'Try again shortly' };
const character = await response.json();
// Saved data may be out of date: never let a member in on it.
if (!character.fresh) return { ok: false, reason: 'Try again shortly' };
if (character.faction !== 'asmodian') return { ok: false, reason: 'Wrong faction' };
if (!ALLOWED_LEGIONS.has(character.legion)) return { ok: false, reason: 'Legion not allowed' };
return { ok: true, characterId: character.characterId };
}Proving ownership
Anyone can type a character name. To confirm that a member plays the character, ask them to display a title of your choice in game, wait a minute, and compare it with title.name.
Linking accounts
Store characterId with the Discord account. Unlike the name, it does not change when a character is renamed.
Rate limits
Each key has its own limit per minute, shown in your panel. Requests that pass authentication carry these headers:
| Header | Description |
|---|---|
x-ratelimit-limit | Requests allowed per minute for this key. |
x-ratelimit-remaining | Requests left in the current window. |
x-ratelimit-reset | Unix time, in seconds, when the window resets. |
retry-after | Seconds to wait. Only on 429 responses. |
Errors
Errors use standard HTTP status codes and share one format.
{
"error": {
"code": "not_found",
"message": "No such character."
}
}| Status | Code | Description |
|---|---|---|
| 401 | missing_key | The Authorization header is missing or malformed. |
| 401 | invalid_key | The API key does not exist. |
| 403 | key_disabled | The API key was disabled. |
| 404 | not_found | Unknown region, server or character, or a hidden character. |
| 429 | rate_limited | The key is over its limit. Wait for the time in retry-after. |
| 429 | upstream_unavailable | The game data is not answering and nothing is saved. Retry after the time in retry-after. |
