Tools
List, inspect, and run Melious-hosted tools — web search, URL scraping, code execution, document conversion — over REST
Tools are server-side capabilities you call over REST — web search, URL scraping, code execution, image generation, and a handful of utility tools. You run them directly, or hand their definitions to a chat model and let it decide when to call them.
Base path: /v1/tools
Auth: Bearer token or x-api-key. Requires scope kit.tools. (Document conversion is its own endpoint with its own scope — see Document conversion.)
Two ways to use a tool
Call it yourself. Hit POST /v1/tools/{slug}/execute with the parameters and read the result. This is the path when your own code decides what to run.
Let a model call it. Fetch the tool definitions in OpenAI function format from GET /v1/tools/openai, pass them to Chat completions, and run whichever ones the model asks for. The model picks; you execute.
The second path is the usual one for agents. It's the standard function-calling loop from Tool calling, with the function bodies living on our side instead of yours.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /v1/tools | List available tools |
GET | /v1/tools/openai | The same tools as OpenAI function definitions |
GET | /v1/tools/{slug} | One tool's metadata |
GET | /v1/tools/{slug}/schema | One tool's parameter JSON Schema |
POST | /v1/tools/{slug}/execute | Run a tool |
GET | /v1/tools/{slug}/rate-limit | Current rate-limit state for a tool |
List tools
GET /v1/toolsReturns the tools your key can run, paginated.
| Query param | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number. |
page_size | integer | 50 | Items per page, max 100. |
source | string | all | Filter by native, managed, or remote. |
tool_type | string | all | Filter by handler, mcp, or api. |
category | string | all | Filter by category (e.g. search). |
search | string | none | Free-text match against name and description. |
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "web_search",
"slug": "web-search",
"source": "native",
"tool_type": "handler",
"display_name": "Web Search",
"description": "Search the web for current information",
"category": "search",
"icon": "search",
"parameters_schema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search query" },
"max_results": { "type": "integer", "minimum": 1, "maximum": 10, "default": 5 },
"timeframe": { "type": "string", "enum": ["day", "week", "month", "year", "all"], "default": "all" },
"language": { "type": "string", "default": "en" },
"detailed": { "type": "boolean", "default": false }
},
"required": ["query"]
},
"supports_streaming": false,
"cost_per_call": "0.004"
}
],
"total": 9,
"page": 1,
"page_size": 50,
"total_pages": 1
}name is the machine identifier (snake_case) — the human-readable label is display_name. source tells you where a tool comes from: native is built into Melious, managed is an integration we operate, remote is one you configured. tool_type tells you how it runs: handler is native code, mcp is a Model Context Protocol backend, api is an external HTTP call.
Today every published tool is native + handler. The managed/remote/mcp/api values remain in the enum for future use, but no such tools are currently live.
Get a tool's details and schema
GET /v1/tools/{slug}
GET /v1/tools/{slug}/schemaThe first returns the full metadata object (same shape as a list entry). The second returns just the parameters_schema — the JSON Schema your execute call has to satisfy. Fetch the schema before executing if you're building the request dynamically.
curl https://api.melious.ai/v1/tools/web-search/schema \
-H "Authorization: Bearer sk-mel-<YOUR_API_KEY>"Execute a tool
POST /v1/tools/{slug}/executeRun the tool. Pass arguments under parameters (a bare top-level object works too):
import httpx
response = httpx.post(
"https://api.melious.ai/v1/tools/web-search/execute",
headers={"Authorization": "Bearer sk-mel-<YOUR_API_KEY>"},
json={"parameters": {"query": "Hanseatic League history", "max_results": 5}},
).json()
print(response["result"])const response = await fetch(
"https://api.melious.ai/v1/tools/web-search/execute",
{
method: "POST",
headers: {
"Authorization": "Bearer sk-mel-<YOUR_API_KEY>",
"Content-Type": "application/json",
},
body: JSON.stringify({
parameters: { query: "Hanseatic League history", max_results: 5 },
}),
},
).then((r) => r.json());
console.log(response.result);curl https://api.melious.ai/v1/tools/web-search/execute \
-H "Authorization: Bearer sk-mel-<YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"parameters": {"query": "Hanseatic League history", "max_results": 5}}'The response wraps the result with execution metadata:
{
"success": true,
"status": "success",
"result": [
{ "title": "The Hanseatic League", "url": "https://...", "content": "..." }
],
"error": null,
"execution_time_ms": 1250
}| Field | Type | Description |
|---|---|---|
success | boolean | Whether the run succeeded. Check this before reading result. |
status | string | success, error, or timeout. |
result | array | object | Tool-specific output. Shape varies per tool — search and scraping return an array of results, each with title, url, and content. |
error | string | Human-readable message when success is false, otherwise null. |
execution_time_ms | integer | Wall-clock time the tool took. |
Use tools with chat completions
To let a model call these tools, fetch their OpenAI function definitions and pass them straight into Chat completions:
GET /v1/tools/openai{
"object": "list",
"data": [
{
"slug": "web-search",
"type": "function",
"function": {
"name": "web_search",
"description": "Search the web for current information",
"parameters": {
"type": "object",
"properties": { "query": { "type": "string" } },
"required": ["query"]
}
}
}
],
"total": 9,
"page": 1,
"page_size": 50,
"usage_hint": "Pass tool slugs in the 'tools' array of /v1/chat/completions"
}Each entry carries both the function definition (what the model reads) and the slug (what you execute). The loop:
- Pass the
functionobjects in thetoolsarray of a chat request. - When the model returns a
tool_callsentry, map itsfunction.nameback to the matchingslug. - Run
POST /v1/tools/{slug}/executewith the model's arguments. - Feed the result back as a
toolmessage and continue.
The full loop, with code, is in Tool calling. The only Melious-specific step is mapping the function name to a slug and calling execute — everything else is the standard OpenAI flow.
Available tools
What we run today. The live list — with current per-call costs and parameter schemas — is GET /v1/tools.
| Tool | Slug | Category | What it does |
|---|---|---|---|
| Web search | web-search | search | Search the web for current information. |
| URL scraper | scrape-url | data | Fetch a URL and return its content as Markdown. |
| Code execution | code-execution | code | Execute Python 3.12 in a sandboxed environment. |
| Image generation | image-generation | media | Generate images from text descriptions. |
| Knowledge search | search-knowledge | search | Semantic search over attached knowledge bases. |
| Load skill | load-skill | utility | Load a skill from the user's library. |
| Read skill document | read-skill-document | utility | Read a document from a loaded skill. |
| Describe images | describe-images | multimodality | Describe attached images with a vision model. |
| Transcribe audio | transcribe-audio | multimodality | Transcribe attached audio files. |
Per-call pricing: web-search €0.004, scrape-url €0.002, code-execution €0.004. The rest are free at the per-call level (image generation is billed by the underlying model instead).
Document conversion (POST /v1/documents/convert) is documented separately — it takes file uploads rather than JSON arguments, so it doesn't share the execute shape.
Rate limits
GET /v1/tools/{slug}/rate-limitTools are rate-limited per tool, separately from the inference rate limit. Check the current state before a heavy run:
{
"remaining_minute": 30,
"remaining_day": 500,
"limit_minute": 30,
"limit_day": 500
}Limits vary per tool (the example above is web-search). Values count down in the current window; when either hits zero the tool is rate-limited.
Exceeding a limit returns KIT_6053 (execution rate-limited). Back off and retry.
Errors
A tool that runs but fails internally returns success: false with the message in error — it's a 200 with a failure body, not an HTTP error, so check the field:
{
"success": false,
"status": "timeout",
"result": null,
"error": "Tool execution timed out after 30000ms",
"execution_time_ms": 30000
}Parameter validation failures are different — they're rejected before execution, so they come back as HTTP errors with KIT_6054, not as a success: false body.
Endpoint-level failures use the standard {"error": {"code", "message"}} shape:
KIT_6001— tool slug doesn't exist.KIT_6054— a parameter failed schema validation (e.g.max_resultsoutside 1–10).KIT_6051— the tool ran but failed internally.KIT_6053— per-tool rate limit hit.BILLING_2001— insufficient balance for a paid tool.AUTH_1015— key is missing thekit.toolsscope.
A malformed request body (unparseable JSON, missing required field) is rejected before routing with a generic gateway response — {"code": "ERROR", "message": "Invalid request. Please check your input and try again."} — instead of a specific tool code. Fix the request shape first.
Related
Tool calling for the model-driven loop • Document conversion for file-to-Markdown • MCP for connecting protocol-native agents.