FA Navy

HD2Clans Hangar API Spec

Endpoints

Base URL https://hd2clans.com/api/public

GET /clan/<clan_id>/hangar one clan
GET /clan/hangars?ids=334,597,1404 up to 25 clans

No authentication. CORS is open, so you can call it straight from a browser. Use the batch form if you're watching more than one clan.

Quick start

import requests

CLANS = [334, 597, 1404, 168, 1381, 1396, 1414, 483,
         1325, 395, 179, 1341, 1719, 1388, 902]

r = requests.get(
    "https://hd2clans.com/api/public/clan/hangars",
    params={"ids": ",".join(map(str, CLANS))},
    headers={"User-Agent": "YourAppName/1.0 (+contact: yourdiscord)"},
    timeout=30,
)
r.raise_for_status()

for hangar in r.json()["hangars"]:
    if not hangar["available"]:
        continue
    print(hangar["clan"]["name"], hangar["counts"]["total"], "ships")
    for ship in hangar["ships"]:
        print(" ", ship["name"], ship["hull"]["label"],
              ship["condition"]["label"])

Results come back in the order you asked for them. Every entry carries clan_id at the top level whether it resolved or not, so you can match responses to requests without relying on position.

Response

Top level

FieldTypeNotes
availableboolFalse means the clan can't be shown right now. See Unavailable clans.
clan_idintAlways present, on both success and failure.
clanobjectid, name, motto, hangar_description, logo_url
countsobjectalliance_ships, personal_ships, total, plus personal_deployed / personal_anchored / personal_drydocked
readinessobjectkey and label: Standby, Routine, Active, Elevated, Maximum. Driven by how many personal ships are out.
fleet_statusobjectFleet-wide rollup for drawing a status bar. Null for an empty fleet. Read fleet_status before you sum anything in it.
battle_groupsarrayThe clan's formations: id, name, tagline, logo_url, ship_count, commodores[]. Empty array when the clan has none. See Battle Groups.
shipsarrayAlliance ships and auxiliaries.
personal_shipsarrayMembers' own ships. Same object shape, sorted by owner name.
crewobjectleaders, shipmasters, commodores, captains, first_officers
activityarrayLast 10 fleet events.
cache_secondsintHow long a response stays warm. Currently 60.

Ship object

FieldTypeNotes
idintStable. Detail page is /hangar/ship/<id>.
namestringThe ship's name.
ship_classstringDisplay name, e.g. "Super Destroyer".
class_keystringSlug form. Null for auxiliary ships, which have no class.
origin_typestringalliance or auxiliary.
is_personalboolTrue for a member's own ship (Super Destroyer).
owner_usernamestringSet on personal ships (SDs), null on alliance ships.
battle_groupobjectid and name of the formation this ship serves in, or null. Always null on personal ships, which cannot join one.
levelintShip level.
level_colorstringHex color we use for the level badge.
xpintTotal XP.
hullobjectsegments (1 to 5), max_segments, label. See below.
conditionobjectkey, label, location, forced. See Hull Breaches for what forced means.
progress_pctfloatHow far through a deployment or repair, 0 to 100. Null when nothing is running.
image_urlstringCDN thumbnail, or null.
awarded_atstringWhen the clan got the ship.
awarded_for_opcodestringOperation it was awarded for, or null.

Condition keys

anchored, preparing_deploy, preparing_dss, deployed, escorting_dss, drydocked, reserve. You may also see supporting_operation on older ships; nothing new enters that state. Match on key, not label. Labels are display copy and can be reworded.

Those are the keys a ship reports. The condition_key inside fleet_status.by_condition is a different, smaller vocabulary: it carries preparing, which no individual ship ever reports, and it never carries reserve. Don't build one lookup table and use it for both.

fleet_status

This object exists to draw the status bar players see on the clan's hangar page, so it is shaped for that job rather than for arithmetic.

FieldTypeNotes
total_shipsintAlliance ships and auxiliaries. Matches counts.alliance_ships.
total_sdsintPersonal Super Destroyers. Matches counts.personal_ships.
total_allinttotal_ships + total_sds. The clan's whole fleet.
reserved_countintShips in reserve, i.e. anchored more than 7 days. Still fully operational.
active_totalinttotal_all minus reserved_count. This is what by_condition adds up to.
operational_pctintPercentage of active_total that is not drydocked. Not a percentage of the whole fleet.
by_conditionarraycondition, condition_key, count, color. Display order, Reserve excluded, prep states merged.
by_condition is not your fleet total

Two things are folded into it before you see it, and both will quietly cost you ships if you sum the array expecting a headcount.

Reserve is absent. Ships anchored more than seven days are pulled out so the bar reflects the active fleet, and they are large in number on established clans. One real example at the time of writing: a clan with 168 ships and 98 in reserve publishes a by_condition summing to 70. Add reserved_count if you want the fleet, or just use total_all.

The two prep states are merged. preparing_deploy and preparing_dss arrive as a single preparing segment. That key appears nowhere else in the API, so a colour or label map built from the ship condition keys above will meet a key it does not know.

If you want counts you control, group the ships and personal_ships arrays yourself on condition.key. Everything in fleet_status is derivable from them, and then the vocabulary is the one this page documents.

Hull

Read this before you build a health bar

Hull is published as filled segments out of five, the same bar players see on the site. There is no raw percentage in this API and there won't be one.

Combat Readysegments: 5
Operationalsegments: 4
Battle Wornsegments: 3
Compromisedsegments: 2
Criticalsegments: 1

Segments never reach zero. A wrecked ship reads 1, "Critical".

Hull Breaches

Checking the condition key is not enough

A breached ship and a ship its leadership sent for routine repairs both read "key": "drydocked". Hull segments won't separate them either, because a breach can fire without the ship crossing a segment boundary. There are two reliable signals instead.

1. The forced flag, for current state

Every ship's condition object carries forced. It is true only when a hull breach put the ship where it is, and false for every voluntary drydock.

"condition": {
  "key": "drydocked",
  "label": "Drydocked",
  "location": "Freedom Alliance Orbital Hub",
  "forced": true
}

Use this when you poll on a slow cycle, or when you want to show which ships are currently out of action through damage rather than by choice. It stays true for as long as the emergency repair lasts, so you cannot miss it.

2. The activity feed, for the moment it happened

A breach writes a distinct event with event_type of hull_breach. Watch for it if you want to announce breaches as they occur rather than report standing state.

for event in hangar["activity"]:
    if event["event_type"] == "hull_breach":
        print(event["ship_name"], "breached at", event["location"])

Bear in mind the feed only carries the last 10 events for the clan. A busy clan during an operation can push a breach out of that window between two polls, which is exactly why the forced flag exists alongside it. If you want both reliability and timeliness, watch the flag for state and the feed for the announcement.

Battle Groups

A Battle Group is a named formation of a clan's ships that its leadership commands as one order. Clans use them to organise a fleet into task forces. The formation list arrives at the top level, and every ship tells you which one it belongs to, so you can present a clan's fleet grouped the way the clan itself groups it.

FieldTypeNotes
idintStable. Page is /hangar/battlegroup/<id>.
namestringThe formation's name. Unique within a clan.
taglinestringShort subtitle, or null.
logo_urlstringCDN image, or null when the formation has no logo.
ship_countintMembers visible in this response. See the note below.
commodoresarrayStandard crew rows: username, public_id.
A roster tells you nothing about what those ships are doing

Membership is a label, not a state. Every member keeps its own condition, so a formation of six can have three ships deployed, one in drydock and two anchored at the same time. There is no formation-level status in this API and there won't be one, because there isn't one in the game either. If you want to know what a task force is up to, read the condition of each ship whose battle_group.id matches.

ship_count counts what you can see. It is derived from the same ships array this response carries, not from the clan's raw membership. A ship on a class we haven't published yet is withheld from the array, and it is left out of the count too, so the two always agree. Group the ships array yourself and you will get the same numbers.

Super Destroyers can never join a formation, so battle_group is always null across personal_ships. The key is still present on every ship in both arrays, so you can walk either one without branching.

groups = {g["id"]: g for g in hangar["battle_groups"]}
for ship in hangar["ships"]:
    bg = ship["battle_group"]
    if bg:
        print(bg["name"], ship["name"], ship["condition"]["label"])

Formations come and go. A disbanded Battle Group leaves the list immediately and its ships' battle_group goes null, so don't treat an id you saw yesterday as permanent. Key your own storage on the id while it is present, and drop it when it stops appearing.

Crew and Activity

Every crew list uses the same row shape: username and public_id. Captains and first officers add ship_id and ship_name; commodores add battle_group_id and battle_group_name. Profile links are /user/<public_id>.

Activity events carry ship_id, ship_name, class_display, event, event_type, condition_key, at (ISO 8601), location, location_kind, location_opcode, notes, and an actor of username plus role_label.

On identifiers

public_id is the only user identifier this API returns. Discord IDs are deliberately absent, and users who have been removed from public view won't appear in any list. Don't key your storage on username, because people rename. Key on public_id.

Unavailable Clans and Errors

A clan that can't be shown still returns HTTP 200 with available: false and a reason. This is on purpose: you can keep a clan in your poll list through a disbandment without special-casing an error.

ReasonMeaning
not_foundNo such clan.
unavailableThe clan is disbanded, suspended, or delisted. It may come back.
hangar_closedThe hangar feature is closed site-wide. Temporary; retry later.

Actual errors

Bad input returns HTTP 400 with an error code and a human-readable detail:

{"error": "invalid_id",   "detail": "Clan IDs must be integers; got 'abc'.",  "max_ids": 25}
{"error": "too_many_ids", "detail": "Requested 26 clans; the limit is 25 per call.", "max_ids": 25}
{"error": "missing_ids",  "detail": "Pass ?ids= with a comma-separated list of clan IDs.", "max_ids": 25}

Over-length lists are rejected.

Rules of the Road

This API is read-only and returns only data already public on the site. It may be changed, updated, or switched off at any time. We make no uptime guarantees.

Need help? Support Server | Business: [email protected]

Built for the Freedom Alliance | freedomalliance.gg

Full clan index: all clans A-Z

HD2Clans is a fan-made community project and is not affiliated with, endorsed by, or associated with Arrowhead Game Studios AB or Sony Interactive Entertainment. HELLDIVERS is a trademark of Sony Interactive Entertainment LLC. All game content and assets are property of their respective owners.