> ## Documentation Index
> Fetch the complete documentation index at: https://iotools.cloud/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Code samples

> A reusable client in four languages

Every endpoint takes the tool's fields as the body — flat, no wrapper — and returns `{ "outputs": { … } }`, so one small client covers all 800+.

<Tabs>
  <Tab title="Node.js">
    ```javascript theme={"system"}
    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",
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    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"})
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    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
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"system"}
    #!/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"
    ```

    ```bash theme={"system"}
    ./run-tool.sh case-converter '{"ioInputString":"hello","ioCase":"uppercase"}'
    ```
  </Tab>
</Tabs>

## Retrying correctly

Retry `429` 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.

```javascript theme={"system"}
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));
  }
}
```

Failed calls cost no credits, but they still consume rate limit.
