diff --git a/frontend/src/Editor.svelte b/frontend/src/Editor.svelte
index 4dd4793..112a4f8 100644
--- a/frontend/src/Editor.svelte
+++ b/frontend/src/Editor.svelte
@@ -191,7 +191,7 @@
       return;
     }
     const y = prompt("Který ročník (číslo 26...X)");
-    await refreshTaskStatuses([`${y}-Z1-1`]);
+    await refreshTaskStatuses();
     const newTasks = Array.from($taskStatuses.values()).filter(
       (t) =>
         t.id.startsWith(y + "-") &&
diff --git a/frontend/src/Odevzdavatko.svelte b/frontend/src/Odevzdavatko.svelte
index cb1af5e..0d39990 100644
--- a/frontend/src/Odevzdavatko.svelte
+++ b/frontend/src/Odevzdavatko.svelte
@@ -1,7 +1,6 @@
 <script lang="ts">
     import { isLoggedIn, parseTaskId } from "./ksp-task-grabber";
-    import type { TaskStatus } from "./ksp-task-grabber";
-    import type { TaskSubmitStatus, SubtaskSubmitStatus } from './ksp-submit-api'
+    import type { TaskSubmitStatus, SubtaskSubmitStatus, TaskStatus } from './ksp-submit-api'
     import * as api from './ksp-submit-api'
     import { taskStatuses, refresh as refreshTaskStatus } from './task-status-cache'
     import * as s from 'svelte'
@@ -164,7 +163,7 @@
 
     async function upload(file: File) {
         const x = await api.submit(id, uploadSubtaskId!, file)
-        refreshTaskStatus([id])
+        refreshTaskStatus()
         const subtasks = [...task.subtasks]
         subtasks[subtasks.findIndex(s => s.id == x.id)] = x
         task = { ...task, subtasks }
diff --git a/frontend/src/TasksLoader.svelte b/frontend/src/TasksLoader.svelte
index c7fd09a..e0d6f46 100644
--- a/frontend/src/TasksLoader.svelte
+++ b/frontend/src/TasksLoader.svelte
@@ -1,6 +1,5 @@
 <script lang="ts">
   import type { TasksFile } from "./tasks";
-  import { refresh } from './task-status-cache'
 
   export let promise: Promise<TasksFile>;
 
@@ -9,14 +8,12 @@
   let err: any | null = null;
     promise.then(
         (d) => {
-            refresh(d.tasks.map(t => t.id))
             data = d;
         },
         (e) => {
             err = e;
         }
     )
-  
 </script>
 
 {#if data == null && err == null}
diff --git a/frontend/src/ksp-submit-api.ts b/frontend/src/ksp-submit-api.ts
index d255703..5d718aa 100644
--- a/frontend/src/ksp-submit-api.ts
+++ b/frontend/src/ksp-submit-api.ts
@@ -1,4 +1,4 @@
-import { fetchHtml } from "./ksp-task-grabber";
+import { fetchHtml, isLoggedIn } from "./ksp-task-grabber";
 
 let apitoken : string | null = null
 
@@ -85,3 +85,28 @@ export async function submit(id: string, subtask: string, uploadedData: string |
             ]
         })
 }
+export type TaskStatus = {
+    id: string
+    name: string
+    submitted: boolean
+    solved: boolean
+    points: number
+    maxPoints: number
+}
+
+export async function grabTaskSummary(): Promise<Map<string, TaskStatus>> {
+    if (!isLoggedIn()) throw new Error()
+
+    const results = await requestJson(`/api/tasks/x-summary`) as { tasks: TaskSubmitStatus[] }
+
+    function mapId(id: string) {
+        if (id.startsWith("cviciste/"))
+            return id.substr("cviciste/".length)
+        else
+            return id
+    }
+
+    return new Map<string, TaskStatus>(
+        results.tasks.map(r => [mapId(r.id), { id: mapId(r.id), maxPoints: r.max_points, name: r.name, points: r.points, solved: r.points > r.max_points - 0.001, submitted: r.points > 0 }])
+    )
+}
diff --git a/frontend/src/ksp-task-grabber.ts b/frontend/src/ksp-task-grabber.ts
index e35e64f..ea4bb59 100644
--- a/frontend/src/ksp-task-grabber.ts
+++ b/frontend/src/ksp-task-grabber.ts
@@ -14,16 +14,6 @@ type TaskLocation = {
     startElement: string
 }
 
-export type TaskStatus = {
-    id: string
-    name: string
-    submitted: boolean
-    solved: boolean
-    points: number
-    maxPoints: number
-    type: string
-}
-
 function fixAllLinks(e: any) {
     if (typeof e.src == "string") {
         e.src = e.src
@@ -150,27 +140,6 @@ function parseTask(startElementId: string, doc: HTMLDocument): TaskAssignmentDat
     }
 }
 
-function parseTaskStatuses(doc: HTMLDocument): TaskStatus[] {
-    const rows = Array.from(doc.querySelectorAll("table.zs-tasklist tr")).slice(1) as HTMLTableRowElement[]
-    return rows.map(r => {
-        const submitted = !r.classList.contains("zs-unsubmitted")
-        const id = r.cells[0].textContent!.trim()
-        const type = r.cells[1].textContent!.trim()
-        const name = r.cells[2].textContent!.trim()
-        const pointsStr = r.cells[4].textContent!.trim()
-        const pointsMatch = /((–|\.|\d)+) *\/ *(\d+)/.exec(pointsStr)
-        if (!pointsMatch) throw new Error()
-        let points = +pointsMatch[1]
-        // points was a dash, means 0
-        if (isNaN(points)) {
-            points = 0
-        }
-        const maxPoints = +pointsMatch[3]
-        const solved = r.classList.contains("zs-submitted")
-        return { id, name, submitted, type, points, maxPoints, solved }
-    })
-}
-
 export async function fetchHtml(url: string) {
     const r = await fetch(url, { headers: { "Accept": "text/html,application/xhtml+xml" } })
     if (r.status >= 400) {
@@ -196,21 +165,6 @@ export function isLoggedIn(): boolean {
     return !!document.head.querySelector("meta[name=x-ksp-uid]")
 }
 
-export async function grabTaskStates(kspIds: string[]): Promise<Map<string, TaskStatus>> {
-    if (!isLoggedIn()) throw new Error()
-
-    const ids = new Set<string>(kspIds.map(parseTaskId).filter(t => t != null).map(t => t!.rocnik))
-    const results = await Promise.all(Array.from(ids.keys()).map(async (rocnik) => {
-        const html = await fetchHtml(`/cviciste/?year=${rocnik}`)
-        return parseTaskStatuses(html)
-    }))
-
-    return new Map<string, TaskStatus>(
-        ([] as TaskStatus[]).concat(...results)
-        .map(r => [r.id, r])
-    )
-}
-
 export async function grabAssignment(id: string): Promise<TaskAssignmentData> {
     return await loadTask(getLocation(id, false))
 }
diff --git a/frontend/src/task-status-cache.ts b/frontend/src/task-status-cache.ts
index d499522..58e30ee 100644
--- a/frontend/src/task-status-cache.ts
+++ b/frontend/src/task-status-cache.ts
@@ -1,5 +1,6 @@
-import { grabTaskStates, isLoggedIn} from "./ksp-task-grabber"
-import type { TaskStatus } from "./ksp-task-grabber"
+import { isLoggedIn } from "./ksp-task-grabber"
+import { grabTaskSummary } from './ksp-submit-api'
+import type { TaskStatus } from "./ksp-submit-api"
 import { readable } from 'svelte/store';
 
 let writeFn: (value: Map<string, TaskStatus>) => void = null!;
@@ -14,12 +15,14 @@ export const taskStatuses = readable(lastVal, write => {
     writeFn = v => { lastVal = v; write(v); }
 })
 
-export function refresh(ids: string[]) {
+export function refresh() {
     if (!isLoggedIn()) return;
 
-    return grabTaskStates(ids).then(t => {
+    return grabTaskSummary().then(t => {
         const tt = Array.from(t.entries())
         writeFn(new Map(Array.from(lastVal.entries()).concat(tt)))
         localStorage.setItem("taskStatuses-cache", JSON.stringify(tt))
     })
 }
+
+refresh()