{ "outputs": { … } }, so one small client covers all 800+.
- Node.js
- Python
- Go
- cURL
const API = "https://api.iotools.cloud/v1/tool";
export async function runTool(slug, inputs) {
const res = await fetch(`${API}/${slug}`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.IOTOOLS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(inputs),
});
const body = await res.json();
// Branch on `code`, never on the human-readable title.
if (!res.ok) throw new Error(`${body.code}: ${body.detail ?? body.title}`);
return body.outputs;
}
const { ioOutputString } = await runTool("case-converter", {
ioInputString: "hello world",
ioCase: "uppercase",
});
import os, requests
API = "https://api.iotools.cloud/v1/tool"
def run_tool(slug: str, inputs: dict) -> dict:
res = requests.post(
f"{API}/{slug}",
headers={"Authorization": f"Bearer {os.environ['IOTOOLS_API_KEY']}"},
json=inputs,
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['code']}: {body.get('detail', body['title'])}")
return body["outputs"]
out = run_tool("case-converter", {"ioInputString": "hello world", "ioCase": "uppercase"})
package iotools
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func RunTool(slug string, inputs map[string]string) (map[string]string, error) {
payload, _ := json.Marshal(inputs)
req, _ := http.NewRequest("POST",
"https://api.iotools.cloud/v1/tool/"+slug, bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("IOTOOLS_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var body struct {
Outputs map[string]string `json:"outputs"`
Code string `json:"code"`
Detail string `json:"detail"`
}
json.NewDecoder(res.Body).Decode(&body)
if res.StatusCode >= 400 {
return nil, fmt.Errorf("%s: %s", body.Code, body.Detail)
}
return body.Outputs, nil
}
#!/usr/bin/env bash
# run-tool.sh <slug> <json-fields> ← $2 is the whole body, sent as-is
curl -sS -X POST "https://api.iotools.cloud/v1/tool/$1" \
-H "Authorization: Bearer $IOTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d "$2"
./run-tool.sh case-converter '{"ioInputString":"hello","ioCase":"uppercase"}'
Retrying correctly
Retry429 and 5xx. Never retry a 4xx other than 429 — a validation_error will fail identically forever, and tool_not_found means the slug is wrong.
async function runWithRetry(slug, inputs, attempts = 3) {
for (let i = 0; i < attempts; i++) {
const res = await fetch(`${API}/${slug}`, { /* … */ });
if (res.ok) return (await res.json()).outputs;
const body = await res.json();
const retryable = res.status === 429 || res.status >= 500;
if (!retryable || i === attempts - 1) throw new Error(body.code);
// Honour Retry-After when the server sends one — it is the real window.
const wait = Number(res.headers.get("Retry-After")) || 2 ** i;
await new Promise((r) => setTimeout(r, wait * 1000));
}
}