| title | Game API |
|---|---|
| description | The endpoints the Krunker client reads without any authentication |
These are the endpoints the game itself calls without a developer key and without a player token: the social popup, the clan window, Turf Wars, the market tab, the shop, the matchmaker and the two legacy browsers. Nothing here is documented by Krunker. Every endpoint, parameter and field below was taken from the game's client code and confirmed with a plain, unauthenticated GET in October 2026. All of it can change without notice.
In krapio these live under krunker.game.*, grouped by resource. Everything is read only: every write the game makes (follows, posts, purchases, listings, trades, clan management) needs the player's own token and is not part of this library.
| Host | Used by | Reply shape |
|---|---|---|
https://gapi.svc.krunker.io |
everything unless noted | {"data": ..., "error": null}; an error is {"error": {"code", "message"}} on a 404 or {"error": "text"} on a 400 |
https://matchmaker.krunker.io |
matchmaker.* |
plain JSON, no envelope |
https://api.krunker.io |
legacy.* |
plain JSON, streams/top wraps in {"data"} |
krapio unwraps the envelope, you get data. A non-null error on a 2xx (never seen, but the game checks for it) throws KrunkerError.
Rate limits. gapi.svc.krunker.io sends X-RateLimit-Limit: 5, X-RateLimit-Remaining and X-RateLimit-Reset on every reply: a window of 5 requests per second per endpoint, with X-RateLimit-Reset as seconds until the window ends (not a unix timestamp like the developer API). krapio waits out an empty window instead of refusing, since it is over within a second. The matchmaker and api.krunker.io send no rate limit headers.
Names. Player and clan name lookups are case-insensitive. Names go url-encoded into the path.
The social profile popup and its tabs.
GET /players/:namekrunker.game.players.get(playerName)
The flat legacy profile the social popup renders. 404 PLAYERS_NOT_FOUND for an unknown name.
type PlayerSocialProfile = {
player_id: number;
player_name: string;
player_clan?: string; // absent without a clan
clan_id?: number;
player_twitchname: string;
player_region: number;
player_badge: string; // comma separated badge ids
player_titles: string; // comma separated title ids
player_datenew: string; // "YYYY-MM-DD HH:MM:SS"
player_featured: number;
player_funds: number; // kr
player_skinvalue: number;
player_elo: number;
player_elo2: number;
player_elo4: string; // junk rating, a decimal as text
player_premium: number;
player_kills: number;
player_deaths: number;
player_wins: number;
player_games_played: number;
player_score: number;
player_timeplayed: number; // seconds
player_following: number;
player_followed: number;
player_chal: number;
player_infected: number;
player_jobrating: number;
player_jobratingpositive: number;
player_calls: number;
player_taggedaccounts: number;
player_stats: PlayerStats;
online: number;
maps: unknown[];
mods: unknown[];
assets: unknown[];
match_history: MatchHistoryGame[]; // only ever seen empty, shape from the game code
ranked_rank: number;
raid_stats: RaidStats[];
};player_stats is a packed blob with short keys. The developer API's player profile has the same numbers under full names.
| Key | Meaning |
|---|---|
| s, h, hs, ls, wb | shots, hits, headshots, legshots, wallbangs |
| mk, fk, tmk | melee kills, fist kills (beatdowns), thrown weapon kills (bullseyes) |
| ast, n, ad, sad | assists, nukes, airdrops, airdrops stolen |
| sl, wm | slimes, warmachines |
| flg | country flag index |
| hds | 1 when the profile hides details |
| c0 to c15 | score per class |
raid_stats rows are {map_id, completions, attempts, best_time, ai_kills, boss_kills, deaths}.
GET /players/search?q=:query&limit=:limitkrunker.game.players.search(query, limit = 5)
Prefix match on the name, limit 1 to 20. Returns {player_id, player_name, player_score}[] (krapio unwraps the results key).
GET /players/:name/sales?range=:dayskrunker.game.players.sales(playerName, range = 3)
One row per day with market sales: {f, t, d}[] where f is kr earned, t items sold, d the day as YYYY-MM-DD. The popup sends range=3.
GET /players/:name/listingskrunker.game.players.listings(playerName)
Only ever answered []. The developer API's player listings endpoint has the real data.
GET /players/:name/followers
GET /players/:name/followingkrunker.game.players.followers(playerName), krunker.game.players.following(playerName)
{player_id, player_name, date, follow_state}[], 100 rows at most.
GET /social/feed?player_id=:id&limit=:limitkrunker.game.players.feed(playerId, limit = 20)
A player's posts, limit 1 to 50. krapio returns the posts array of FeedPost (see Social for the keys).
The in-game clan window and the recruitment board.
GET /clans/by-name/:namekrunker.game.clans.byName(clanName)
Case-insensitive. The roster carries no player ids.
type ClanPublicProfile = {
clan_id: number;
clan_name: string;
clan_score: number;
clan_funds: number;
clan_rank: number;
clan_link4: string; // discord invite code
clan_logo: number | null; // logo version, 0 or null without one
clan_banner_color: string; // "s<n>" = paint shader n, otherwise a color
creator_name: string;
members: ClanPublicMember[];
};
type ClanPublicMember = {
p: string; // name
s: number; // score
r: number; // role: 0 recruit, 1 soldier, 2 captain, 3 commander; the creator is listed with 0
h: number; // hacker flag
pr: number;
ve: number; // verified
su: number; // staff
s7: number; // score of the last 7 days
};The clan logo is at https://user-assets.krunker.io/clans/<clan_id>.png?v=<clan_logo>.
GET /clans/:id/profilekrunker.game.clans.profile(clanId)
The same reply as by name.
GET /clans/:id/loot-configkrunker.game.clans.lootConfig(clanId)
The clan's Turf Wars payout rule.
type ClanLootConfig = {
clan_id: number;
distribution_mode: "clan_bank" | "even" | "top_weighted" | "performance" | "top_performance";
clan_pct: number; // the treasury cut
top_player_count: number;
top_player_pct: number; // top_player_pct + remainder_pct = 100
remainder_pct: number;
min_games_required: number;
updated_at: string;
updated_by: number;
updated_by_name: string;
};GET /clans/:id/motdkrunker.game.clans.motd(clanId)
{message, set_by, set_by_name, set_at}.
GET /clans/:id/recruitmentkrunker.game.clans.recruitment(clanId)
The clan's own post on the recruitment board.
type RecruitmentPost = {
recruitment_id: number;
clan_id: number;
clan_name: string;
clan_logo: number | null;
description: string;
looking_for: string;
min_level: number;
min_kd: number;
region: string | null;
language: string | null;
is_active: number;
created_at: string;
updated_at: string;
created_by: number;
loot_mode: LootDistributionMode | null;
loot_clan_pct: number | null;
loot_top_player_count: number | null;
loot_top_player_pct: number | null;
loot_remainder_pct: number | null;
loot_min_games: number | null;
};GET /clans/recruitment?page=:page&limit=:limit®ion=:region&language=:languagekrunker.game.clans.recruitmentBoard({ page, limit, region, language })
| Name | Type | Required | Description |
|---|---|---|---|
| page | int | no | 1 based |
| limit | int | no | 1 to 50, the game sends 20 |
| region | string | no | the board's region select |
| language | string | no | the board's language select |
Returns {posts: RecruitmentPost[], total, page, limit}.
https://gapi.svc.krunker.io/territorial-wars/*. Days are whole UTC days since the season epoch 2026-03-17 (TURF_EPOCH_UTC, turfDay(date) in krapio). map_id is the Turf map: 0 Burg, 2 Sandstorm, 3 Subzero (TURF_MAPS). Leaderboards return 100 rows, kd is a float.
GET /territorial-wars/statuskrunker.game.turf.status()
Every Turf map with its current owner and the top 5 clans.
type TurfStatus = {
map_id: number;
map_name: string;
clan_id: number; // the owner
clan_name: string;
clan_logo: number | null;
kills: number;
games: number;
reward: number | null;
leaderboard: { position: number; clan_id: number; clan_name: string; clan_logo: number | null; kills: number; deaths: number }[];
total_kills_today: number;
guaranteed_reward: number;
estimated_reward: number;
};GET /territorial-wars/clan/:clanIdkrunker.game.turf.clan(clanId)
type TurfClan = {
clan_id: number;
clan_name: string;
clan_logo: number | null;
current_day: number;
maps: TurfClanMap[];
historical_days: Omit<TurfClanMap, "games">[];
};
type TurfClanMap = {
map_id: number;
map_name: string;
day: number;
kills: number;
deaths: number;
games: number;
position: number;
comparison_kills: number; // the clan one position up
comparison_deaths: number;
comparison_clan_name: string;
comparison_clan_id: number;
comparison_clan_logo: number | null;
comparison_position: number;
};GET /territorial-wars/clan/:clanId/players?day=:day&map_id=:mapIdkrunker.game.turf.clanPlayers(clanId, { day, map_id })
Today without day. Rows are {player_id, player_name, kills, deaths, games, kd}.
GET /territorial-wars/clan/:clanId/earningskrunker.game.turf.clanEarnings(clanId)
{day, map_id, map_name, reward}[].
GET /territorial-wars/clan/:clanId/payouts?day=:daykrunker.game.turf.clanPayouts(clanId, day?)
Season totals without day. {payouts: {player_id, player_name, total_kr}[], total}.
GET /territorial-wars/leaderboards/top-performing-clans?day=:day&map_id=:mapId&clan_name=:namekrunker.game.turf.topClans({ day, map_id, clan_name })
clan_name is an exact match filter. Rows: {position, clan_id, clan_name, clan_logo, kills, deaths, games, kd}.
GET /territorial-wars/leaderboards/top-grossing-clans?clan_name=:namekrunker.game.turf.topGrossing(clanName?)
Rows: {position, clan_id, clan_name, clan_logo, total_kr, distributed, bank}.
GET /territorial-wars/leaderboards/top-performing-players?map_id=:mapId&player_name=:namekrunker.game.turf.topPlayers({ map_id, player_name })
Slow, several seconds. Rows: {position, player_id, player_name, clan_id, clan_name, kills, deaths, games, kd, earnings}.
GET /territorial-wars/player/:playerIdkrunker.game.turf.player(playerId)
Also the way to resolve a stored player id to the current name.
{player_id, player_name, current_clan, current_clan_id, current_clan_logo, total_kills, total_deaths, total_games, kd, total_kr, first_day, last_day}
GET /territorial-wars/player/:playerId/contributions?day=:daykrunker.game.turf.playerContributions(playerId, day?)
Every day and map of the season without day. Rows: {day, clan_id, clan_name, map_id, map_name, kills, deaths, games, kd}.
GET /territorial-wars/player/:playerId/earningskrunker.game.turf.playerEarnings(playerId)
{day, map_id, map_name, clan_id, clan_name, amount}[].
GET /leaderboards/:type?sort=:sort&season=:seasonkrunker.game.leaderboards.players(type, { sort, season })
type is one of player_score, player_kills, player_wins, player_timeplayed, player_funds, player_followed, player_nukes, player_skinvalue, player_eventcount, player_ranked_matches. 100 rows of {position, player_name, player_clan, player_featured, value}.
GET /leaderboards/player_clan?sort=score|memberskrunker.game.leaderboards.clans(sort = "score")
100 rows of {position, clan_name, clan_score, member_count, hack_count}.
GET /leaderboards/raids/:mapId?sort=time|completionskrunker.game.leaderboards.raids(mapId, sort?)
100 rows of {position, player_name, player_clan, best_time, completions}, best_time in milliseconds. Map ids with a raid board in October 2026: 18, 29, 31, 35 and 43 (RAID_MAPS). Any other id is a 400 invalid raid map ID.
GET /leaderboards/raids/:mapId/monthly?month=YYYY-MM&sort=time|completionskrunker.game.leaderboards.raidsMonthly(mapId, month, sort?)
month is required, as YYYY-MM (what the game sends). A bare number like 8 is also answered, with different rows of unknown meaning, so krapio only accepts YYYY-MM. A month without runs answers {"data": null}, which krapio returns as [].
GET /leaderboards/krunkcup/teams
GET /leaderboards/krunkcup/teams/:teamId/contributors
GET /leaderboards/krunkcup/players?limit=:limitkrunker.game.leaderboards.krunkCupTeams(), .krunkCupContributors(teamId), .krunkCupPlayers(limit?)
Teams are {id, total_kills, total_score, total_games}. Contributors and players are {position, player_name, player_clan, kills, score, games}.
Static data the page loads at start, and the community map and mod records.
GET /mapskrunker.game.game.maps()
{name, id}[]. The id is also the preview image number at https://assets.krunker.io/img/maps/map_<id>.png.
GET /maps/:idkrunker.game.game.mapConfig(mapId)
The whole map file (120 KB for Burg). krapio names only the keys the client reads (name, modes, spawns, objects, sky, skyDome), the rest is Record<string, unknown>.
GET /maps/by-name/:namekrunker.game.game.mapByName(mapName)
The community map record. Note that Burg resolves to a community map of that name, not the official map.
{map_id, name, description, creator_name, votes, gameplays, playtime, category, created_at, updated_at, leaderboard_type, leaderboard_order, featured}
GET /maps/by-name/:name/leaderboard?page=:page&type=score|time&order=0|1krunker.game.game.mapLeaderboard(mapName, { page, type, order })
25 per page. {page, per_page, map_name, leaderboard_type, leaderboard_order, entries}.
GET /mods/by-name/:namekrunker.game.game.modByName(modName)
{mod_id, name, description, creator_name, votes, featured, version, created_at, updated_at}
GET /titleskrunker.game.game.titles()
Every player title with its requirement: {id, name, desc, rarity, requirements: {kind, text} | null, active}[]. Kinds seen: challenge, bundle, blackmarket.
GET /creators/eventskrunker.game.game.creatorEvents()
The menu's event and survey box: {events: unknown[], polls: {id, title, description, ends_in}[]}, ends_in in seconds.
GET /data/skinskrunker.game.game.skins()
Every skin the client knows, about 8150 of them, loaded once at start. Unlike the rest of gapi this is text/plain in jsonpack format, no envelope, 1.2 MB (190 KB gzipped). krapio decodes it. Its own rate limit: X-RateLimit-Limit: 1000 per 60 seconds, Cache-Control: public, max-age=300.
The array index is the skin index the market, inventory and trades use. id is not: it is the texture or model number inside its weapon or slot (weapon_1_107.png), a string for paints, and missing on creator skins.
type GameSkin = {
id?: number | string;
name: string;
rarity: number;
seas: number; // the season it came out in
thumbnail: string; // https://assets.krunker.io/textures/previews/...
tradable: boolean;
weapon?: number; // weapon skins
keyW?: string; // the other slots: "Hat", "Back", "Knife", "Sprays", ...
creator?: string;
limited?: boolean;
limT?: string; // "Gift", "Twitch", "Charity", ...
blackM?: boolean; // black market: funds is the kr price, req/reqT the unlock requirement
noSale?: boolean | number;
noSpin?: boolean | number;
noQuickSell?: boolean | number;
// about 200 more keys of render config (offsets, shaders, sounds, paint masks)
};GET /data/spinskrunker.game.game.spins()
The raw spin config, jsonpack like Skins, max-age=60. {itemPools: Record<string, {name, color, items}>, wheels}: items are skin indices, a wheel's pools are keys of itemPools and poolWeights their chances in percent. It has the hidden and ended event wheels too (hide, locked, startDate, endDate); old ones carry only rarities and no pools. Spin Wheels is the shop's view of the live wheels with the pools inlined.
The reads of the market tab that answer without a token. Listings, the player's inventory and history need one, see the token API.
GET /market/stats?cursor=&limit=&sort=&rarities=&weapons=&types=&season=&searchTerm=krunker.game.market.stats({ cursor, limit, sort, rarities, weapons, types, season, searchTerm })
| Name | Type | Description |
|---|---|---|
| cursor | int | next_cursor of the previous page |
| limit | int | 1 to 100 |
| sort | string | rarity is what the market tab sends; an unknown value is answered with the default order |
| rarities, weapons, types | int list | comma separated filters |
| season | int | |
| searchTerm | string |
type MarketStats = {
stats: { ski: number; inC: number; onS: number; high?: number; low?: number }[];
has_more: boolean;
next_cursor: number | null;
};ski is the skin index, inC the count in circulation, onS the count on sale, high and low the ask range while something is listed (seen with sort=rarity, absent under other sorts). onS of a non tradable skin is noise: Atlantean Armour reports 20 on sale with zero listings anywhere.
GET /market/items/:id/saleskrunker.game.market.itemSales(skinIndex)
Despite the path, no sales: the ask range, the circulation and the player ids of the top owners.
type ItemSalesInfo = {
ind: number; // skin index
high: number; // highest ask, 0 without listings
low: number; // lowest ask, 0 without listings
inC: number; // count in circulation
owners: number[]; // player ids of the top 100 owners, the same 100 the item view lists
};The id list is capped at 100 and is never the whole owner base (Soul Essence, 6.7 million in circulation: 100 ids; Flame Tamer: 61 ids, and 61 owners on the item view). Measured 2026-10-17.
GET /market/items/:id/sales-statistics?daysback=:dayskrunker.game.market.itemSalesStatistics(skinIndex, daysBack = 30)
One row per day with a sale: {f, t, d}[] (kr, count, YYYY-MM-DD). Reaches back about 85 days whatever daysback says (measured 2026-10-17 with 3650).
The item page of the social market, by skin index.
GET /items/:id/viewkrunker.game.items.view(skinIndex)
The whole order book of the skin in one reply, not a page of it: every live listing (41 of 42 for Codefall Paint, the missing one had sold between two calls; the developer api's market endpoint pages 10), the top 100 owners, about two weeks of daily sales and the stats.
type ItemView = {
stats: { onS: number; high: number; low: number; inC: number };
owners: { nm: string; ct: number; su: number; ft: number; cr: number }[]; // top 100 by count: name, count owned, staff, featured, cr
sales: { f: number; t: number; d: string }[]; // kr, count, YYYY-MM-DD, days with a sale
listings: ItemListing[];
stackable: boolean;
wearable: boolean;
};
type ItemListing = {
uid: number;
marketid: number;
skinid: number; // the item instance
skinindex: number;
funds: number; // ask in kr
seller_id: number;
seller_name: string;
listed_time: number; // unix ms
m?: string; // json text {"wear": 0.9013, "serial": 32}, only on wearable skins
};GET /items/:id/sales?range=:rangekrunker.game.items.sales(skinIndex, range = 6)
Same rows as the market's sales statistics. The item page sends range=6, which reaches back about 120 days; range=24 is answered 400 "invalid sales range".
GET /items/:id/instances?sort=mintkrunker.game.items.instances(skinIndex, sort = "mint")
The Mints and Wear tabs of a non stackable skin (the serial numbered Season 10 items): every instance's holder name, serial and, on wearable skins, wear. Stackable skins answer []. Capped at 100 rows and not pageable: page, offset, limit and cursor are ignored, and any sort other than mint and wear falls back to mint. So a skin with up to 100 instances is complete (Roselle: 60, Bren Gun: 64), a bigger one gives the 100 lowest serials and the 100 lowest wears.
type ItemInstance = {
nm: string; // holder's name
serial: number;
wear?: number; // wearable skins only
};GET /social/feed?tab=global&limit=:limitkrunker.game.social.feed(tab = "global", limit?)
global is the only tab seen; an unknown one is a 400 SOCIAL_INVALID_TAB. limit 1 to 50. krapio returns the posts array.
type FeedPost = {
pid: number; // post id
pln: string; // poster name
pli: number; // poster id
tim: number;
lnk: string;
txt: string;
ftr: number;
typ: number;
cln: string;
flw: number;
clr: number;
prm: number;
lnk2: string;
vot: number; // votes
dlk: number;
cnt: number; // comment count
exp: number;
fds: number;
};GET /social/post/:idkrunker.game.social.post(postId)
{post: FeedPost, comments: unknown[]}.
GET /social/newskrunker.game.social.news()
{news: unknown[]}, only seen empty.
The shop's catalogue. The wild offer and every purchase need the player's token.
GET /shop/displays/:id
GET /shop/displays?tag=:tagkrunker.game.shop.display(displayId), krunker.game.shop.displayByTag(tag)
Display 1 is the front page, tag=premium is what the menu asks for. /shop/displays without a tag answers 400.
type ShopDisplay = {
id: number;
type: string;
order: number;
data: Record<string, unknown>;
children: { id: number; parent_id: number; order: number; type: string; data: Record<string, unknown>; product_id?: number }[];
products: Record<string, ShopProduct>;
};
type ShopProduct = {
id: number;
name: string;
description: string;
image_url: string;
active: boolean;
prices: { id: number; price_ref: string; type: string; real?: { price_usd: number }; recurring?: { amount: number; periodicity: string }; available: boolean }[];
contents: { id: number; type: string; ref: string; quantity: number }[];
available: boolean;
display_ids: number[];
};GET /spins/wheelskrunker.game.shop.spinWheels()
Every wheel with its pools and weights: {index, name, price, ticketIndex, ticketOnly, multi, poolWeights, pools: {id, name, color, weight, items}[]}[], items are skin indices.
GET /lottery/:idkrunker.game.shop.lottery(lotteryId)
Id 1 is the running one. {lottery: {id, title, frequency, theme}, player_entries, total_entries, previous_draws: {id, lottery_id, starts_at, ends_at, drawn_at, prizes}[]}. player_entries is 0 without a token.
https://matchmaker.krunker.io, plain JSON without an envelope. The game polls the list every 60 seconds. krapio adds hostname=krunker.io to every call.
GET /game-list?hostname=krunker.iokrunker.game.matchmaker.gameList()
Every public lobby, about 47 KB.
type GameList = {
games: GameListEntry[];
totalPlayerCount: number;
};
// [game id, region key, players, max players, options, seconds left]
type GameListEntry = [string, string, number, number, GameOptions, number];
type GameOptions = {
v: string; // game version
g: number; // gamemode id
c: number; // custom game
cm: number;
m: number; // map index
i: string; // map name
};GET /ping-list?hostname=krunker.iokrunker.game.matchmaker.pingList()
Region key (as in a game entry) to a lobby host:port to ping.
GET /game-info?game=:gameIdkrunker.game.matchmaker.gameInfo(gameId)
One lobby as a game list entry. Ids look like FRA:4kpj2.
Not wrapped: seeking takes a slot on a lobby and can ask for a captcha. This is what the game does, recorded once in November 2026.
| Request | Reply |
|---|---|
GET /generate-token |
a validation token |
GET /seek-game?hostname=krunker.io®ion=:region&autoChangeGame=false&validationToken=:token&dataQuery={"v":":build"}&accessToken=:frvrToken[&captchaToken=:turnstile] |
the lobby to connect to |
/generate-token is plain text, <unix seconds>:<base64 of 32 bytes>. The game fetches a fresh one for every seek. dataQuery.v is the client build, and accessToken is the FRVR token (the anonymous one before login) as a query parameter, not a header.
The first seek of the page came back 400 {"error":"CaptchaRequired"}. The game then rendered a Cloudflare Turnstile widget (sitekey 0x4AAAAAAD19NTFH3Itq7QOn, the same one the login uses), which passed without any interaction about 2.4 seconds later. It then fetched a new validation token and repeated the seek with captchaToken=1.…:
{"changeReason": null, "gameId": "FRA:5awji", "host": "lobby-n6427-jf8nq.frankfurt.krunker.io", "port": 3000, "clientId": "406de4eb-…", "data": {"t": "d", "p": 62.3, "l": 41.3, "d": 21.0, "b": 2, "n": false}}data is not read by anything visible. The game copies it into its analytics as seek_t, seek_p, … .
https://api.krunker.io, the old REST API. Only the paths the page calls without a token.
GET /webhooks/general/maps/list?index=:index&pos=:poskrunker.game.legacy.mapsList({ index, pos })
index is the category tab, pos the page, both 0 based. {index, data: {map_id, map_name, map_votes, creatorname, map_featured, map_info, map_updatecounter, map_pl, fund, category, partner, map_date}[]}, map_info is JSON text.
GET /webhooks/general/mods/list?index=:index&pos=:poskrunker.game.legacy.modsList({ index, pos })
{index, data: {mod_id, mod_name, mod_votes, creatorname, mod_featured, mod_url, mod_info, mod_updatecounter}[]}.
GET /webhooks/general/items/priceskrunker.game.legacy.itemPrices()
The kr value per skin the inventory shows: an array indexed by skin index, null where there is no price. About 8000 entries, 38 KB.
GET /streams/top?cb=2krunker.game.legacy.topStreams()
The menu's stream box plus the running drop campaigns. krapio unwraps the data key.
type TopStreams = {
streams: { id: string; username: string; title: string; viewerCount: number; thumbnailUrl: string; startedAt: string; provider: string; featured: boolean; languageCode: string }[];
promo: boolean;
dropCampaigns: { name: string; startDate: number; endDate: number; drops: { itemId: number }[]; startDateISO: string; endDateISO: string }[];
};