Login

Fit API

Programmatic access to submit square packing solutions from any language.

Quick Start

Two steps to submit solutions from your own code:

1 Get a token: exchange your username & password for a JWT.

curl -X POST https://extsearch.org/api/fit/token \ -H "Content-Type: application/json" \ -d '{ "username": "you", "password": "yourpass" }' # → { "token": "eyJhbGciOi..." }

2 Submit a solution: send your squares with the token.

curl -X POST https://extsearch.org/api/fit/submit \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "squares": [ [{"x": 0, "y": 0}, {"x": 56, "y": 0}, {"x": 56, "y": 56}, {"x": 0, "y": 56}], ... ] }'

Authentication

POST /api/fit/token

Exchange credentials for a JWT token. Tokens are valid for 24 hours.

Request:

{"username": "alice", "password": "secret123"}

Success (200):

{"token": "eyJhbGciOiJIUzI1NiIs..."}

Error (401):

{"error": "Invalid credentials."}

Include the token in all subsequent requests as:
Authorization: Bearer <token>

Submit Solution

POST /api/fit/submit

Submit a square packing solution. Requires authentication.

Data Format

Each square is represented by its 4 corners in pixel coordinates. One unit square = 56 × 56 pixels.

FieldTypeDescription
squaresarrayArray of squares, each a 4-element array of corner points
Corner{"x": float, "y": float}Pixel coordinate. Order: top-left, top-right, bottom-right, bottom-left (before rotation)

Request body:

{
  "squares": [
    [
      {"x": 0,  "y": 0},
      {"x": 56, "y": 0},
      {"x": 56, "y": 56},
      {"x": 0,  "y": 56}
    ],
    [
      {"x": 56, "y": 0},
      {"x": 112, "y": 0},
      {"x": 112, "y": 56},
      {"x": 56,  "y": 56}
    ]
  ]
}

Success (200):

{"submission_id": 42, "message": "Solution submitted."}
Rate limit: 60 submissions per hour per account. Check X-RateLimit-Remaining in response headers. If exceeded, a 429 is returned with Retry-After.

Retrieve Solution

GET /api/submission/<id>/squares

Retrieve the squares of any submission. No authentication required.

Response:

{
  "squares": [
    {"cx": 28.0, "cy": 28.0, "ux": 0.7071, "uy": 0.7071},
    ...
  ]
}

cx, cy = centre, ux, uy = unit direction vector. Reconstruct corners using a half-diagonal of 28√2 ≈ 39.598 pixels.

Code Examples

Complete working examples. Copy, set your credentials, and run.

import requests BASE_URL = "https://extsearch.org" UNIT = 56 # one square = 56 × 56 pixels # ── Step 1: Authenticate ────────────────────────────────── response = requests.post( f"{BASE_URL}/api/fit/token", json = { "username": "your_username", "password": "your_password", }, ) token = response.json()["token"] headers = { "Authorization": f"Bearer {token}" } # ── Step 2: Build a solution ────────────────────────────── # Each square is 4 corner points: TL → TR → BR → BL squares = [] for i in range(2): x = i * UNIT squares.append([ { "x": x, "y": 0 }, # top-left { "x": x + UNIT, "y": 0 }, # top-right { "x": x + UNIT, "y": UNIT }, # bottom-right { "x": x, "y": UNIT }, # bottom-left ]) # ── Step 3: Submit ──────────────────────────────────────── result = requests.post( f"{BASE_URL}/api/fit/submit", json = { "squares": squares }, headers = headers, ) print(result.json()) print(f"Submissions remaining: {result.headers.get('X-RateLimit-Remaining')}")
const BASE_URL = "https://extsearch.org"; const UNIT = 56; // ── Step 1: Authenticate ────────────────────────────────── const authResponse = await fetch(`${BASE_URL}/api/fit/token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: "your_username", password: "your_password", }), }); const { token } = await authResponse.json(); // ── Step 2: Build a solution ────────────────────────────── const squares = [0, 1].map(i => { const x = i * UNIT; return [ { x: x, y: 0 }, // top-left { x: x + UNIT, y: 0 }, // top-right { x: x + UNIT, y: UNIT }, // bottom-right { x: x, y: UNIT }, // bottom-left ]; }); // ── Step 3: Submit ──────────────────────────────────────── const result = await fetch(`${BASE_URL}/api/fit/submit`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}`, }, body: JSON.stringify({ squares }), }); console.log(await result.json());
# ── Step 1: Get a token ─────────────────────────────────── TOKEN=$( \ curl -s -X POST https://extsearch.org/api/fit/token \ -H "Content-Type: application/json" \ -d '{ "username": "your_username", "password": "your_password" }' \ | jq -r .token \ ) # ── Step 2: Submit a solution ───────────────────────────── # Two axis-aligned unit squares side by side curl -v -X POST https://extsearch.org/api/fit/submit \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "squares": [ [ {"x": 0, "y": 0}, {"x": 56, "y": 0}, {"x": 56, "y": 56}, {"x": 0, "y": 56} ], [ {"x": 56, "y": 0}, {"x": 112, "y": 0}, {"x": 112, "y": 56}, {"x": 56, "y": 56} ] ] }'
using System.Net.Http; using System.Text; using System.Text.Json; var http = new HttpClient(); var baseUrl = "https://extsearch.org"; // ── Step 1: Authenticate ────────────────────────────────── var authBody = JsonSerializer.Serialize(new { username = "your_username", password = "your_password", }); var authResp = await http.PostAsync( $"{baseUrl}/api/fit/token", new StringContent(authBody, Encoding.UTF8, "application/json") ); var authJson = JsonDocument.Parse( await authResp.Content.ReadAsStringAsync() ); var token = authJson.RootElement .GetProperty("token") .GetString(); http.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); // ── Step 2: Build a solution ────────────────────────────── var squares = new[] { new[] { new { x = 0, y = 0 }, // top-left new { x = 56, y = 0 }, // top-right new { x = 56, y = 56 }, // bottom-right new { x = 0, y = 56 }, // bottom-left }, }; // ── Step 3: Submit ──────────────────────────────────────── var body = JsonSerializer.Serialize(new { squares }); var resp = await http.PostAsync( $"{baseUrl}/api/fit/submit", new StringContent(body, Encoding.UTF8, "application/json") ); Console.WriteLine(await resp.Content.ReadAsStringAsync());
// Cargo.toml dependencies: // reqwest = { version = "0.12", features = ["json"] } // serde_json = "1" // tokio = { version = "1", features = ["full"] } use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let base_url = "https://extsearch.org"; let client = reqwest::Client::new(); // ── Step 1: Authenticate ───────────────────────────── let auth: serde_json::Value = client .post(format!("{base_url}/api/fit/token")) .json(&json!({ "username": "your_username", "password": "your_password", })) .send().await? .json().await?; let token = auth["token"].as_str().unwrap(); // ── Step 2: Build + submit ─────────────────────────── let squares = json!([[ { "x": 0, "y": 0 }, { "x": 56, "y": 0 }, { "x": 56, "y": 56 }, { "x": 0, "y": 56 }, ]]); let resp: serde_json::Value = client .post(format!("{base_url}/api/fit/submit")) .bearer_auth(token) .json(&json!({ "squares": squares })) .send().await? .json().await?; println!("{resp}"); Ok(()) }

Notes