WalkScape Public API
A read-only API for community tools that track WalkScape characters, the leaderboards, where players are, and the weekly job boards.
- Audience: authors of third-party tools, bots and websites.
- Version: v1. Fields may be added to v1 responses; nothing is renamed, removed or changes meaning without a new version.
- Base URL:
/public/v1on the server hosting this page. All paths below are relative to that host. - This page: always at
/public/v1. The markdown source is at/docs/public_api_v1.md.
If your tool currently calls
/portal/shared/characters/{id}, please move to this API. The portal endpoint is shaped by what the portal needs and may change without notice; this one will not.
Authentication and limits
None required. Requests are anonymous. Please do not poll faster than the
Cache-Control header allows (see Caching); rate limits per IP
address will be introduced, and tools that respect the cache headers will not
notice them.
Endpoints
GET /public/v1/characters/{id}
Returns one character.
404with error typecharacter_does_not_existwhen there is no such character.
GET /public/v1/characters?ids={id},{id},...
Returns up to 100 characters in one request. Ids can be comma separated or
given as repeated parameters (?ids=a&ids=b); duplicates are ignored. If you
track many characters, use this instead of one request per character.
{
"characters": [ { "id": "...", "...": "..." } ],
"not_found": ["id-that-does-not-exist"]
}
characters follows the order the ids were requested in, leaving out the ones
listed in not_found.
400with error typemissing_query_paramswhen no ids are given.400with error typecap_reachedwhen more than 100 distinct ids are given.
GET /public/v1/characters/directory?after={cursor}
Lists which characters exist, so you can find ids without scraping the leaderboards. Returns pages of 1000 in the order characters were created:
{
"characters": [ { "id": "...", "character_name": "..." } ],
"next_cursor": "12815",
"has_more": true
}
- Leave out
afterto start from the beginning. Pass the previous page'snext_cursorto continue, untilhas_moreisfalse. - Keep the last
next_cursor. Calling again with it later returns only the characters created since, so you never need to walk the whole directory twice. - Treat the cursor as opaque. Its format may change.
- Only id and name are returned. Fetch the data through the batch endpoint above.
- Characters whose players have hidden them from the leaderboards are not
listed. Deleted characters silently disappear; the batch endpoint reports them
in
not_found. 400with error typebad_search_stringfor a malformed cursor.
The character object
{
"id": "walker-4fdb40cb-9cc2-4c7f-b1f8-6eb95e91f950",
"character_name": "Walker",
"created_at": "2024-01-07T03:42:37.124597Z",
"updated_at": "2026-08-02T19:43:55.887438Z",
"total_steps": 1234567,
"location_uid": "b1f0e251-ad0c-4be8-bb9b-7e5449dfc221",
"statistics": { "agility": 123456, "...": "..." },
"ironfoot": false,
"user": { "username": "walker", "avatar": null }
}
| Field | Type | Notes |
|---|---|---|
id |
string | Stable character id. |
character_name |
string | |
created_at |
ISO 8601 UTC string | |
updated_at |
ISO 8601 UTC string | Last time the character was saved. |
total_steps |
integer or null |
null when the player has chosen to hide their steps. |
location_uid |
string or null |
The character's current location. |
statistics |
object | Skill experience and other statistics, keyed by name. |
ironfoot |
boolean | Whether this is an Ironfoot character. |
user |
object or null |
The owning account's username and avatar. null unless the player's profile visibility is public. |
Every field is always present; optional values are null rather than missing.
GET /public/v1/jobs/weeks
Lists the job board weeks there are jobs for, oldest first, ending with the
running week. A week runs from Monday 00:00 UTC to the next Monday; week 0
began on 2024-01-01.
{
"current_week_index": 147,
"weeks": [
{ "week_index": 146, "starts_at": "2026-10-19T00:00:00.000Z", "ends_at": "2026-10-26T00:00:00.000Z" },
{ "week_index": 147, "starts_at": "2026-10-26T00:00:00.000Z", "ends_at": "2026-11-02T00:00:00.000Z" }
]
}
Every player sees the same jobs. A week is never available before it starts.
GET /public/v1/jobs/weeks/{weekIndex}
Returns every board's jobs for one week. {weekIndex} is a week number from
the list above, or current for the running week.
{
"week_index": 147,
"starts_at": "2026-10-26T00:00:00.000Z",
"ends_at": "2026-11-02T00:00:00.000Z",
"jobs": [ { "id": 552, "board_id": "...", "...": "..." } ]
}
jobs are job objects, ordered by board_id and then
slot. On rare occasions a running week's board is rolled again: its jobs are
then replaced by new ones with new ids, and the old ones disappear from this
list and from the stats.
404with error typedoes_not_existfor a week that has not started, has no jobs, or is not a week number.
GET /public/v1/jobs/weeks/{weekIndex}/stats
How many characters took part, for one week (current works here too). Counts
only; the API never lists who.
{
"week_index": 147,
"generated_at": "2026-10-29T12:05:00.000Z",
"jobs": [
{ "job_id": 552, "accepted_characters": 1834, "completed_characters": 1210 }
],
"boards": [
{
"board_id": "...",
"hand_ins": 5120,
"characters": 2048,
"bonus_claims": [ { "milestone": 5, "characters": 312 } ]
}
]
}
| Field | Notes |
|---|---|
jobs |
One entry per job of this week that anyone accepted, among the jobs the week lists. Accepted jobs can be handed in until their expires_at, weeks after their board closed, so these counts keep growing after the week ends. A job never accepted is left out. |
accepted_characters |
Characters who accepted the job, including those who accepted and handed it in at once. |
completed_characters |
Characters who handed it in. |
boards |
What happened on each board during this week: hand_ins counts every job handed in there, whichever week it was from, and characters the characters who handed in at least one. |
bonus_claims |
Characters who reached each weekly bonus milestone, by the number of jobs it needs. |
generated_at |
When the counts were taken. |
404with error typedoes_not_exist, as for the week itself.
The job object
{
"id": 552,
"board_id": "jobboard-kallaheim-...",
"slot": 0,
"difficulty_id": "jobdifficulty-hard",
"special_id": null,
"bonus": false,
"required_level": 2,
"expires_at": "2026-11-30T00:00:00.000Z",
"goals": [
{
"item": { "id": "item-birch_logs-...", "quality": "common", "tag": "none" },
"quantity": 214,
"alternatives": []
}
],
"rewards": [
{ "kind": "faction_reputation", "item": null, "target_id": "faction-jarvonia-...", "amount": 3 },
{ "kind": "item", "item": { "id": "item-jarvonia_chest-...", "quality": "common", "tag": "none" }, "target_id": null, "amount": 1 }
]
}
| Field | Type | Notes |
|---|---|---|
id |
integer | Stable job id, unique across all weeks. |
board_id |
string | The job board, as in the game data. |
slot |
integer | Position on the board. |
difficulty_id |
string | |
special_id |
string or null |
Set for a hand-made job, null for a generated one. |
bonus |
boolean | One of its difficulty's bonus jobs, which ask and pay more. |
required_level |
integer | Level in the board's faction needed to accept it. |
expires_at |
ISO 8601 UTC string | A player who accepted the job can hand it in until then. |
goals |
array | Every goal must be met. A goal takes quantity of item, or the quantity of any one of its alternatives instead (higher qualities of the same item). |
rewards |
array | kind is item, coins, faction_reputation or skill_experience. item is set for item rewards, target_id (a faction or skill id) for the last two. |
Items are given as their game data id, a quality (common, uncommon,
rare, epic, legendary or ethereal) and a tag (none, fine or
rare_egg). Every field is always present.
GET /public/v1/leaderboards/{type}?page={n}
One page of a leaderboard, 100 entries per page, best first. {type} is one of
total_level, total_xp, total_steps, achievement_points, agility,
carpentry, cooking, crafting, fishing, foraging, hunting, mining,
smithing, tailoring, trinketry or woodcutting. page counts from 1 and
defaults to 1; every page of a board can be fetched.
{
"type": "total_level",
"page": 1,
"page_size": 100,
"has_more": true,
"entries": [
{
"rank": 1,
"position": 1,
"character_id": "walker-4fdb40cb-9cc2-4c7f-b1f8-6eb95e91f950",
"character_name": "Walker",
"title_id": null,
"ironfoot": false,
"value": 1234,
"total_xp": 98765432
}
]
}
| Field | Type | Notes |
|---|---|---|
rank |
integer | Characters with the same score share a rank, and the next rank skips accordingly (1, 2, 2, 4). On total_level, total_xp breaks ties. |
position |
integer | The place in the list, counting from 1. |
character_id |
string or null |
null for a player who is anonymous on the leaderboards. |
character_name |
string | Anonymous Walker for an anonymous player. |
title_id |
string or null |
The character's title, as in the game data. null without one, and for an anonymous player. |
ironfoot |
boolean | Whether this is an Ironfoot character. |
value |
integer or null |
The score this board ranks by. |
total_xp |
integer |
- Keep fetching pages while
has_moreistrue. A board that ends exactly on a full page answers the next page with no entries andhas_morefalse. - Characters whose players have hidden them from the leaderboards are left out,
and so are those who hide their steps, from
total_stepsonly. Anonymous players are ranked but not named. - To find character ids, use the directory, not the leaderboards.
404with error typedoes_not_existfor an unknown{type}.400with error typebad_search_stringwhenpageis not a whole number from 1 to 1000000.
GET /public/v1/census
How many characters are at each location, counting the characters whose game was saved in the last 30 days. The window is fixed and counted from the start of the current hour.
{
"total": 10779,
"locations": [
{ "location_uid": "b1f0e251-ad0c-4be8-bb9b-7e5449dfc221", "name": "Kallaheim", "count": 312 }
]
}
locations is ordered by count, highest first. name is the location's
English name.
GET /public/v1/census/activities
How many of those characters are doing each activity, and where. Characters that are not doing anything are not counted.
{
"total": 6259,
"activities": [
{
"activity_id": "...",
"name": "Birch Logs",
"total": 120,
"locations": [
{ "location_uid": "...", "name": "Kallaheim", "count": 80 }
]
}
]
}
Names are in English. The census never lists characters, only counts.
Caching
Character data is refreshed at most once a minute. Every response carries:
Cache-Control: public, max-age=60(max-age=300for the directory, the leaderboards, the job week list, the running week's jobs and all job stats;max-age=900for the census;max-age=3600for the jobs of an ended week). Do not ask again for the same data within that time.ETag— send it back asIf-None-Matchon your next request. If nothing changed you get an empty304 Not Modified, which is cheaper for both sides.
Because of the server-side cache, a change to a character can take up to about two minutes to appear here. The leaderboards are rebuilt every two minutes and cached for five, so they can be up to about seven minutes behind the game, and the census up to about fifteen.
Errors
Errors use the standard WalkScape error body:
{
"error": {
"type": "character_does_not_exist",
"code": "1002",
"error_message": "Character does not exist.",
"hint": "The character you tried to access does not exist.",
"http_code": 404
}
}
Only GET is accepted; other methods get 400 with error type
wrong_http_method.