Reference
Tools reference
Every tool live today, with its input and a response generated from the same data the site publishes. Each response carries the address of the page it comes from.
On this page
At a glance
| Tool | Returns | Scope |
|---|---|---|
list_countries | List measurable countries | read |
get_country_bands | Get a country's income bands | read |
list_shapes | List the nine shapes | read |
get_shape | Get one shape | read |
explain_method | Explain the method | read |
start_measurement | Start a reading | read |
list_countries
Lists every country Betteria can measure, with its ISO 3166-1 alpha-3 and alpha-2 codes, currency, the confidence its data was graded with, and whether its page is offered to search engines. Call this first when you need a country code for get_country_bands, or to answer which countries are covered.
Input
No input.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_countries",
"arguments": {}
}
}Example response
Generated from the site's data when this page was rendered. Shortened here: the full response lists all 149.
{
"datasetVersion": "2026.4",
"referenceYear": 2024,
"count": 149,
"countries": [
{
"code": "ALB",
"alpha2": "AL",
"name": "Albania",
"currency": "ALL",
"currencySymbol": "ALL",
"confidence": "medium",
"indexed": true,
"url": "https://betteria.app/countries/alb"
},
{
"code": "DZA",
"alpha2": "DZ",
"name": "Algeria",
"currency": "DZD",
"currencySymbol": "DA",
"confidence": "low",
"indexed": false,
"url": "https://betteria.app/countries/dza"
},
{
"code": "AGO",
"alpha2": "AO",
"name": "Angola",
"currency": "AOA",
"currencySymbol": "AOA",
"confidence": "low",
"indexed": false,
"url": "https://betteria.app/countries/ago"
}
],
"url": "https://betteria.app/countries"
}get_country_bands
Returns one country's published yearly income bands in its own currency, the bottom-tenth, top-tenth and top-hundredth cut-offs, the per-earner factor, what the distribution is measured on, the years and surveys behind it, the dataset version, and the sources. Use it for questions like "what income puts someone in the top 10% in Germany". Takes an ISO alpha-3 code; call list_countries if you do not have one.
Input
| Field | Type | Required | Description |
|---|---|---|---|
country | string | Yes | ISO 3166-1 alpha-3 country code, for example DEU or BRA. Case does not matter. |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_country_bands",
"arguments": {
"country": "DEU"
}
}
}Example response
Generated from the site's data when this page was rendered.
{
"country": {
"code": "DEU",
"alpha2": "DE",
"name": "Germany"
},
"currency": "EUR",
"currencySymbol": "€",
"period": "year",
"datasetVersion": "2026.4",
"referenceYear": 2024,
"unit": "per earner",
"perEarnerFactor": 1.9738,
"measuredOn": "income",
"bands": [
{
"fromPercentile": null,
"toPercentile": 10,
"label": "under €17,000"
},
{
"fromPercentile": 10,
"toPercentile": 25,
"label": "€17,000 – €27,000"
},
{
"fromPercentile": 25,
"toPercentile": 40,
"label": "€27,000 – €34,000"
},
{
"fromPercentile": 40,
"toPercentile": 55,
"label": "€34,000 – €42,000"
},
{
"fromPercentile": 55,
"toPercentile": 70,
"label": "€42,000 – €51,000"
},
{
"fromPercentile": 70,
"toPercentile": 80,
"label": "€51,000 – €60,000"
},
{
"fromPercentile": 80,
"toPercentile": 90,
"label": "€60,000 – €78,000"
},
{
"fromPercentile": 90,
"toPercentile": 95,
"label": "€78,000 – €96,000"
},
{
"fromPercentile": 95,
"toPercentile": 98,
"label": "€96,000 – €130,000"
},
{
"fromPercentile": 98,
"toPercentile": 99,
"label": "€130,000 – €170,000"
},
{
"fromPercentile": 99,
"toPercentile": null,
"label": "€170,000 or more"
}
],
"cutoffs": {
"bottomTenthBelow": "€17,000",
"topTenthFrom": "€78,000",
"topOnePercentFrom": "€170,000"
},
"coverage": {
"confidence": "medium",
"yearsCovered": {
"from": 1981,
"to": 2024
},
"surveyBasedSharePct": 73,
"yearsToNearestSurvey": 2,
"ageBandsResolved": 6,
"quotedInNewCurrencyFrom": null,
"reason": "the span is short of the longest horizon"
},
"sources": [
{
"name": "World Bank Poverty and Inequality Platform",
"url": "https://pip.worldbank.org/",
"provides": "National income distribution by percentile, harmonised across surveys.",
"licence": "CC0 1.0 — public domain dedication"
},
{
"name": "OECD Income Distribution Database",
"url": "https://data-explorer.oecd.org/",
"provides": "Disposable income distribution resolved by age, for member countries.",
"licence": "CC BY 4.0 — attribution required"
},
{
"name": "IMF World Economic Outlook",
"url": "https://www.imf.org/en/Publications/WEO",
"provides": "Consumer prices by country and year, so a gain is a real gain.",
"licence": "not established"
},
{
"name": "World Inequality Database",
"url": "https://wid.world/",
"provides": "Top shares and the long-run series the lifetime horizon reads from.",
"licence": "not established"
}
],
"sourceStatement": "Measured against World Bank, OECD, IMF and WID data.",
"attribution": "World Bank poverty and inequality data is reused under CC0 1.0; World Development Indicators and OECD data under CC BY 4.0. IMF and WID material is used under each publisher's own terms, recorded per dataset version.",
"url": "https://betteria.app/countries/deu"
}list_shapes
Lists the nine shapes a Betteria reading can land in and the three families they are grouped in, with each shape's one-line meaning. Use it to explain what a shape name such as "The Builder" means, or to show how the nine fit together.
Input
No input.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_shapes",
"arguments": {}
}
}Example response
Generated from the site's data when this page was rendered. Shortened here: the full response lists all 9.
{
"families": [
{
"slug": "rising-ground",
"name": "Rising ground",
"meaning": "After prices, what you hold grew. The three shapes differ in what happened to your place in your country while it did.",
"shapes": [
"climber",
"builder",
"gatherer"
],
"url": "https://betteria.app/families/rising-ground"
},
{
"slug": "level-ground",
"name": "Level ground",
"meaning": "After prices, what you hold stayed close to where it was. The three shapes differ in what happened to your place in your country around it.",
"shapes": [
"outpacer",
"anchor",
"keeper"
],
"url": "https://betteria.app/families/level-ground"
},
{
"slug": "shifting-ground",
"name": "Shifting ground",
"meaning": "After prices, what you hold eased. The three shapes differ in what happened to your place in your country while the ground moved.",
"shapes": [
"pathfinder",
"weatherer",
"rebuilder"
],
"url": "https://betteria.app/families/shifting-ground"
}
],
"shapes": [
{
"slug": "climber",
"name": "The Climber",
"code": "↑↑",
"family": "rising-ground",
"line": "You moved. Both ways.",
"url": "https://betteria.app/types/climber"
},
{
"slug": "builder",
"name": "The Builder",
"code": "→↑",
"family": "rising-ground",
"line": "Same place. More ground.",
"url": "https://betteria.app/types/builder"
},
{
"slug": "gatherer",
"name": "The Gatherer",
"code": "↓↑",
"family": "rising-ground",
"line": "More in hand. The whole field ran fast.",
"url": "https://betteria.app/types/gatherer"
}
],
"url": "https://betteria.app/types"
}get_shape
Returns one shape's full definition: what it describes, what it does not mean, which way place and ground moved, and its family. Takes the shape's slug from list_shapes.
Input
| Field | Type | Required | Description |
|---|---|---|---|
slug | string, one of | Yes | The shape's slug, as list_shapes returns it.climberbuildergathereroutpaceranchorkeeperpathfinderweathererrebuilder |
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_shape",
"arguments": {
"slug": "builder"
}
}
}Example response
Generated from the site's data when this page was rendered.
{
"slug": "builder",
"name": "The Builder",
"code": "→↑",
"family": "rising-ground",
"line": "Same place. More ground.",
"url": "https://betteria.app/types/builder",
"short": "Builder",
"describes": "You didn't change your place in line — you built under it. More real ground, same relative spot: the quiet, compounding kind of progress.",
"doesNotMean": "Builder doesn't mean you stood still. Your place held because the field moved too — and what you hold after prices grew.",
"place": "held",
"ground": "grew",
"familyName": "Rising ground",
"familyUrl": "https://betteria.app/families/rising-ground"
}explain_method
Returns how a Betteria reading is worked out: what is asked, how place and ground are measured, the band that counts as held, how confidence is graded, the sources, coverage and limits. Use it before explaining how Betteria arrives at a number.
Input
No input.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "explain_method",
"arguments": {}
}
}Example response
Generated from the site's data when this page was rendered.
{
"summary": "Arithmetic over published data, the same every time for the same answers. No model writes a figure. This page is the working, start to finish.",
"asked": "9 short screens, every answer a tap. The ones marked below are the only answers the figure is computed from; the rest is context kept beside it, and nothing typed is ever stored.",
"measures": [
{
"name": "Your place",
"meaning": "Where your income sits in your country's income distribution today, against where your income a year ago sat. The movement is the ratio of the two positions, so it reads the same at any level of income."
},
{
"name": "Your ground",
"meaning": "Your income now and a year ago, both with consumer prices taken out, against how the middle of your country moved over the same year. A raise that only kept up with prices is not ground gained; a year in which everyone's money bought less is not ground lost by you alone."
},
{
"name": "The figure on the card",
"meaning": "The two movements combined, with weights recorded in the reference data's own version so a reading can always be recomputed. It is bounded at both ends, and the card says so when a bound applies. Within 0.5 percent either side of zero, the card says you held level."
}
],
"states": "Each of the two movements lands in one of three states. Inside 2 percent either side of no change, it counts as held; beyond that, gained or eased. The same band on both, for every reading.",
"heldBandPercent": 2,
"confidence": [
{
"level": "high",
"meaning": "Both ends of the year were answered by your country's own distribution and its own price index."
},
{
"level": "medium",
"meaning": "Part of the distribution was interpolated between published points, for example an age band the source does not split out."
},
{
"level": "low",
"meaning": "The price series had to be filled for a year it does not cover, or the distribution leaned on a regional stand-in."
}
],
"coverage": "149 countries can be measured in reference data version 2026.4. A country without enough published data is left out of the list, not estimated.",
"frozen": "Reference data is frozen once published, so the same answers give the same figure. New data arrives as a new version, never as a rewrite.",
"limits": [
"It measures one country at a time. A person who moved can read a crossing between two readings from an account, but a single reading never mixes two countries.",
"Income is a band, never an exact figure, so a small move inside one band does not register.",
"It compares a year ago with today. It says nothing about why the year went the way it did.",
"Where the data is thin, the card says so through its confidence level rather than quietly inventing precision.",
"It does not rank you against other people, and it gives no advice."
],
"sources": [
{
"name": "World Bank Poverty and Inequality Platform",
"url": "https://pip.worldbank.org/",
"provides": "National income distribution by percentile, harmonised across surveys.",
"licence": "CC0 1.0 — public domain dedication"
},
{
"name": "OECD Income Distribution Database",
"url": "https://data-explorer.oecd.org/",
"provides": "Disposable income distribution resolved by age, for member countries.",
"licence": "CC BY 4.0 — attribution required"
},
{
"name": "IMF World Economic Outlook",
"url": "https://www.imf.org/en/Publications/WEO",
"provides": "Consumer prices by country and year, so a gain is a real gain.",
"licence": "not established"
},
{
"name": "World Inequality Database",
"url": "https://wid.world/",
"provides": "Top shares and the long-run series the lifetime horizon reads from.",
"licence": "not established"
}
],
"url": "https://betteria.app/method"
}start_measurement
Returns the link where a person takes their own Betteria reading, and one sentence on what it involves. Use it when someone wants to measure themselves; never ask them for their answers yourself.
Input
No input.
Example call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "start_measurement",
"arguments": {}
}
}Example response
Generated from the site's data when this page was rendered.
{
"url": "https://betteria.app/m/place",
"about": "An anonymous reading: 9 short screens, every answer a tap, nothing typed and no account needed. The person gets one number, the name of their shape, and a card to keep."
}Tools for your own Betteria
These read and change your own Betteria, as the account you signed in with. Reading needs betteria.read; adding and marking needs betteria.write, which you allow on the consent screen. None of them deletes anything, and words other people wrote come back in an untrusted_text field. They are part of Betteria Plus, open to every account while Plus features are not yet enforced.
| Tool | What it does | Scope |
|---|---|---|
get_my_dashboard | Returns the signed-in person's own Betteria at a glance: their latest readings (each figure as Betteria computed it), their goals with where each one stands, the crew tasks and goals on their today, and how many crews and circles they are in. Call this first to see what the person already has before adding anything. | betteria.read |
list_my_goals | Lists every goal the person set for themselves, with its id, kind, target date and where it stands. Use the id with log_goal_amount or toggle_goal_day. | betteria.read |
list_my_crews | Without crew_id: lists the crews the person is in. With crew_id: that crew's live tasks and goals, the person's own first, with ids for the mark and outcome tools. | betteria.read |
list_my_circles_today | Lists the circles the person joined and the tasks and goals in each that are on today, with the person's own mark if they made one. Use an item's id with mark_circle_item. | betteria.read |
create_goal | Sets a goal the person defines, of one kind: number (save or reach an amount, with a unit, a start and a target, counted as a running sum or the latest value), rhythm (a count per week or month, such as run three times a week), checklist (steps to tick), or milestone (one thing done). Needs a description in the person's words and a target date today or later. Free accounts hold three active goals at once; the refusal says so. | betteria.write |
log_goal_amount | Logs an amount against one of the person's number goals, such as 500 saved this month. Betteria recomputes where the goal stands from every log. | betteria.write |
toggle_goal_day | Marks a day done on one of the person's rhythm goals, such as a run today — or, if that day is already marked, takes the mark back. Read the goal first if you are not sure which. | betteria.write |
add_crew_task | Adds a task of the person's own inside one of their crews: once (optionally by a date) or daily. Nobody else is put on it; crew members may choose to join. | betteria.write |
add_crew_goal | Adds a goal of the person's own inside one of their crews. repeat none is one target date with an outcome at the end; daily or weekdays comes up on those days between start_date and due_date (at most 366 days), and can be marked done each day. | betteria.write |
mark_crew_task_done | Marks a crew task the person is doing as done for a day. A once task is done once; a daily task once per day. | betteria.write |
undo_crew_task_mark | Takes back the person's done mark on a crew task for a day. | betteria.write |
mark_crew_goal_today | Marks one of the person's own repeating crew goals done for a scheduled day. A goal with repeat none is never marked; say how it went with set_crew_goal_outcome instead. | betteria.write |
undo_crew_goal_mark | Takes back the person's done mark on one of their repeating crew goals for a day. | betteria.write |
set_crew_goal_outcome | Records how one of the person's own crew goals went: reached or not_reached, with an optional note in their words. Both are the same kind of fact; neither is a verdict. Calling it again changes what was said. | betteria.write |
mark_circle_item | Marks a task or goal in one of the person's circles for today, as a quarter, half, three quarters or done. One mark per item per day, and it stays. | betteria.write |
Protocol
Streamable HTTP. Every message is a POST to the server address with Content-Type: application/json, and every answer is a single JSON body. There is no session and no server-to-client stream: GET and DELETE answer 405.
Every request carries an OAuth bearer token from your Betteria sign-in. Without one the server answers 401, with a WWW-Authenticate header naming its protected-resource metadata; with one that lacks a tool's scope, 403 insufficient_scope.
Protocol versions 2025-11-25, 2025-06-18, 2025-03-26. initialize answers with the client's version when it is one of these, and the latest of them otherwise.
A tool result carries the data twice, as the protocol suggests: as structuredContent, and as the same JSON in a text block for clients that read only text.
Arguments that do not match a tool's schema are a JSON-RPC error (-32602). A well-formed question the data cannot answer, such as a country that is not measured, is a tool result with isError set and a sentence the assistant can act on.