Skip to content

AI generation API code examples

Working recipes for the FreeInd API. Each example creates a text-to-image generation and polls to completion — replace API_KEY, WORKSPACE_ID, and MODEL_ID with your values.

curl

Create and poll in bashbash
API_KEY=fi_live_...
WORKSPACE_ID=00000000-0000-4000-8000-000000000000
MODEL_ID=00000000-0000-4000-8000-000000000001
BASE=https://free.ind.in/v1

# Create (idempotent with the header)
GENERATION=$(curl -s -X POST $BASE/generations \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: fox-1" \
  -H "Content-Type: application/json" \
  -d "{\"workspaceId\":\"$WORKSPACE_ID\",\"modelId\":\"$MODEL_ID\",\"params\":{\"mode\":\"text_to_image\",\"prompt\":\"a red fox in snow\"}}" \
  | jq -r .id)

# Poll
while true; do
  STATUS=$(curl -s "$BASE/generations/$GENERATION?workspaceId=$WORKSPACE_ID" \
    -H "Authorization: Bearer $API_KEY" | jq -r .status)
  echo "status: $STATUS"
  [ "$STATUS" = "succeeded" ] || [ "$STATUS" = "failed" ] && break
  sleep 2
done

JavaScript (Node 18+)

Create and poll in JSjavascript
const API_KEY = process.env.FREEIND_API_KEY;
const WORKSPACE_ID = process.env.FREEIND_WORKSPACE_ID;
const BASE = 'https://free.ind.in/v1';

const headers = {
  Authorization: `Bearer ${API_KEY}`,
  'Content-Type': 'application/json',
};

async function api(path, init = {}) {
  const res = await fetch(`${BASE}${path}`, { ...init, headers: { ...headers, ...init.headers } });
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error?.code}: ${body.error?.message}`);
  return body;
}

const { id } = await api('/generations', {
  method: 'POST',
  headers: { 'Idempotency-Key': 'fox-1' },
  body: JSON.stringify({
    workspaceId: WORKSPACE_ID,
    modelId: '00000000-0000-4000-8000-000000000001',
    params: { mode: 'text_to_image', prompt: 'a red fox in snow' },
  }),
});

let generation;
do {
  await new Promise((r) => setTimeout(r, 2000));
  generation = await api(`/generations/${id}?workspaceId=${WORKSPACE_ID}`);
  console.log('status:', generation.status);
} while (['queued', 'reserved', 'submitted', 'running'].includes(generation.status));

Python

Create and poll in Pythonpython
import json
import time
import urllib.request

API_KEY = "fi_live_..."
WORKSPACE_ID = "00000000-0000-4000-8000-000000000000"
BASE = "https://free.ind.in/v1"


def api(path, method="GET", body=None, headers=None):
    request = urllib.request.Request(
        f"{BASE}{path}",
        method=method,
        data=json.dumps(body).encode() if body else None,
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
            **(headers or {}),
        },
    )
    with urllib.request.urlopen(request) as response:
        return json.load(response)


creation = api(
    "/generations",
    method="POST",
    headers={"Idempotency-Key": "fox-1"},
    body={
        "workspaceId": WORKSPACE_ID,
        "modelId": "00000000-0000-4000-8000-000000000001",
        "params": {"mode": "text_to_image", "prompt": "a red fox in snow"},
    },
)

while True:
    generation = api(f"/generations/{creation['id']}?workspaceId={WORKSPACE_ID}")
    print("status:", generation["status"])
    if generation["status"] in ("succeeded", "failed", "cancelled", "expired"):
        break
    time.sleep(2)

Webhook verification (any language)

On terminal generation states, FreeInd POSTs the event to your URL signed with X-FreeInd-Signature: t=<unix-timestamp>,v1=<hmac-sha256(secret, t + "." + body)>. Verify with the signing secret shown when you created the endpoint:

HMAC-SHA256 verificationjavascript
import { createHmac, timingSafeEqual } from 'node:crypto';

function verifySignature(secret, rawBody, header) {
  const [timestamp, signature] = header.slice(3).split(',v1=');
  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
  return timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Deliveries retry with exponential backoff (up to 5 attempts) and are listed at GET /v1/webhooks/:id/deliveries. See Webhooks for the full guide.

Wallet-backed generation

Pass "paymentMode": "wallet" in the generation payload to pay from the pay-as-you-go wallet instead of plan credits:

Pay with the walletbash
curl -X POST https://free.ind.in/v1/generations \
  -H "Authorization: Bearer fi_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId": "<workspace-id>",
    "modelId": "<model-id>",
    "paymentMode": "wallet",
    "params": { "mode": "text_to_image", "prompt": "a red fox in snow" }
  }'

The estimated charge (credits × $0.01) is reserved before the job starts and settled on completion. Wallet payments require the workspace to have deposited the $10 minimum — see Billing & wallet.

More

Input files (image/video modes)

For image_to_image, image_to_video, and video_to_video modes, the source file goes in inputs either way:

Option 1 — upload (private): request a presigned URL with POST /v1/files/upload-url, PUT the bytes, finalize with POST /v1/files/upload-complete, then pass the asset ids in inputs.imageAssetIds / inputs.videoAssetIds.

Option 2 — direct URL (no upload): pass a public http(s) URL or a base64 data URL in inputs.imageUrls / inputs.videoUrls. Data URLs must declare a matching mime type (data:image/… for image modes, data:video/… for video-to-video); http(s) targets must be publicly reachable (private network addresses are rejected). See the API reference.

Note

All examples here are also maintained in the repo under docs/api/examples.md.

Cookies keep FreeInd working. Optional analytics require your permission. Read our cookie policy.