diff --git a/example.env b/example.env
index 888dc252a442fefd9c4057eba4f43fe8b6c58ce8..e594c89ca3dcbe0cd5c773707192c3ac23c28014 100644
--- a/example.env
+++ b/example.env
@@ -14,6 +14,12 @@ API_BASE_URLS="https://simulateur-socio-fiscal.leximpact.dev/api/"
 # BASE_URL="https://simulateur-socio-fiscal.leximpact.dev/"
 BASE_URL="http://localhost:5173"
 
+# Public HTTP(S) URL of LexImpact Socio-Fiscal Budget server
+# BUDGET_API_URL="https://SECRET.DOMAIN.NAME/state_simulation"
+
+# Secret key used to sign JSON web tokens sent to Budget API
+# BUDGET_JWT_SECRET="SECRET"
+
 # Key for children in a family
 CHILDREN_KEY="enfants"
 
diff --git a/src/lib/server/auditors/config.ts b/src/lib/server/auditors/config.ts
index e924be8780b3dfcb6a90329dddb14431a1c57a7e..39cb524dfa68857a5b063ddbe4d49882ba8a5a79 100644
--- a/src/lib/server/auditors/config.ts
+++ b/src/lib/server/auditors/config.ts
@@ -65,6 +65,14 @@ export function auditConfig(
       auditRequire,
     )
   }
+  audit.attribute(
+    data,
+    "budgetApiUrl",
+    true,
+    errors,
+    remainingKeys,
+    auditHttpUrl,
+  )
   for (const key of [
     "childrenKey",
     "familyEntityKey",
@@ -84,7 +92,11 @@ export function auditConfig(
       auditRequire,
     )
   }
-  for (const key of ["githubPersonalAccessToken", "reformName"]) {
+  for (const key of [
+    "budgetJwtSecret",
+    "githubPersonalAccessToken",
+    "reformName",
+  ]) {
     audit.attribute(data, key, true, errors, remainingKeys, auditTrimString)
   }
   audit.attribute(
diff --git a/src/lib/server/config.ts b/src/lib/server/config.ts
index ffee47bd7bd1d1b657d8edd2d761002bea242c3d..ccc24e24580cd7e5fa7d15cc74fdb66a9357ec0d 100644
--- a/src/lib/server/config.ts
+++ b/src/lib/server/config.ts
@@ -9,6 +9,8 @@ export interface Config {
   apiBaseUrls: string[]
   apiWebSocketBaseUrls: string[]
   baseUrl: string
+  budgetApiUrl?: string
+  budgetJwtSecret?: string
   childrenKey: string
   familyEntityKey: string
   githubPersonalAccessToken?: string
@@ -43,6 +45,8 @@ const [validConfig, error] = validateConfig({
   allowRobots: process.env["ALLOW_ROBOTS"],
   apiBaseUrls: process.env["API_BASE_URLS"],
   baseUrl: process.env["BASE_URL"],
+  budgetApiUrl: process.env["BUDGET_API_URL"],
+  budgetJwtSecret: process.env["BUDGET_JWT_SECRET"],
   childrenKey: process.env["CHILDREN_KEY"],
   familyEntityKey: process.env["FAMILY_KEY"],
   githubPersonalAccessToken: process.env["GITHUB_PERSONAL_ACCESS_TOKEN"],
diff --git a/src/routes/budget/+page.server.ts b/src/routes/budget/+page.server.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0415495fb01b1cf3313930cdb202d071bf76bc8c
--- /dev/null
+++ b/src/routes/budget/+page.server.ts
@@ -0,0 +1,67 @@
+import { error } from "@sveltejs/kit"
+
+import type { User } from "$lib/users"
+import jwt from "jsonwebtoken"
+
+import config from "$lib/server/config"
+
+import type { PageServerLoad } from "./$types"
+
+export const load: PageServerLoad = async ({ fetch, parent, url }) => {
+  if (
+    config.budgetApiUrl === undefined ||
+    config.budgetJwtSecret === undefined
+  ) {
+    throw error(
+      404,
+      "La configuration de l'application ne permet pas d'accéder aux calculs budgétaires",
+    )
+  }
+
+  const data = await parent()
+  const user = data.user as User
+
+  if (user === undefined) {
+    throw error(
+      401,
+      "Vous devez être authentifiés pour accéder aux calculs budgétaires",
+    )
+  }
+
+  const payload = {
+    exp: user.exp,
+    iat: user.iat,
+    user: { ...user },
+  }
+  delete payload.user.exp
+  delete payload.user.iat
+
+  const response = await fetch(config.budgetApiUrl, {
+    body: JSON.stringify(
+      {
+        base: 2022,
+        output_variables: ["rfr", "irpp"],
+        quantile_nb: 10,
+        quantile_base_variable: ["rfr"],
+        quantile_compare_variables: ["irpp"],
+        plf: 2023,
+      },
+      null,
+      2,
+    ),
+    headers: {
+      Accept: "application/json",
+      "Content-Type": "application/json; charset=utf-8",
+      "jwt-token": jwt.sign(payload, config.budgetJwtSecret),
+    },
+    method: "POST",
+  })
+  if (!response.ok) {
+    console.error(
+      `${url.pathname}: Error calling budget API:\n${response.status} ${response.statusText}`,
+    )
+    console.error(await response.text())
+    return undefined
+  }
+  return { simulation: await response.json() }
+}
diff --git a/src/routes/budget/+page.svelte b/src/routes/budget/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..98e5adf3a771d6c391ec8050959fc1c4430a8643
--- /dev/null
+++ b/src/routes/budget/+page.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+  import type { PageData } from "./$types"
+
+  export let data: PageData
+</script>
+
+<pre>
+    {JSON.stringify(data, null, 2)}
+</pre>