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

# Quickstart

> Get an API key and generate your first handwriting output in minutes.

## 1) Get an API key

Create an API key in the [HandTextAI dashboard](https://dashboard.handtextai.com) and copy the value (it starts with `htext_`).

## 2) Generate a PNG

<Tip>
  For the most predictable production output, start with `font_id: 1` or `2` and validate your final settings with `/preview` before you ship.
</Tip>

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    curl -sS https://api.handtextai.com/api/v1/generate \
      -H "Authorization: Bearer htext_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "text": "Hello from HandTextAI!",
        "font_id": 1,
        "font_size": "auto",
        "page_size": "a4",
        "dpi": 300
      }' \
      | python3 - <<'PY'
    import sys, json, base64
    payload = json.load(sys.stdin)
    img = base64.b64decode(payload["image_base64"])
    open("output.png", "wb").write(img)
    print("Saved output.png (request_id:", payload.get("request_id"), ")")
    PY
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests
    import base64

    API_KEY = "htext_YOUR_KEY"

    response = requests.post(
        "https://api.handtextai.com/api/v1/generate",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "text": "Hello from HandTextAI!",
            "font_id": 1,
            "font_size": "auto",
            "page_size": "a4",
            "dpi": 300,
        },
    )

    if response.status_code == 200:
        data = response.json()
        image_bytes = base64.b64decode(data["image_base64"])
        with open("output.png", "wb") as f:
            f.write(image_bytes)
        print(f"Saved output.png ({data['width']}x{data['height']})")
    else:
        print(f"Error {response.status_code}: {response.text}")
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const API_KEY = "htext_YOUR_KEY";

    const response = await fetch("https://api.handtextai.com/api/v1/generate", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        text: "Hello from HandTextAI!",
        font_id: 1,
        font_size: "auto",
        page_size: "a4",
        dpi: 300,
      }),
    });

    if (response.ok) {
      const data = await response.json();
      const imageBuffer = Buffer.from(data.image_base64, "base64");
      require("fs").writeFileSync("output.png", imageBuffer);
      console.log(`Saved output.png (${data.width}x${data.height})`);
    } else {
      console.error(`Error ${response.status}: ${await response.text()}`);
    }
    ```
  </Tab>
</Tabs>

## 3) Preview before you generate (free)

Use `/preview` to iterate on margins/font sizing quickly. Previews are watermarked and free.

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    curl -sS https://api.handtextai.com/api/v1/preview \
      -H "Authorization: Bearer htext_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{"text": "Hello!", "font_id": 1, "font_size": "auto", "page_size": "a4"}'
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.post(
        "https://api.handtextai.com/api/v1/preview",
        headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
        json={"text": "Hello!", "font_id": 1, "font_size": "auto", "page_size": "a4"},
    )
    data = response.json()
    print(f"Preview: {data['preview_width']}x{data['preview_height']}")
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const previewResponse = await fetch("https://api.handtextai.com/api/v1/preview", {
      method: "POST",
      headers: {"Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json"},
      body: JSON.stringify({text: "Hello!", font_id: 1, font_size: "auto", page_size: "a4"}),
    });
    const preview = await previewResponse.json();
    console.log(`Preview: ${preview.preview_width}x${preview.preview_height}`);
    ```
  </Tab>
</Tabs>

## 4) Generate a PDF

PDF responses are binary, not JSON.

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    curl -sS https://api.handtextai.com/api/v1/generate \
      -H "Authorization: Bearer htext_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "text": "Hello from HandTextAI!",
        "font_id": 1,
        "font_size": "auto",
        "page_size": "letter",
        "output_format": "pdf"
      }' \
      -o output.pdf
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    response = requests.post(
        "https://api.handtextai.com/api/v1/generate",
        headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
        json={
            "text": "Hello from HandTextAI!",
            "font_id": 1,
            "font_size": "auto",
            "page_size": "letter",
            "output_format": "pdf",
        },
    )

    if response.status_code == 200:
        with open("output.pdf", "wb") as f:
            f.write(response.content)
        print("Saved output.pdf")
    else:
        print(f"Error {response.status_code}: {response.text}")
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    const pdfResponse = await fetch("https://api.handtextai.com/api/v1/generate", {
      method: "POST",
      headers: {"Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json"},
      body: JSON.stringify({
        text: "Hello from HandTextAI!",
        font_id: 1,
        font_size: "auto",
        page_size: "letter",
        output_format: "pdf",
      }),
    });

    if (pdfResponse.ok) {
      const buffer = await pdfResponse.arrayBuffer();
      require("fs").writeFileSync("output.pdf", Buffer.from(buffer));
      console.log("Saved output.pdf");
    }
    ```
  </Tab>
</Tabs>

## 5) Handle rate limits

When you hit rate limits, use the `Retry-After` header:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import time

    def generate_with_retry(request_data, max_retries=3):
        for attempt in range(max_retries):
            response = requests.post(
                "https://api.handtextai.com/api/v1/generate",
                headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
                json=request_data,
            )

            if response.status_code == 429:
                retry_after = int(response.headers.get("Retry-After", 60))
                print(f"Rate limited, waiting {retry_after}s...")
                time.sleep(retry_after)
                continue

            return response

        raise Exception("Max retries exceeded")
    ```
  </Tab>

  <Tab title="JavaScript">
    ```javascript theme={null}
    async function generateWithRetry(requestData, maxRetries = 3) {
      for (let attempt = 0; attempt < maxRetries; attempt++) {
        const response = await fetch("https://api.handtextai.com/api/v1/generate", {
          method: "POST",
          headers: {"Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json"},
          body: JSON.stringify(requestData),
        });

        if (response.status === 429) {
          const retryAfter = parseInt(response.headers.get("Retry-After") || "60");
          console.log(`Rate limited, waiting ${retryAfter}s...`);
          await new Promise(r => setTimeout(r, retryAfter * 1000));
          continue;
        }

        return response;
      }
      throw new Error("Max retries exceeded");
    }
    ```
  </Tab>
</Tabs>

## Need to generate many outputs?

Use the dashboard's [Batch Generation](/guides/batch-generation) for CSV-based bulk processing.

## Troubleshooting

| Error | Cause            | Solution                                           |
| ----- | ---------------- | -------------------------------------------------- |
| `401` | Invalid API key  | Check `Authorization: Bearer htext_...` format     |
| `422` | Validation error | Check `details` array in response for field errors |
| `429` | Rate/quota limit | Wait for `Retry-After` seconds, then retry         |

See [Errors & Rate Limits](/errors) for complete error reference.
