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

> ## Agent Instructions
> MakeSeamless (makeseamless.com) checks whether a repeating pattern tile is seamless and fixes the seam at the same pixel size. Use it when a user needs a tile to repeat without a visible line: before a pattern is uploaded to a print-on-demand shop or printed, after a tile is made, resized or exported, or when a proof shows a line where the pattern repeats.
> Check first: POST https://makeseamless.com/v1/check with the image as the multipart field file. It is free and needs no account and no key. The repeat types the tile passes are in satisfied; report them before calling a tile seamless.
> Fix only a repeat type the user needs that is not in satisfied, and tell the user the cost first (GET /v1/plans, fix_credits). POST /v1/fix needs an API key in the x-api-key header. Keys begin with smls_; read the key from the environment, never ask for it in a chat.
> POST /v1/fix answers 202 with a job. Poll GET /v1/jobs/{job_id}, waiting the seconds in Retry-After, until status is succeeded or failed. The fixed PNG is at result.image_url. Answers are JSON, except the images, which are PNG.
> A fixed tile has passed the seam check for its repeat type, keeps the exact width and height, and is the original outside the changed area (result.changed_mask_url). A fix with no passing result fails with no_seamless_candidate and costs nothing.
> Branch on error.code, never on error.message, and retry only when error.retryable is true.

# MakeSeamless API error codes and when to retry them

> Every stable error code with its status and meaning, which ones a retry can help, and a retry loop that waits as Retry-After says. Branch on error.code.

Every failure has the same shape, on every endpoint. Branch on `code`, never
on `message`: codes are stable, and messages are plain language that may
change. Keep a default case that shows `message`, so a code you do not know
yet degrades into a readable failure instead of a crash.

```json Error theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "error": {
    "code": "insufficient_credits",
    "message": "Not enough credits for this fix.",
    "retryable": false,
    "details": {
      "credits_needed": 1
    }
  }
}
```

| Field | What it is |
| - | - |
| `code` | The stable code. The tables below list every one. |
| `message` | A plain-language explanation for people. Do not parse it. |
| `retryable` | True when the same request can succeed later, unchanged. |
| `details` | Optional machine-readable context, such as `credits_needed`. |

A `429` and a `409` `result_not_ready` carry `Retry-After`, the seconds to
wait before you try again.

## Which status codes can the API answer?

| Status | Meaning |
| - | - |
| `200` | The request succeeded. |
| `201` | The rating was stored. |
| `202` | The fix was accepted; poll the job. |
| `401` | The API key is missing, revoked or unknown. |
| `402` | Not enough credits for the fix. |
| `404` | No such job or image, or the image is no longer stored. |
| `409` | The job has no image yet, or none at all, or its rating window closed. |
| `413` | The file is larger than 40 MB. |
| `422` | The request or the image cannot be used. |
| `429` | A limit was reached. Wait the seconds in `Retry-After`. |
| `500` | Something went wrong on our side. |

## Which errors mean the image cannot be used?

| Status | Code | Retry | What to do |
| - | - | - | - |
| `413` | `payload_too_large` | no | The file is over 40 MB. Send a smaller file. |
| `422` | `validation_error` | no | A field is missing or has a value the API does not know. |
| `422` | `unsupported_format` | no | Send a PNG, JPEG or WebP image. |
| `422` | `decode_failed` | no | The file did not open as an image. Save it again, for example as PNG. |
| `422` | `too_small` | no | The image is under 64 px on a side. |
| `422` | `too_large` | no | A check takes at most 64 megapixels, a fix at most 3600 px on a side. `details` may say the limit. |
| `422` | `odd_dimension` | no | Fix only: half-drop needs an even height and half-brick an even width. Choose another repeat type or resize the tile. |
| `422` | `transparent_seam_band` | no | Fix only: the tile has transparent pixels near its edges. Fill them, then send the tile again. |

## Which errors are about the key or the credits?

| Status | Code | Retry | What to do |
| - | - | - | - |
| `401` | `auth_required` | no | Send a valid key in the `x-api-key` header. The key may be missing, revoked or unknown. |
| `402` | `insufficient_credits` | no | Tell the user `details.credits_needed`. Credits come with a Pro plan on makeseamless.com, and a Pro account can buy top-ups. |

## Which errors mean a limit was reached?

| Status | Code | Retry | What to do |
| - | - | - | - |
| `429` | `rate_limited` | yes | Too many checks from this client. Wait `Retry-After` seconds. |
| `429` | `too_many_running_fixes` | yes | The account already has 3 fixes queued or running. Wait for one to finish. |
| `429` | `daily_capacity_reached` | yes | Fixes are at capacity for now. Try again after `Retry-After` seconds. |

[Rate and spend limits](https://makeseamless.com/docs/api-reference/limits.md) has the numbers behind them.

## Which errors come from jobs and their images?

| Status | Code | Retry | What to do |
| - | - | - | - |
| `404` | `not_found` | no | No job with this id, no image with this id in the job, or an image older than 30 days. |
| `409` | `result_not_ready` | yes | The job is still `queued` or `running`. Poll it again after `Retry-After`. |
| `409` | `no_result` | no | The job failed, so it has no image. |
| `409` | `rating_closed` | no | Ratings close 30 days after the job was created. |

## Why did a fix job fail?

A job that ends in `failed` carries its own `error`, in the same shape.

| Code | Retry | What happened |
| - | - | - |
| `no_seamless_candidate` | no | No version of the tile passed the seam check. No image and no charge. |
| `service_unavailable` | yes | The fix could not run for now. No charge; start it again later. |
| `interrupted` | yes | The service stopped while the job ran. No charge; start the fix again. |
| `internal_error` | no | Something went wrong on our side. |

## Which error can any request return?

| Status | Code | Retry | What to do |
| - | - | - | - |
| `500` | `internal_error` | no | Something went wrong on our side. Try again later, and write to [hello@makeseamless.com](mailto:hello@makeseamless.com) if it stays. |

## Which errors come from billing on makeseamless.com?

Three codes come only from the billing pages of makeseamless.com, which are
used signed in and never with an API key. They are listed so the set is
complete.

| Status | Code | Meaning |
| - | - | - |
| `403` | `pro_required` | Top-up credits are for Pro plans. |
| `409` | `already_subscribed` | The account already has a subscription. |
| `409` | `no_subscription` | The account has no subscription to change. |

## When should I retry a request?

Retry only when `retryable` is true. Wait as `Retry-After` says when it is
present, and otherwise start at 1 second and double the wait each time. A fix
that failed with `service_unavailable` or `interrupted` cost nothing, so a new
`POST /v1/fix` is safe. An answer with no JSON body, such as a `502`, `503`
or `504` from the network, can be retried the same way.

```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
import time

import requests


def call(send, attempts=5):
    wait = 1
    for _ in range(attempts):
        r = send()
        if r.ok:
            return r
        try:
            error = r.json()["error"]
        except (ValueError, KeyError):
            error = {"retryable": r.status_code >= 502}
        if not error.get("retryable"):
            r.raise_for_status()
        time.sleep(int(r.headers.get("Retry-After", wait)))
        wait *= 2
    r.raise_for_status()
```

```js Node.js theme={"theme":{"light":"github-light","dark":"vesper"}}
async function call(send, attempts = 5) {
  let wait = 1;
  let res;
  for (let i = 0; i < attempts; i++) {
    res = await send();
    if (res.ok) return res;
    const body = await res.clone().json().catch(() => null);
    const retryable = body?.error?.retryable ?? res.status >= 502;
    if (!retryable) break;
    const after = Number(res.headers.get("Retry-After") ?? wait);
    await new Promise((done) => setTimeout(done, after * 1000));
    wait *= 2;
  }
  throw new Error(`${res.status}`);
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.