> ## 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.

# Check whether a pattern tile repeats without a seam

> Says, for each requested repeat type, whether the image tiles seamlessly and where its edges
break when copies of it meet. Only the tile's own edges are read; a seam inside the tile is not
checked, and the summary says so. Inputs: PNG, JPEG or WebP, at most 40 MB, at least 64 x 64 px,
at most 64 MP. A repeat type that does not apply (an odd height for half-drop, an odd width for
half-brick) is reported as `not_applicable`. No account is needed; checks are limited per client.


```js Node.js theme={"theme":{"light":"github-light","dark":"vesper"}}
import { readFile } from "node:fs/promises";

const form = new FormData();
form.append("file", new Blob([await readFile("tile.png")]), "tile.png");

const response = await fetch("https://makeseamless.com/v1/check", {
  method: "POST",
  body: form,
});

const data = await response.json();
console.log(data);
```

```php PHP theme={"theme":{"light":"github-light","dark":"vesper"}}
<?php

$ch = curl_init("https://makeseamless.com/v1/check");
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    "file" => new CURLFile("tile.png"),
]);

$response = curl_exec($ch);
curl_close($ch);

echo $response;
```

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

with open("tile.png", "rb") as file:
    response = requests.post(
        "https://makeseamless.com/v1/check",
        files={"file": file},
    )

response.raise_for_status()
print(response.json())
```

```ruby Ruby theme={"theme":{"light":"github-light","dark":"vesper"}}
require "net/http"
require "uri"

uri = URI("https://makeseamless.com/v1/check")
request = Net::HTTP::Post.new(uri)
request.set_form(
  [
    ["file", File.open("tile.png")],
  ],
  "multipart/form-data",
)

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts response.body
```

```go Go theme={"theme":{"light":"github-light","dark":"vesper"}}
package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	var payload bytes.Buffer
	form := multipart.NewWriter(&payload)
	file, err := os.Open("tile.png")
	if err != nil {
		panic(err)
	}
	defer file.Close()
	part, err := form.CreateFormFile("file", "tile.png")
	if err != nil {
		panic(err)
	}
	if _, err := io.Copy(part, file); err != nil {
		panic(err)
	}
	form.Close()

	req, err := http.NewRequest("POST", "https://makeseamless.com/v1/check", &payload)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Content-Type", form.FormDataContentType())

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}
```

```java Java theme={"theme":{"light":"github-light","dark":"vesper"}}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

String boundary = "makeseamless-form";
List<byte[]> parts = new ArrayList<>();
parts.add(("--" + boundary + "\r\n"
    + "Content-Disposition: form-data; name=\"file\"; filename=\"tile.png\"\r\n\r\n")
    .getBytes(StandardCharsets.UTF_8));
parts.add(Files.readAllBytes(Path.of("tile.png")));
parts.add("\r\n".getBytes(StandardCharsets.UTF_8));
parts.add(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://makeseamless.com/v1/check"))
    .header("Content-Type", "multipart/form-data; boundary=" + boundary)
    .POST(HttpRequest.BodyPublishers.ofByteArrays(parts))
    .build();

HttpResponse<String> response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.body());
```

```csharp .NET theme={"theme":{"light":"github-light","dark":"vesper"}}
using System.Net.Http;

var request = new HttpRequestMessage(HttpMethod.Post, "https://makeseamless.com/v1/check");
var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(File.ReadAllBytes("tile.png")), "file", "tile.png");
request.Content = form;

var client = new HttpClient();
var response = await client.SendAsync(request);

Console.WriteLine(await response.Content.ReadAsStringAsync());
```

```bash cURL theme={"theme":{"light":"github-light","dark":"vesper"}}
curl -X POST "https://makeseamless.com/v1/check" \
  -F "file=@tile.png"
```

```json Response theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "input": {
    "sha256": "07c1858ff8d2c84e7ef521aa92e49ad251dec90553b65e600bd52ee84ef4065f",
    "width": 256,
    "height": 256,
    "format": "PNG",
    "has_alpha": true
  },
  "verdicts": {
    "straight": {
      "repeat": "straight",
      "verdict": "bad",
      "edges": [
        {
          "edge": "vertical",
          "verdict": "bad",
          "anomaly": 1.9222,
          "broken": [
            [
              90,
              170
            ]
          ]
        },
        {
          "edge": "horizontal",
          "verdict": "ok",
          "anomaly": 0.9328,
          "broken": []
        }
      ]
    },
    "half_drop": {
      "repeat": "half_drop",
      "verdict": "bad",
      "edges": [
        {
          "edge": "vertical",
          "verdict": "bad",
          "anomaly": 2.164,
          "broken": [
            [
              83,
              172
            ],
            [
              208,
              238
            ]
          ]
        },
        {
          "edge": "horizontal",
          "verdict": "ok",
          "anomaly": 0.9328,
          "broken": []
        }
      ]
    },
    "half_brick": {
      "repeat": "half_brick",
      "verdict": "bad",
      "edges": [
        {
          "edge": "vertical",
          "verdict": "bad",
          "anomaly": 1.9222,
          "broken": [
            [
              90,
              170
            ]
          ]
        },
        {
          "edge": "horizontal",
          "verdict": "minor",
          "anomaly": 1.4314,
          "broken": [
            [
              0,
              72
            ],
            [
              108,
              147
            ],
            [
              149,
              201
            ],
            [
              240,
              256
            ]
          ]
        }
      ]
    }
  },
  "satisfied": [],
  "duplicate_edge": {
    "vertical": false,
    "horizontal": false
  },
  "summary": "Does not tile seamlessly as any checked repeat type. Straight: bad (left/right edge, 1 stretch). Half-drop: bad (left/right edge, 2 stretches). Half-brick: bad (left/right edge, 1 stretch; top/bottom edge, 4 stretches). Seams inside the tile are not checked."
}
```


## OpenAPI

````yaml openapi.json POST /v1/check
openapi: 3.1.0
info:
  title: MakeSeamless API
  version: 1.0.0
  summary: Check whether an image tiles seamlessly, and make it tile seamlessly.
  description: >
    Check a pattern tile for seams where its copies meet, and fix it so that it
    repeats without a

    visible seam.


    - A check is free and needs no account. A fix needs an API key and costs
    credits (`GET /v1/plans`).

    - A fix runs in the background: `POST /v1/fix` answers `202` with a job.
    Poll
      `GET /v1/jobs/{job_id}` until its `status` is `succeeded` or `failed`, waiting the seconds that
      `Retry-After` gives.
    - Every image the API returns has the exact width and height of the uploaded
    image (after its
      orientation is applied), as a fresh lossless PNG that carries no metadata of the upload.
    - Breaking changes come only under a new prefix (`/v2`). Fields may be added
    to responses; ignore
      the fields you do not know.
    - Error codes are stable: read `error.code`, not `error.message`. Any
    request may also fail with
      `500` and the code `internal_error`.
  license:
    name: Proprietary
servers:
  - url: https://makeseamless.com
security: []
tags:
  - name: check
    description: Seam check, answered at once.
  - name: fix
    description: Fix job, run in the background.
  - name: jobs
    description: Job status, result images and ratings.
  - name: health
    description: Service status.
  - name: account
    description: The account, its plan and credits.
paths:
  /v1/check:
    post:
      tags:
        - check
      summary: Check an image for seams
      description: >
        Says, for each requested repeat type, whether the image tiles seamlessly
        and where its edges

        break when copies of it meet. Only the tile's own edges are read; a seam
        inside the tile is not

        checked, and the summary says so. Inputs: PNG, JPEG or WebP, at most 40
        MB, at least 64 x 64 px,

        at most 64 MP. A repeat type that does not apply (an odd height for
        half-drop, an odd width for

        half-brick) is reported as `not_applicable`. No account is needed;
        checks are limited per client.
      operationId: checkImage
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                  description: The image file (PNG, JPEG or WebP).
                repeats:
                  type: array
                  description: Repeat types to check. Default all three.
                  items:
                    $ref: '#/components/schemas/RepeatType'
                  minItems: 1
                  uniqueItems: true
            encoding:
              repeats:
                style: form
                explode: true
      responses:
        '200':
          description: The check.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckResult'
              example:
                input:
                  sha256: >-
                    07c1858ff8d2c84e7ef521aa92e49ad251dec90553b65e600bd52ee84ef4065f
                  width: 256
                  height: 256
                  format: PNG
                  has_alpha: true
                verdicts:
                  straight:
                    repeat: straight
                    verdict: bad
                    edges:
                      - edge: vertical
                        verdict: bad
                        anomaly: 1.9222
                        broken:
                          - - 90
                            - 170
                      - edge: horizontal
                        verdict: ok
                        anomaly: 0.9328
                        broken: []
                  half_drop:
                    repeat: half_drop
                    verdict: bad
                    edges:
                      - edge: vertical
                        verdict: bad
                        anomaly: 2.164
                        broken:
                          - - 83
                            - 172
                          - - 208
                            - 238
                      - edge: horizontal
                        verdict: ok
                        anomaly: 0.9328
                        broken: []
                  half_brick:
                    repeat: half_brick
                    verdict: bad
                    edges:
                      - edge: vertical
                        verdict: bad
                        anomaly: 1.9222
                        broken:
                          - - 90
                            - 170
                      - edge: horizontal
                        verdict: minor
                        anomaly: 1.4314
                        broken:
                          - - 0
                            - 72
                          - - 108
                            - 147
                          - - 149
                            - 201
                          - - 240
                            - 256
                satisfied: []
                duplicate_edge:
                  vertical: false
                  horizontal: false
                summary: >-
                  Does not tile seamlessly as any checked repeat type. Straight:
                  bad (left/right edge, 1 stretch). Half-drop: bad (left/right
                  edge, 2 stretches). Half-brick: bad (left/right edge, 1
                  stretch; top/bottom edge, 4 stretches). Seams inside the tile
                  are not checked.
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '422':
          $ref: '#/components/responses/InvalidInput'
        '429':
          $ref: '#/components/responses/RateLimited'
      security: []
      x-codeSamples:
        - lang: js
          label: Node.js
          source: >-
            import { readFile } from "node:fs/promises";


            const form = new FormData();

            form.append("file", new Blob([await readFile("tile.png")]),
            "tile.png");


            const response = await fetch("https://makeseamless.com/v1/check", {
              method: "POST",
              body: form,
            });


            const data = await response.json();

            console.log(data);
        - lang: php
          label: PHP
          source: |-
            <?php

            $ch = curl_init("https://makeseamless.com/v1/check");
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, [
                "file" => new CURLFile("tile.png"),
            ]);

            $response = curl_exec($ch);
            curl_close($ch);

            echo $response;
        - lang: python
          label: Python
          source: |-
            import requests

            with open("tile.png", "rb") as file:
                response = requests.post(
                    "https://makeseamless.com/v1/check",
                    files={"file": file},
                )

            response.raise_for_status()
            print(response.json())
        - lang: ruby
          label: Ruby
          source: >-
            require "net/http"

            require "uri"


            uri = URI("https://makeseamless.com/v1/check")

            request = Net::HTTP::Post.new(uri)

            request.set_form(
              [
                ["file", File.open("tile.png")],
              ],
              "multipart/form-data",
            )


            response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do
            |http|
              http.request(request)
            end


            puts response.body
        - lang: go
          label: Go
          source: "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"os\"\n)\n\nfunc main() {\n\tvar payload bytes.Buffer\n\tform := multipart.NewWriter(&payload)\n\tfile, err := os.Open(\"tile.png\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer file.Close()\n\tpart, err := form.CreateFormFile(\"file\", \"tile.png\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tif _, err := io.Copy(part, file); err != nil {\n\t\tpanic(err)\n\t}\n\tform.Close()\n\n\treq, err := http.NewRequest(\"POST\", \"https://makeseamless.com/v1/check\", &payload)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treq.Header.Set(\"Content-Type\", form.FormDataContentType())\n\n\tres, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer res.Body.Close()\n\n\tbody, _ := io.ReadAll(res.Body)\n\tfmt.Println(string(body))\n}"
        - lang: java
          label: Java
          source: >-
            import java.net.URI;

            import java.net.http.HttpClient;

            import java.net.http.HttpRequest;

            import java.net.http.HttpResponse;

            import java.nio.charset.StandardCharsets;

            import java.nio.file.Files;

            import java.nio.file.Path;

            import java.util.ArrayList;

            import java.util.List;


            String boundary = "makeseamless-form";

            List<byte[]> parts = new ArrayList<>();

            parts.add(("--" + boundary + "\r\n"
                + "Content-Disposition: form-data; name=\"file\"; filename=\"tile.png\"\r\n\r\n")
                .getBytes(StandardCharsets.UTF_8));
            parts.add(Files.readAllBytes(Path.of("tile.png")));

            parts.add("\r\n".getBytes(StandardCharsets.UTF_8));

            parts.add(("--" + boundary +
            "--\r\n").getBytes(StandardCharsets.UTF_8));


            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://makeseamless.com/v1/check"))
                .header("Content-Type", "multipart/form-data; boundary=" + boundary)
                .POST(HttpRequest.BodyPublishers.ofByteArrays(parts))
                .build();

            HttpResponse<String> response = HttpClient.newHttpClient()
                .send(request, HttpResponse.BodyHandlers.ofString());

            System.out.println(response.body());
        - lang: csharp
          label: .NET
          source: >-
            using System.Net.Http;


            var request = new HttpRequestMessage(HttpMethod.Post,
            "https://makeseamless.com/v1/check");

            var form = new MultipartFormDataContent();

            form.Add(new ByteArrayContent(File.ReadAllBytes("tile.png")),
            "file", "tile.png");

            request.Content = form;


            var client = new HttpClient();

            var response = await client.SendAsync(request);


            Console.WriteLine(await response.Content.ReadAsStringAsync());
        - lang: bash
          label: cURL
          source: |-
            curl -X POST "https://makeseamless.com/v1/check" \
              -F "file=@tile.png"
components:
  schemas:
    RepeatType:
      type: string
      enum:
        - straight
        - half_drop
        - half_brick
      description: >
        `straight`: plain repeat on both axes. `half_drop`: the next column is
        shifted down by half the

        height (needs an even height). `half_brick`: the next row is shifted
        right by half the width

        (needs an even width).
    CheckResult:
      type: object
      additionalProperties: false
      required:
        - input
        - verdicts
        - satisfied
        - duplicate_edge
        - summary
      properties:
        input:
          $ref: '#/components/schemas/InputInfo'
        verdicts:
          type: object
          additionalProperties: false
          description: One entry per requested repeat type.
          properties:
            straight:
              $ref: '#/components/schemas/RepeatVerdict'
            half_drop:
              $ref: '#/components/schemas/RepeatVerdict'
            half_brick:
              $ref: '#/components/schemas/RepeatVerdict'
        satisfied:
          type: array
          description: >-
            The requested repeat types the image already tiles as (verdict
            `ok`).
          items:
            $ref: '#/components/schemas/RepeatType'
        duplicate_edge:
          type: object
          additionalProperties: false
          required:
            - vertical
            - horizontal
          description: >-
            A 1 px line repeated on both sides of an edge, which shows as a
            doubled line when tiled.
          properties:
            vertical:
              type: boolean
            horizontal:
              type: boolean
        summary:
          type: string
          description: >-
            One plain-language line; it says that seams inside the tile are not
            checked.
    InputInfo:
      type: object
      additionalProperties: false
      required:
        - sha256
        - width
        - height
        - format
        - has_alpha
      properties:
        sha256:
          type: string
          pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the uploaded file bytes.
        width:
          type: integer
          minimum: 64
          description: Width in px, after the image's orientation is applied.
        height:
          type: integer
          minimum: 64
          description: Height in px, after the image's orientation is applied.
        format:
          type: string
          enum:
            - PNG
            - JPEG
            - WEBP
        has_alpha:
          type: boolean
    RepeatVerdict:
      type: object
      additionalProperties: false
      required:
        - repeat
        - verdict
        - edges
      description: The verdict for one repeat type, with its seam report per edge.
      properties:
        repeat:
          $ref: '#/components/schemas/RepeatType'
        verdict:
          $ref: '#/components/schemas/Verdict'
        edges:
          type: array
          description: Empty when the verdict is `not_applicable`.
          items:
            $ref: '#/components/schemas/EdgeReport'
    Error:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
    Verdict:
      type: string
      enum:
        - ok
        - minor
        - bad
        - not_applicable
      description: >-
        `ok`: no seam. `minor`: a faint seam. `bad`: a visible seam.
        `not_applicable`: the repeat type does not fit this size.
    EdgeReport:
      type: object
      additionalProperties: false
      required:
        - edge
        - verdict
        - anomaly
        - broken
      description: >
        One edge where copies of the tile meet. Positions along the `vertical`
        edge are rows of the

        tile; along the `horizontal` edge, columns.
      properties:
        edge:
          type: string
          enum:
            - vertical
            - horizontal
          description: >-
            `vertical`: where the right side meets the left. `horizontal`: where
            the bottom meets the top.
        verdict:
          type: string
          enum:
            - ok
            - minor
            - bad
          description: The level of `anomaly`.
        anomaly:
          type: number
          minimum: 0
          description: >-
            How strong the seam is, on one scale for every edge; higher is
            stronger.
        broken:
          type: array
          description: The stretches of the edge where the seam shows.
          items:
            $ref: '#/components/schemas/Range'
    ErrorBody:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - retryable
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          description: Plain-language explanation; it may change, do not parse it.
        retryable:
          type: boolean
          description: >-
            True when the same request can succeed later (a new fix, or polling
            again).
        details:
          type: object
          description: 'Optional machine-readable context, for example `{"max_side": 3600}`.'
    Range:
      type: array
      description: Half-open pixel range `[start, end)` along an edge.
      items:
        type: integer
        minimum: 0
      minItems: 2
      maxItems: 2
    ErrorCode:
      type: string
      enum:
        - validation_error
        - payload_too_large
        - unsupported_format
        - decode_failed
        - too_small
        - too_large
        - odd_dimension
        - transparent_seam_band
        - not_found
        - result_not_ready
        - no_result
        - no_seamless_candidate
        - service_unavailable
        - interrupted
        - internal_error
        - auth_required
        - insufficient_credits
        - daily_capacity_reached
        - too_many_running_fixes
        - rate_limited
        - already_subscribed
        - pro_required
        - no_subscription
        - rating_closed
      description: >
        Stable codes. Of a failed job: `no_seamless_candidate` (no version of
        the image tiled

        seamlessly), `service_unavailable` (the fix could not run for now; try
        again later),

        `interrupted` (the service stopped while the job ran; start the fix
        again) and

        `internal_error`.
  responses:
    PayloadTooLarge:
      description: >
        The image file is larger than 40 MB (`payload_too_large`); refused
        before it is read in full.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InvalidInput:
      description: >
        The request or the image cannot be used. Codes: `validation_error`,
        `unsupported_format`,

        `decode_failed`, `too_small`, `too_large`, `odd_dimension` (fix only),
        `transparent_seam_band`

        (fix only).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: >
        Too many requests from this client in the last minute (`rate_limited`);
        `Retry-After` says when

        to try again.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfterLimit'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  headers:
    RetryAfterLimit:
      description: Seconds to wait before trying again.
      schema:
        type: integer
        minimum: 1

````

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