Endpoints
Both REST API endpoints in full: listing the tools and calling one, with their parameters, example requests and answers, and the errors each can send.
The REST API has two endpoints, both under /api/v1/tools on the console. Both need an access token. See The REST API for the base URL, the token and the rules every request follows.
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/tools | Lists every tool, with its input schema, and the rules every answer must follow. |
POST | /api/v1/tools/<name> | Calls one tool with a JSON object of its arguments. |
List the tools
GET /api/v1/tools
- Token: any access token, read-only or with write access.
- Parameters: none.
- Counts against: the per-address budget only. Listing the tools does not spend your tool-call budgets. See Rate limits.
curl -H "Authorization: Bearer YOUR_UNITLEY_TOKEN" https://console.unitley.com/api/v1/toolsThe answer
HTTP 200 with two fields:
instructions: the rules Unitley asks every agent to follow, word for word what the MCP server sends.tools: every tool the API offers, whatever your token's scope.
Each tool has these fields:
| Field | What it holds |
|---|---|
name | The name to call it by. |
title | A short, readable title. |
description | What it does, what it returns and when to use it. This is the text agents are given. |
scope | read or write. A write tool needs a token with write access. |
hints | readOnlyHint, destructiveHint, idempotentHint and openWorldHint, the same hints the MCP server sends. |
inputSchema | The arguments, as JSON Schema (draft 2020-12). |
{
"instructions": "Unitley is analysis, not a guarantee; use only Unitley tools…",
"tools": [
{
"name": "calc_implied_probability",
"title": "Implied probability",
"description": "Converts American odds to decimal odds and the win probability the price implies, vig included.",
"scope": "read",
"hints": {
"readOnlyHint": true,
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": false
},
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"americanOdds": {
"type": "integer",
"minimum": -100000,
"maximum": 1000000
}
},
"required": [
"americanOdds"
]
}
}
]
}Errors
| Status | Code | When |
|---|---|---|
| 401 | invalid_token | No token, a malformed one, or one that is unknown, revoked or expired. |
| 403 | forbidden | The wrong host, or a browser on another site. |
| 429 | rate_limited | The per-address budget is spent. Wait for Retry-After. |
| 500 | internal | Something failed on Unitley's side. |
| 503 | unavailable | Unitley could not serve the request just then. |
Call a tool
POST /api/v1/tools/<name>
- Token: any access token for a
readtool. Awritetool, log_bet, settle_bet and save_recommendation, needs a token with write access. - Path:
name, the tool's name from the list, such asget_game_odds. - Body: a JSON object of the tool's arguments, sent with
Content-Type: application/json. An empty body means no arguments. Every tool lists each tool's arguments. - Counts against: the per-address budget, then your per-token and per-user budgets. See Rate limits.
A success is HTTP 200 with the tool's result under data:
curl -X POST https://console.unitley.com/api/v1/tools/calc_implied_probability \
-H "Authorization: Bearer YOUR_UNITLEY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"americanOdds": -110}'{
"data": {
"americanOdds": -110,
"decimalOdds": 1.9090909090909092,
"impliedProbability": 0.5238095238095238
}
}The order of the checks
A call meets these checks in this order, and the first one that fails is the answer:
- Host and origin. The console's hostname, and no browser from another site.
- Per-address budget. Counted before the token is looked at.
- Token shape. A token must be present and shaped like a Unitley token.
- Body. The content type, the size and the JSON.
- Token owner. The token must be live: not unknown, revoked or expired.
- The tool. A name the API does not offer is
unknown_tool. - Tool-call budgets. Per token, then per user.
- Scope. A read-only token calling a
writetool getsinsufficient_scope. - Arguments. Checked against the tool's schema.
A call that reaches step 7 counts against your tool-call budgets even when its scope or its arguments then fail. A call to a name that is not a tool is not counted there.
Some calls take a while, such as get_player_props when it fetches a game's props. Give your client a timeout of up to a minute.
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_input | The body is not valid JSON or not an object, or an argument is wrong. message names the first problem, and issues lists what the schema check found (and, for log_bet, a game in another sport). |
| 401 | invalid_token | No token, a malformed one, or one that is unknown, revoked or expired. |
| 403 | forbidden | The wrong host, or a browser on another site. |
| 403 | insufficient_scope | A write tool with a read-only token. |
| 404 | unknown_tool | No tool by that name. |
| 404 | not_found | The game, bet or pick does not exist or is not yours, or a tool that sizes a stake finds no bankroll set up. |
| 413 | payload_too_large | The body is over 256 KB. |
| 415 | unsupported_media_type | A Content-Type other than JSON. A body with no Content-Type is read as JSON. |
| 422 | limit | A rule refused the request, such as a date range that is too long. |
| 429 | rate_limited | A budget is spent. Wait for Retry-After. |
| 500 | internal | Something failed on Unitley's side. |
| 503 | unavailable | Unitley could not serve the call just then, or the tool needs something you have not set up, such as jev_classify without a TypeSafe key. |
Each tool's own errors are listed under it in Every tool. Errors covers the error format and what to do about each code.
Examples
The first three examples run calculators, so they need no data and give the same answer every time. Replace YOUR_UNITLEY_TOKEN with your token.
No-vig odds
The fair odds of a market priced −110 on both sides:
curl -X POST https://console.unitley.com/api/v1/tools/calc_no_vig_odds \
-H "Authorization: Bearer YOUR_UNITLEY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"prices":[-110,-110]}'{
"data": {
"method": "multiplicative",
"overround": 0.04761904761904767,
"outcomes": [
{
"americanOdds": -110,
"impliedProbability": 0.5238095238095238,
"fairProbability": 0.5,
"fairDecimalOdds": 2,
"fairAmericanOdds": 100
},
{
"americanOdds": -110,
"impliedProbability": 0.5238095238095238,
"fairProbability": 0.5,
"fairDecimalOdds": 2,
"fairAmericanOdds": 100
}
]
}
}See No-vig odds.
Expected value
A bet at −110 that you give a 55% chance:
curl -X POST https://console.unitley.com/api/v1/tools/calc_ev \
-H "Authorization: Bearer YOUR_UNITLEY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"probability":0.55,"americanOdds":-110}'{
"data": {
"probability": 0.55,
"americanOdds": -110,
"decimalOdds": 1.9090909090909092,
"impliedProbability": 0.5238095238095238,
"edge": 0.02619047619047621,
"stake": 100,
"expectedValue": 5.000000000000021,
"expectedValuePercent": 0.050000000000000044
}
}See Expected value.
A parlay
Two legs, at −110 and +150, with no probabilities given:
curl -X POST https://console.unitley.com/api/v1/tools/calc_parlay \
-H "Authorization: Bearer YOUR_UNITLEY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"legs":[{"americanOdds":-110},{"americanOdds":150}]}'{
"data": {
"legs": 2,
"decimalOdds": 4.772727272727273,
"americanOdds": 377,
"impliedProbability": 0.2095238095238095,
"combinedProbability": null,
"expectedValuePercent": null,
"correlationWarnings": []
}
}combinedProbability and expectedValuePercent are null unless every leg has a probability.
A bad argument
calc_implied_probability with odds of 50, which American odds cannot be:
{
"error": {
"code": "invalid_input",
"message": "americanOdds: American odds must be +100 or more, or -100 or less.",
"issues": [
{
"path": "americanOdds",
"message": "American odds must be +100 or more, or -100 or less."
}
]
}
}A write tool with a read-only token
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer realm="unitley", error="insufficient_scope", scope="write"
Content-Type: application/json
{
"error": {
"code": "insufficient_scope",
"message": "log_bet saves to your account, and this access token is read-only. Create a token with write access on the Connect page to use it.",
"issues": []
}
}