> ## Documentation Index
> Fetch the complete documentation index at: https://01c.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Meshgen API

> Integrate 3D mesh generation into your applications using our REST API.

<Warning>
  **EXPERIMENTAL** — This API is in beta. We're evaluating demand to determine scaling priorities. Your feedback is invaluable — please share your experience on [Discord](https://discord.gg/RZjB7NVsJC) or via email.
</Warning>

Last updated: January 31, 2026

## Base URL

```
https://amara-meshgen-api.01c.ai/meshgen-api
```

## Authentication

All requests require a Bearer token:

```
Authorization: Bearer sk_amara_YOUR_API_KEY
```

Get your API key from the Amara web app: **Profile** → **Admin/Developer Panel** → **Developer API**.

## Endpoints

```
POST {base_url}                                    # Create generation
GET  {base_url}?id={generation_id}                 # Check status
GET  {base_url}?id={generation_id}&download=true   # Download GLB
```

### Create Generation

Start a new mesh generation from an image.

**Request Body:**

| Field              | Type   | Required | Description                                             |
| ------------------ | ------ | -------- | ------------------------------------------------------- |
| image              | string | Yes      | Base64-encoded image (PNG, JPEG, or WebP)               |
| mode               | string | No       | "fast", "standard", or "detailed" (default: "fast")     |
| prompt             | string | No       | Text prompt to guide mesh generation                    |
| seed               | number | No       | Seed for reproducible results (default: 42)             |
| decimation\_target | number | No       | Target triangle count, 100000-500000 (default: 300000)  |
| texture\_size      | number | No       | Texture resolution in pixels, 1024-4096 (default: 2048) |

**Response:**

```json theme={null}
{
  "generation_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "processing",
  "message": "Generation started. Poll the status endpoint for updates.",
  "credits_used": 1,
  "credits_remaining": 99,
  "key_monthly_used": 45,
  "key_monthly_limit": 100
}
```

Use `generation_id` for all subsequent API calls (status polling and downloads).

<Note>
  `key_monthly_used` and `key_monthly_limit` only appear if your API key has a monthly limit configured.
</Note>

### Check Status

Poll the status of a generation.

**Response (Queued):**

```json theme={null}
{
  "generation_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued",
  "queue_position": 5,
  "total_in_queue": 8
}
```

**Response (Processing):**

```json theme={null}
{
  "generation_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "processing",
  "progress": 45,
  "current_step": "Generating mesh",
  "queue_position": 1,
  "total_in_queue": 3
}
```

**Response (Completed):**

```json theme={null}
{
  "generation_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "glb_url": "https://...",
  "thumbnail_url": "https://...",
  "asset_name": "generated_asset_001",
  "expires_in": 3600
}
```

<Note>
  `expires_in` is the number of seconds until `glb_url` expires. Download and save to your own storage immediately. `queue_position` indicates your place in line (1 means actively processing).
</Note>

**Response (Failed):**

```json theme={null}
{
  "generation_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "failed",
  "error": "Generation failed",
  "credits_refunded": true
}
```

### Download GLB

Download the GLB file directly.

**Response:** Binary GLB file with headers:

* `Content-Type: model/gltf-binary`
* `Content-Disposition: attachment; filename="asset_name.glb"`

<Warning>
  **CRITICAL:** You MUST download and save the GLB file to your own storage immediately after generation completes. Files are NOT retained on our servers.
</Warning>

## Code Examples

<CodeGroup>
  ```python Python theme={null}
  import base64
  import time
  import requests

  API_KEY = "sk_amara_YOUR_API_KEY"
  BASE_URL = "https://amara-meshgen-api.01c.ai/meshgen-api"

  def generate_mesh(image_path: str, mode: str = "fast") -> str:
      """Generate a 3D mesh from an image. Returns the generation ID."""
      with open(image_path, "rb") as f:
          image_base64 = base64.b64encode(f.read()).decode("utf-8")

      response = requests.post(
          BASE_URL,
          headers={
              "Authorization": f"Bearer {API_KEY}",
              "Content-Type": "application/json",
          },
          json={"image": image_base64, "mode": mode},
          timeout=60,
      )
      response.raise_for_status()
      return response.json()["generation_id"]

  def poll_status(generation_id: str, timeout: int = 600) -> dict:
      """Poll until generation completes or fails."""
      start = time.time()
      while time.time() - start < timeout:
          response = requests.get(
              f"{BASE_URL}?id={generation_id}",
              headers={"Authorization": f"Bearer {API_KEY}"},
          )
          result = response.json()
          if result["status"] in ("completed", "failed"):
              return result
          time.sleep(5)
      raise TimeoutError("Generation timed out")

  def download_glb(generation_id: str, output_path: str):
      """Download the GLB file for a completed generation."""
      response = requests.get(
          f"{BASE_URL}?id={generation_id}&download=true",
          headers={"Authorization": f"Bearer {API_KEY}"},
          stream=True,
      )
      response.raise_for_status()
      with open(output_path, "wb") as f:
          for chunk in response.iter_content(chunk_size=8192):
              f.write(chunk)

  # Usage
  gen_id = generate_mesh("input.png", mode="fast")
  result = poll_status(gen_id)
  if result["status"] == "completed":
      download_glb(gen_id, "output.glb")
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = "sk_amara_YOUR_API_KEY";
  const BASE_URL = "https://amara-meshgen-api.01c.ai/meshgen-api";

  async function generateMesh(imageBase64, mode = "fast") {
    const response = await fetch(BASE_URL, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ image: imageBase64, mode }),
    });
    if (!response.ok) throw new Error("Failed to create generation");
    const data = await response.json();
    return data.generation_id;
  }

  async function pollStatus(generationId, timeoutMs = 600000) {
    const start = Date.now();
    while (Date.now() - start < timeoutMs) {
      const response = await fetch(`${BASE_URL}?id=${generationId}`, {
        headers: { "Authorization": `Bearer ${API_KEY}` },
      });
      const result = await response.json();
      if (result.status === "completed" || result.status === "failed") {
        return result;
      }
      await new Promise(resolve => setTimeout(resolve, 5000));
    }
    throw new Error("Generation timed out");
  }

  async function downloadGlb(generationId) {
    const response = await fetch(
      `${BASE_URL}?id=${generationId}&download=true`,
      { headers: { "Authorization": `Bearer ${API_KEY}` } }
    );
    return response.blob();
  }
  ```

  ```bash cURL theme={null}
  #!/bin/bash
  API_KEY="sk_amara_YOUR_API_KEY"
  BASE_URL="https://amara-meshgen-api.01c.ai/meshgen-api"

  # Encode image
  IMAGE_BASE64=$(base64 -w 0 input.png)

  # Create generation
  RESPONSE=$(curl -s -X POST "$BASE_URL" \
    -H "Authorization: Bearer $API_KEY" \
    -H "Content-Type: application/json" \
    -d "{\"image\": \"$IMAGE_BASE64\", \"mode\": \"fast\"}")

  GENERATION_ID=$(echo "$RESPONSE" | jq -r '.generation_id')

  # Poll for completion
  while true; do
    STATUS=$(curl -s "$BASE_URL?id=$GENERATION_ID" -H "Authorization: Bearer $API_KEY")
    STATE=$(echo "$STATUS" | jq -r '.status')
    echo "Status: $STATE"
    [ "$STATE" = "completed" ] && break
    [ "$STATE" = "failed" ] && exit 1
    sleep 5
  done

  # Download GLB
  curl -s "$BASE_URL?id=$GENERATION_ID&download=true" \
    -H "Authorization: Bearer $API_KEY" -o output.glb
  ```
</CodeGroup>

## Best Practices

1. **Download & Store Immediately** — You MUST download and save GLB files to your own storage immediately. Files are NOT retained on our servers.
2. **Poll Every 5 Seconds** — Check status every 5 seconds. Handle `queue_position` in the response to show users their place in line.
3. **Reproducible Results** — Use the same `seed` value to regenerate identical meshes from the same input image.
4. **Resize Images Client-Side** — Resize to max 1024x1024 before encoding to reduce payload size and improve performance.
5. **Exponential Backoff** — For production, implement exponential backoff with jitter when polling and retrying.
6. **Handle Rate Limits** — Check for `retry_after_seconds` in 429 responses and wait accordingly.
7. **Store Generation IDs** — Persist generation IDs so you can recover from crashes and resume polling later.
8. **Tune Output Quality** — Adjust `decimation_target` (triangle count) and `texture_size` to balance quality vs file size.

## Generation Modes

| Mode     | Resolution | Credits | Processing Time | Best For                  |
| -------- | ---------- | ------- | --------------- | ------------------------- |
| fast     | 512px      | 1       | \~30s-2 min     | Previews, prototyping     |
| standard | 1024px     | 1       | \~2-4 min       | Production assets         |
| detailed | 1536px     | 1       | \~4-8 min       | High-quality final assets |

## Image Requirements

| Constraint       | Limit           | Notes                                             |
| ---------------- | --------------- | ------------------------------------------------- |
| Max payload size | 3MB             | Base64-encoded; raw files up to \~2.25MB will fit |
| Max dimension    | 1024px          | Longest side; resize larger images                |
| Formats          | PNG, JPEG, WebP | Data URLs or raw base64 accepted                  |

## Rate Limits

| Limit               | Value            | Scope                  |
| ------------------- | ---------------- | ---------------------- |
| Generation requests | 5 per 15 minutes | Per API key            |
| Monthly credits     | Configurable     | Per API key (optional) |

<Note>
  Only generation creation (POST) counts against rate limits. Status polling (GET) and downloads are NOT rate-limited, so you can poll as frequently as needed.
</Note>

## Error Codes

### HTTP Status Codes

| Code | Meaning             | Action                          |
| ---- | ------------------- | ------------------------------- |
| 200  | Success             | Process the response            |
| 400  | Bad request         | Check image format/size         |
| 401  | Unauthorized        | Verify API key                  |
| 402  | Payment required    | Add credits or check key limits |
| 403  | Forbidden           | Key doesn't have meshgen scope  |
| 429  | Rate limited        | Wait and retry                  |
| 503  | Service unavailable | Backend busy; credits refunded  |

### Error Response Codes

| Code                     | HTTP | Description                                 |
| ------------------------ | ---- | ------------------------------------------- |
| IMAGE\_VALIDATION\_ERROR | 400  | Image too large, invalid format, or corrupt |
| BILLING\_ERROR           | 402  | Organization has insufficient credits       |
| KEY\_LIMIT\_EXCEEDED     | 402  | API key's monthly limit reached             |
| CREDIT\_ERROR            | 402  | Failed to deduct credits                    |
| RATE\_LIMIT\_EXCEEDED    | 429  | Too many requests                           |
| BACKEND\_TIMEOUT         | 503  | Generation service timed out                |
| BACKEND\_ERROR           | 503  | Generation service unavailable              |

<Note>
  On 503 errors, credits are automatically refunded. Check for `credits_refunded: true` in the response.
</Note>

### Example Error Responses

**400 - Image Validation Error:**

```json theme={null}
{
  "error": "Image too large: 5.2MB. Maximum size: 3MB.",
  "code": "IMAGE_VALIDATION_ERROR",
  "limits": {
    "max_size_mb": 3,
    "supported_formats": ["PNG", "JPEG", "WebP"]
  }
}
```

**429 - Rate Limit Exceeded:**

```json theme={null}
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "rate_limit": {
    "limit": 5,
    "used": 5,
    "remaining": 0,
    "retry_after_seconds": 847
  }
}
```

**402 - Insufficient Credits:**

```json theme={null}
{
  "error": "Insufficient credits. Required: 1, Available: 0",
  "code": "BILLING_ERROR",
  "credits_required": 1,
  "credits_remaining": 0
}
```

## Support

* [Discord](https://discord.gg/RZjB7NVsJC)
* [Support Center](https://amara.01c.ai/support)
* [Email: info@01c.ai](mailto:info@01c.ai)
